16d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti/* 26d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * Copyright (C) 2016 The Android Open Source Project 36d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 46d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * Licensed under the Apache License, Version 2.0 (the "License"); 56d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * you may not use this file except in compliance with the License. 66d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * You may obtain a copy of the License at 76d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 86d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * http://www.apache.org/licenses/LICENSE-2.0 96d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 106d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * Unless required by applicable law or agreed to in writing, software 116d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * distributed under the License is distributed on an "AS IS" BASIS, 126d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * See the License for the specific language governing permissions and 146d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * limitations under the License. 156d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti */ 166d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti 176d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colittipackage com.android.internal.util; 186d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti 196d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colittiimport android.net.Uri; 206d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colittiimport android.os.Bundle; 216d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colittiimport android.provider.Settings; 226d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colittiimport android.test.mock.MockContentProvider; 236d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colittiimport android.util.Log; 246d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti 256d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colittiimport java.util.HashMap; 266d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti 276d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti/** 286d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * Fake for system settings. 296d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 306d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * To use, ensure that the Context used by the test code returns a ContentResolver that uses this 316d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * provider for the Settings authority: 326d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 336d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * class MyTestContext extends MockContext { 346d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * ... 356d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * private final MockContentResolver mContentResolver; 366d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * public MyTestContext(...) { 376d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * ... 386d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * mContentResolver = new MockContentResolver(); 396d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); 406d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * } 416d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * ... 426d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * @Override 436d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * public ContentResolver getContentResolver() { 446d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * return mContentResolver; 456d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * } 466d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 476d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * As long as the code under test is using the test Context, the actual code under test does not 486d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * need to be modified, and can access Settings using the normal static methods: 496d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 506d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * Settings.Global.getInt(cr, "my_setting", 0); // Returns 0. 516d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * Settings.Global.putInt(cr, "my_setting", 5); 526d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * Settings.Global.getInt(cr, "my_setting", 0); // Returns 5. 536d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 546d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * Note that this class cannot be used in the same process as real settings. This is because it 556d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * works by passing an alternate ContentResolver to Settings operations. Unfortunately, the Settings 566d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * class only fetches the content provider from the passed-in ContentResolver the first time it's 576d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * used, and after that stores it in a per-process static. 586d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 596d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * TODO: evaluate implementing settings change notifications. This would require: 606d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 616d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 1. Making ContentResolver#registerContentObserver non-final and overriding it in 626d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * MockContentResolver. 636d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 2. Making FakeSettingsProvider take a ContentResolver argument. 646d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 3. Calling ContentResolver#notifyChange(getUriFor(table, arg), ...) on every settings change. 656d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti */ 666d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colittipublic class FakeSettingsProvider extends MockContentProvider { 676d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti 686d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti private static final String TAG = FakeSettingsProvider.class.getSimpleName(); 696d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti private static final boolean DBG = false; 706d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti private static final String[] TABLES = { "system", "secure", "global" }; 716d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti 726d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti private final HashMap<String, HashMap<String, String>> mTables = new HashMap<>(); 736d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti 746d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti public FakeSettingsProvider() { 756d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti for (int i = 0; i < TABLES.length; i++) { 766d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti mTables.put(TABLES[i], new HashMap<String, String>()); 776d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti } 786d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti } 796d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti 806d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti private Uri getUriFor(String table, String key) { 816d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti switch (table) { 826d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti case "system": 836d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti return Settings.System.getUriFor(key); 846d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti case "secure": 856d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti return Settings.Secure.getUriFor(key); 866d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti case "global": 876d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti return Settings.Global.getUriFor(key); 886d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti default: 896d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti throw new UnsupportedOperationException("Unknown settings table " + table); 906d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti } 916d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti } 926d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti 936d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti public Bundle call(String method, String arg, Bundle extras) { 946d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti // Methods are "GET_system", "GET_global", "PUT_secure", etc. 956d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti String[] commands = method.split("_", 2); 966d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti String op = commands[0]; 976d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti String table = commands[1]; 986d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti 996d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti Bundle out = new Bundle(); 1006d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti String value; 1016d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti switch (op) { 1026d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti case "GET": 1036d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti value = mTables.get(table).get(arg); 1046d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti if (value != null) { 1056d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti if (DBG) { 1066d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti Log.d(TAG, String.format("Returning fake setting %s.%s = %s", 1076d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti table, arg, value)); 1086d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti } 1096d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti out.putString(Settings.NameValueTable.VALUE, value); 1106d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti } 1116d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti break; 1126d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti case "PUT": 1136d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti value = extras.getString(Settings.NameValueTable.VALUE, null); 1146d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti if (DBG) { 1156d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti Log.d(TAG, String.format("Inserting fake setting %s.%s = %s", 1166d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti table, arg, value)); 1176d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti } 1186d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti if (value != null) { 1196d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti mTables.get(table).put(arg, value); 1206d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti } else { 1216d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti mTables.get(table).remove(arg); 1226d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti } 1236d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti break; 1246d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti default: 1256d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti throw new UnsupportedOperationException("Unknown command " + method); 1266d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti } 1276d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti 1286d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti return out; 1296d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti } 1306d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti} 131