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 android.content.ContentProviderClient; 18 import android.content.Context; 19 import android.os.Bundle; 20 import android.os.RemoteException; 21 import android.os.UserHandle; 22 import android.provider.Settings; 23 import android.test.mock.MockContentProvider; 24 import android.util.Log; 25 26 import java.util.HashMap; 27 28 import static org.junit.Assert.*; 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 47 private TestableSettingsProvider(ContentProviderClient settings) { 48 mSettings = settings; 49 } 50 51 void clearValuesAndCheck(Context context) { 52 int userId = UserHandle.myUserId(); 53 mValues.put(key("global", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY); 54 mValues.put(key("secure", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY); 55 mValues.put(key("system", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY); 56 57 // Verify that if any test is using TestableContext, they all have the correct settings 58 // provider. 59 assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY, 60 Settings.Global.getString(context.getContentResolver(), MY_UNIQUE_KEY)); 61 assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY, 62 Settings.Secure.getString(context.getContentResolver(), MY_UNIQUE_KEY)); 63 assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY, 64 Settings.System.getString(context.getContentResolver(), MY_UNIQUE_KEY)); 65 66 mValues.clear(); 67 } 68 69 public Bundle call(String method, String arg, Bundle extras) { 70 // Methods are "GET_system", "GET_global", "PUT_secure", etc. 71 final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, 0); 72 final String[] commands = method.split("_", 2); 73 final String op = commands[0]; 74 final String table = commands[1]; 75 76 String k = key(table, arg, userId); 77 String value; 78 Bundle out = new Bundle(); 79 switch (op) { 80 case "GET": 81 if (mValues.containsKey(k)) { 82 value = mValues.get(k); 83 if (value != null) { 84 out.putString(Settings.NameValueTable.VALUE, value); 85 } 86 } else { 87 // Fall through to real settings. 88 try { 89 if (DEBUG) Log.d(TAG, "Falling through to real settings " + method); 90 // TODO: Add our own version of caching to handle this. 91 Bundle call = mSettings.call(method, arg, extras); 92 call.remove(Settings.CALL_METHOD_TRACK_GENERATION_KEY); 93 return call; 94 } catch (RemoteException e) { 95 throw new RuntimeException(e); 96 } 97 } 98 break; 99 case "PUT": 100 value = extras.getString(Settings.NameValueTable.VALUE, null); 101 mValues.put(k, value); 102 break; 103 default: 104 throw new UnsupportedOperationException("Unknown command " + method); 105 } 106 return out; 107 } 108 109 private static String key(String table, String key, int userId) { 110 if ("global".equals(table)) { 111 return table + "_" + key; 112 } else { 113 return table + "_" + userId + "_" + key; 114 } 115 116 } 117 118 /** 119 * Since the settings provider is cached inside android.provider.Settings, this must 120 * be gotten statically to ensure there is only one instance referenced. 121 */ 122 static TestableSettingsProvider getFakeSettingsProvider(ContentProviderClient settings) { 123 if (sInstance == null) { 124 sInstance = new TestableSettingsProvider(settings); 125 } 126 return sInstance; 127 } 128 } 129