1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.providers.settings; 18 19 import android.content.ContentResolver; 20 import android.content.ContentValues; 21 import android.content.Context; 22 import android.content.pm.UserInfo; 23 import android.database.Cursor; 24 import android.net.Uri; 25 import android.os.UserHandle; 26 import android.os.UserManager; 27 import android.provider.Settings; 28 29 import androidx.test.InstrumentationRegistry; 30 import androidx.test.runner.AndroidJUnit4; 31 32 import libcore.io.Streams; 33 34 import org.junit.After; 35 import org.junit.Before; 36 import org.junit.runner.RunWith; 37 38 import java.io.FileInputStream; 39 import java.io.IOException; 40 import java.io.InputStream; 41 import java.util.List; 42 43 /** 44 * Base class for the SettingContentProvider tests. 45 */ 46 @RunWith(AndroidJUnit4.class) 47 abstract class BaseSettingsProviderTest { 48 protected static final int SETTING_TYPE_GLOBAL = 1; 49 protected static final int SETTING_TYPE_SECURE = 2; 50 protected static final int SETTING_TYPE_SYSTEM = 3; 51 52 protected static final String FAKE_SETTING_NAME = "fake_setting_name"; 53 protected static final String FAKE_SETTING_NAME_1 = "fake_setting_name1"; 54 protected static final String FAKE_SETTING_NAME_2 = "fake_setting_name2"; 55 protected static final String FAKE_SETTING_VALUE = "fake_setting_value"; 56 protected static final String FAKE_SETTING_VALUE_1 = SettingsStateTest.CRAZY_STRING; 57 protected static final String FAKE_SETTING_VALUE_2 = null; 58 59 private static final String[] NAME_VALUE_COLUMNS = new String[] { 60 Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE 61 }; 62 63 private int mSecondaryUserId = Integer.MIN_VALUE; 64 65 @Before setUp()66 public void setUp() { 67 Settings.Global.clearProviderForTest(); 68 Settings.Secure.clearProviderForTest(); 69 Settings.System.clearProviderForTest(); 70 } 71 72 @After tearDown()73 public void tearDown() { 74 Settings.Global.clearProviderForTest(); 75 Settings.Secure.clearProviderForTest(); 76 Settings.System.clearProviderForTest(); 77 } 78 setStringViaFrontEndApiSetting(int type, String name, String value, int userId)79 protected void setStringViaFrontEndApiSetting(int type, String name, String value, int userId) { 80 ContentResolver contentResolver = getContext().getContentResolver(); 81 82 switch (type) { 83 case SETTING_TYPE_GLOBAL: { 84 Settings.Global.putStringForUser(contentResolver, name, value, userId); 85 } break; 86 87 case SETTING_TYPE_SECURE: { 88 Settings.Secure.putStringForUser(contentResolver, name, value, userId); 89 } break; 90 91 case SETTING_TYPE_SYSTEM: { 92 Settings.System.putStringForUser(contentResolver, name, value, userId); 93 } break; 94 95 default: { 96 throw new IllegalArgumentException("Invalid type: " + type); 97 } 98 } 99 } 100 getStringViaFrontEndApiSetting(int type, String name, int userId)101 protected String getStringViaFrontEndApiSetting(int type, String name, int userId) { 102 ContentResolver contentResolver = getContext().getContentResolver(); 103 104 switch (type) { 105 case SETTING_TYPE_GLOBAL: { 106 return Settings.Global.getStringForUser(contentResolver, name, userId); 107 } 108 109 case SETTING_TYPE_SECURE: { 110 return Settings.Secure.getStringForUser(contentResolver, name, userId); 111 } 112 113 case SETTING_TYPE_SYSTEM: { 114 return Settings.System.getStringForUser(contentResolver, name, userId); 115 } 116 117 default: { 118 throw new IllegalArgumentException("Invalid type: " + type); 119 } 120 } 121 } 122 insertStringViaProviderApi(int type, String name, String value, boolean withTableRowUri)123 protected Uri insertStringViaProviderApi(int type, String name, String value, 124 boolean withTableRowUri) { 125 Uri uri = getBaseUriForType(type); 126 if (withTableRowUri) { 127 uri = Uri.withAppendedPath(uri, name); 128 } 129 ContentValues values = new ContentValues(); 130 values.put(Settings.NameValueTable.NAME, name); 131 values.put(Settings.NameValueTable.VALUE, value); 132 133 return getContext().getContentResolver().insert(uri, values); 134 } 135 deleteStringViaProviderApi(int type, String name)136 protected int deleteStringViaProviderApi(int type, String name) { 137 Uri uri = getBaseUriForType(type); 138 return getContext().getContentResolver().delete(uri, "name=?", new String[]{name}); 139 } 140 updateStringViaProviderApiSetting(int type, String name, String value)141 protected int updateStringViaProviderApiSetting(int type, String name, String value) { 142 Uri uri = getBaseUriForType(type); 143 ContentValues values = new ContentValues(); 144 values.put(Settings.NameValueTable.NAME, name); 145 values.put(Settings.NameValueTable.VALUE, value); 146 return getContext().getContentResolver().update(uri, values, "name=?", 147 new String[]{name}); 148 } 149 queryStringViaProviderApi(int type, String name)150 protected String queryStringViaProviderApi(int type, String name) { 151 return queryStringViaProviderApi(type, name, false, false); 152 } 153 queryStringViaProviderApi(int type, String name, boolean queryStringInQuotes, boolean appendNameToUri)154 protected String queryStringViaProviderApi(int type, String name, boolean queryStringInQuotes, 155 boolean appendNameToUri) { 156 final Uri uri; 157 final String queryString; 158 final String[] queryArgs; 159 160 if (appendNameToUri) { 161 uri = Uri.withAppendedPath(getBaseUriForType(type), name); 162 queryString = null; 163 queryArgs = null; 164 } else { 165 uri = getBaseUriForType(type); 166 queryString = queryStringInQuotes ? "(name=?)" : "name=?"; 167 queryArgs = new String[]{name}; 168 } 169 170 Cursor cursor = getContext().getContentResolver().query(uri, NAME_VALUE_COLUMNS, 171 queryString, queryArgs, null); 172 173 if (cursor == null) { 174 return null; 175 } 176 177 try { 178 if (cursor.moveToFirst()) { 179 final int valueColumnIdx = cursor.getColumnIndex(Settings.NameValueTable.VALUE); 180 return cursor.getString(valueColumnIdx); 181 } 182 } finally { 183 cursor.close(); 184 } 185 186 return null; 187 } 188 resetSettingsViaShell(int type, int resetMode)189 protected static void resetSettingsViaShell(int type, int resetMode) throws IOException { 190 final String modeString; 191 switch (resetMode) { 192 case Settings.RESET_MODE_UNTRUSTED_DEFAULTS: { 193 modeString = "untrusted_defaults"; 194 } break; 195 196 case Settings.RESET_MODE_UNTRUSTED_CHANGES: { 197 modeString = "untrusted_clear"; 198 } break; 199 200 case Settings.RESET_MODE_TRUSTED_DEFAULTS: { 201 modeString = "trusted_defaults"; 202 } break; 203 204 default: { 205 throw new IllegalArgumentException("Invalid reset mode: " + resetMode); 206 } 207 } 208 209 switch (type) { 210 case SETTING_TYPE_GLOBAL: { 211 executeShellCommand("settings reset global " + modeString); 212 } break; 213 214 case SETTING_TYPE_SECURE: { 215 executeShellCommand("settings reset secure " + modeString); 216 } break; 217 218 default: { 219 throw new IllegalArgumentException("Invalid type: " + type); 220 } 221 } 222 } 223 resetToDefaultsViaShell(int type, String packageName)224 protected static void resetToDefaultsViaShell(int type, String packageName) throws IOException { 225 resetToDefaultsViaShell(type, packageName, null); 226 } 227 resetToDefaultsViaShell(int type, String packageName, String tag)228 protected static void resetToDefaultsViaShell(int type, String packageName, String tag) 229 throws IOException { 230 switch (type) { 231 case SETTING_TYPE_GLOBAL: { 232 executeShellCommand("settings reset global " + packageName + " " 233 + (tag != null ? tag : "")); 234 } break; 235 236 case SETTING_TYPE_SECURE: { 237 executeShellCommand("settings reset secure " + packageName + " " 238 + (tag != null ? tag : "")); 239 } break; 240 241 case SETTING_TYPE_SYSTEM: { 242 executeShellCommand("settings reset system " + packageName + " " 243 + (tag != null ? tag : "")); 244 } break; 245 246 default: { 247 throw new IllegalArgumentException("Invalid type: " + type); 248 } 249 } 250 } 251 getSetting(int type, String name)252 protected String getSetting(int type, String name) { 253 switch (type) { 254 case SETTING_TYPE_GLOBAL: { 255 return Settings.Global.getString(getContext().getContentResolver(), name); 256 } 257 258 case SETTING_TYPE_SECURE: { 259 return Settings.Secure.getString(getContext().getContentResolver(), name); 260 } 261 262 case SETTING_TYPE_SYSTEM: { 263 return Settings.System.getString(getContext().getContentResolver(), name); 264 } 265 266 default: { 267 throw new IllegalArgumentException("Invalid type: " + type); 268 } 269 } 270 } 271 putSetting(int type, String name, String value)272 protected void putSetting(int type, String name, String value) { 273 switch (type) { 274 case SETTING_TYPE_GLOBAL: { 275 Settings.Global.putString(getContext().getContentResolver(), name, value); 276 } break; 277 278 case SETTING_TYPE_SECURE: { 279 Settings.Secure.putString(getContext().getContentResolver(), name, value); 280 } break; 281 282 case SETTING_TYPE_SYSTEM: { 283 Settings.System.putString(getContext().getContentResolver(), name, value); 284 } break; 285 286 default: { 287 throw new IllegalArgumentException("Invalid type: " + type); 288 } 289 } 290 } 291 setSettingViaShell(int type, String name, String value, boolean makeDefault)292 protected static void setSettingViaShell(int type, String name, String value, 293 boolean makeDefault) throws IOException { 294 setSettingViaShell(type, name, value, null, makeDefault); 295 } 296 setSettingViaShell(int type, String name, String value, String token, boolean makeDefault)297 protected static void setSettingViaShell(int type, String name, String value, 298 String token, boolean makeDefault) throws IOException { 299 switch (type) { 300 case SETTING_TYPE_GLOBAL: { 301 executeShellCommand("settings put global " + name + " " 302 + value + (token != null ? " " + token : "") 303 + (makeDefault ? " default" : "")); 304 305 } break; 306 307 case SETTING_TYPE_SECURE: { 308 executeShellCommand("settings put secure " + name + " " 309 + value + (token != null ? " " + token : "") 310 + (makeDefault ? " default" : "")); 311 } break; 312 313 case SETTING_TYPE_SYSTEM: { 314 executeShellCommand("settings put system " + name + " " 315 + value + (token != null ? " " + token : "") 316 + (makeDefault ? " default" : "")); 317 } break; 318 319 default: { 320 throw new IllegalArgumentException("Invalid type: " + type); 321 } 322 } 323 } 324 getContext()325 protected Context getContext() { 326 return InstrumentationRegistry.getContext(); 327 } 328 getSecondaryUserId()329 protected int getSecondaryUserId() { 330 if (mSecondaryUserId == Integer.MIN_VALUE) { 331 UserManager userManager = (UserManager) getContext() 332 .getSystemService(Context.USER_SERVICE); 333 List<UserInfo> users = userManager.getUsers(); 334 final int userCount = users.size(); 335 for (int i = 0; i < userCount; i++) { 336 UserInfo user = users.get(i); 337 if (!user.isPrimary() && !user.isManagedProfile()) { 338 mSecondaryUserId = user.id; 339 return mSecondaryUserId; 340 } 341 } 342 } 343 if (mSecondaryUserId == Integer.MIN_VALUE) { 344 mSecondaryUserId = UserHandle.USER_SYSTEM; 345 } 346 return mSecondaryUserId; 347 } 348 getBaseUriForType(int type)349 protected static Uri getBaseUriForType(int type) { 350 switch (type) { 351 case SETTING_TYPE_GLOBAL: { 352 return Settings.Global.CONTENT_URI; 353 } 354 355 case SETTING_TYPE_SECURE: { 356 return Settings.Secure.CONTENT_URI; 357 } 358 359 case SETTING_TYPE_SYSTEM: { 360 return Settings.System.CONTENT_URI; 361 } 362 363 default: { 364 throw new IllegalArgumentException("Invalid type: " + type); 365 } 366 } 367 } 368 executeShellCommand(String command)369 protected static void executeShellCommand(String command) throws IOException { 370 InputStream is = new FileInputStream(InstrumentationRegistry.getInstrumentation() 371 .getUiAutomation().executeShellCommand(command).getFileDescriptor()); 372 Streams.readFully(is); 373 } 374 } 375