1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package android.testing; 16 17 import static org.junit.Assert.assertEquals; 18 19 import android.content.ContentProviderClient; 20 import android.content.Context; 21 import android.os.Bundle; 22 import android.os.RemoteException; 23 import android.os.UserHandle; 24 import android.provider.Settings; 25 import android.test.mock.MockContentProvider; 26 import android.util.Log; 27 28 import java.util.HashMap; 29 30 /** 31 * Allows calls to android.provider.Settings to be tested easier. 32 * 33 * This provides a simple copy-on-write implementation of settings that gets cleared 34 * at the end of each test. 35 */ 36 public class TestableSettingsProvider extends MockContentProvider { 37 38 private static final String TAG = "TestableSettingsProvider"; 39 private static final boolean DEBUG = false; 40 private static final String MY_UNIQUE_KEY = "Key_" + TestableSettingsProvider.class.getName(); 41 private static TestableSettingsProvider sInstance; 42 43 private final ContentProviderClient mSettings; 44 45 private final HashMap<String, String> mValues = new HashMap<>(); 46 TestableSettingsProvider(ContentProviderClient settings)47 private TestableSettingsProvider(ContentProviderClient settings) { 48 mSettings = settings; 49 } 50 clearValuesAndCheck(Context context)51 void clearValuesAndCheck(Context context) { 52 // Ensure we swapped over to use TestableSettingsProvider 53 Settings.Global.clearProviderForTest(); 54 Settings.Secure.clearProviderForTest(); 55 Settings.System.clearProviderForTest(); 56 57 // putString will eventually invoking the mocked call() method and update mValues 58 Settings.Global.putString(context.getContentResolver(), MY_UNIQUE_KEY, MY_UNIQUE_KEY); 59 Settings.Secure.putString(context.getContentResolver(), MY_UNIQUE_KEY, MY_UNIQUE_KEY); 60 Settings.System.putString(context.getContentResolver(), MY_UNIQUE_KEY, MY_UNIQUE_KEY); 61 // Verify that if any test is using TestableContext, they all have the correct settings 62 // provider. 63 assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY, 64 Settings.Global.getString(context.getContentResolver(), MY_UNIQUE_KEY)); 65 assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY, 66 Settings.Secure.getString(context.getContentResolver(), MY_UNIQUE_KEY)); 67 assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY, 68 Settings.System.getString(context.getContentResolver(), MY_UNIQUE_KEY)); 69 70 mValues.clear(); 71 } 72 call(String method, String arg, Bundle extras)73 public Bundle call(String method, String arg, Bundle extras) { 74 // Methods are "GET_system", "GET_global", "PUT_secure", etc. 75 final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, UserHandle.myUserId()); 76 final String[] commands = method.split("_", 2); 77 final String op = commands[0]; 78 final String table = commands[1]; 79 80 String k = key(table, arg, userId); 81 String value; 82 Bundle out = new Bundle(); 83 switch (op) { 84 case "GET": 85 if (mValues.containsKey(k)) { 86 value = mValues.get(k); 87 if (value != null) { 88 out.putString(Settings.NameValueTable.VALUE, value); 89 } 90 } else { 91 // Fall through to real settings. 92 try { 93 if (DEBUG) Log.d(TAG, "Falling through to real settings " + method); 94 // TODO: Add our own version of caching to handle this. 95 Bundle call = mSettings.call(method, arg, extras); 96 call.remove(Settings.CALL_METHOD_TRACK_GENERATION_KEY); 97 return call; 98 } catch (RemoteException e) { 99 throw new RuntimeException(e); 100 } 101 } 102 break; 103 case "PUT": 104 value = extras.getString(Settings.NameValueTable.VALUE, null); 105 mValues.put(k, value); 106 break; 107 default: 108 throw new UnsupportedOperationException("Unknown command " + method); 109 } 110 return out; 111 } 112 key(String table, String key, int userId)113 private static String key(String table, String key, int userId) { 114 if ("global".equals(table)) { 115 return table + "_" + key; 116 } else { 117 return table + "_" + userId + "_" + key; 118 } 119 120 } 121 122 /** 123 * Since the settings provider is cached inside android.provider.Settings, this must 124 * be gotten statically to ensure there is only one instance referenced. 125 */ getFakeSettingsProvider(ContentProviderClient settings)126 static TestableSettingsProvider getFakeSettingsProvider(ContentProviderClient settings) { 127 if (sInstance == null) { 128 sInstance = new TestableSettingsProvider(settings); 129 } 130 return sInstance; 131 } 132 } 133