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