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.runner.RunWith;
35 
36 import java.io.FileInputStream;
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.util.List;
40 
41 /**
42  * Base class for the SettingContentProvider tests.
43  */
44 @RunWith(AndroidJUnit4.class)
45 abstract class BaseSettingsProviderTest {
46     protected static final int SETTING_TYPE_GLOBAL = 1;
47     protected static final int SETTING_TYPE_SECURE = 2;
48     protected static final int SETTING_TYPE_SYSTEM = 3;
49 
50     protected static final String FAKE_SETTING_NAME = "fake_setting_name";
51     protected static final String FAKE_SETTING_NAME_1 = "fake_setting_name1";
52     protected static final String FAKE_SETTING_NAME_2 = "fake_setting_name2";
53     protected static final String FAKE_SETTING_VALUE = "fake_setting_value";
54     protected static final String FAKE_SETTING_VALUE_1 = SettingsStateTest.CRAZY_STRING;
55     protected static final String FAKE_SETTING_VALUE_2 = null;
56 
57     private static final String[] NAME_VALUE_COLUMNS = new String[] {
58             Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE
59     };
60 
61     private int mSecondaryUserId = Integer.MIN_VALUE;
62 
setStringViaFrontEndApiSetting(int type, String name, String value, int userId)63     protected void setStringViaFrontEndApiSetting(int type, String name, String value, int userId) {
64         ContentResolver contentResolver = getContext().getContentResolver();
65 
66         switch (type) {
67             case SETTING_TYPE_GLOBAL: {
68                 Settings.Global.putStringForUser(contentResolver, name, value, userId);
69             } break;
70 
71             case SETTING_TYPE_SECURE: {
72                 Settings.Secure.putStringForUser(contentResolver, name, value, userId);
73             } break;
74 
75             case SETTING_TYPE_SYSTEM: {
76                 Settings.System.putStringForUser(contentResolver, name, value, userId);
77             } break;
78 
79             default: {
80                 throw new IllegalArgumentException("Invalid type: " + type);
81             }
82         }
83     }
84 
getStringViaFrontEndApiSetting(int type, String name, int userId)85     protected String getStringViaFrontEndApiSetting(int type, String name, int userId) {
86         ContentResolver contentResolver = getContext().getContentResolver();
87 
88         switch (type) {
89             case SETTING_TYPE_GLOBAL: {
90                 return Settings.Global.getStringForUser(contentResolver, name, userId);
91             }
92 
93             case SETTING_TYPE_SECURE: {
94                 return Settings.Secure.getStringForUser(contentResolver, name, userId);
95             }
96 
97             case SETTING_TYPE_SYSTEM: {
98                 return Settings.System.getStringForUser(contentResolver, name, userId);
99             }
100 
101             default: {
102                 throw new IllegalArgumentException("Invalid type: " + type);
103             }
104         }
105     }
106 
insertStringViaProviderApi(int type, String name, String value, boolean withTableRowUri)107     protected Uri insertStringViaProviderApi(int type, String name, String value,
108             boolean withTableRowUri) {
109         Uri uri = getBaseUriForType(type);
110         if (withTableRowUri) {
111             uri = Uri.withAppendedPath(uri, name);
112         }
113         ContentValues values = new ContentValues();
114         values.put(Settings.NameValueTable.NAME, name);
115         values.put(Settings.NameValueTable.VALUE, value);
116 
117         return getContext().getContentResolver().insert(uri, values);
118     }
119 
deleteStringViaProviderApi(int type, String name)120     protected int deleteStringViaProviderApi(int type, String name) {
121         Uri uri = getBaseUriForType(type);
122         return getContext().getContentResolver().delete(uri, "name=?", new String[]{name});
123     }
124 
updateStringViaProviderApiSetting(int type, String name, String value)125     protected int updateStringViaProviderApiSetting(int type, String name, String value) {
126         Uri uri = getBaseUriForType(type);
127         ContentValues values = new ContentValues();
128         values.put(Settings.NameValueTable.NAME, name);
129         values.put(Settings.NameValueTable.VALUE, value);
130         return getContext().getContentResolver().update(uri, values, "name=?",
131                 new String[]{name});
132     }
133 
queryStringViaProviderApi(int type, String name)134     protected String queryStringViaProviderApi(int type, String name) {
135         return queryStringViaProviderApi(type, name, false, false);
136     }
137 
queryStringViaProviderApi(int type, String name, boolean queryStringInQuotes, boolean appendNameToUri)138     protected String queryStringViaProviderApi(int type, String name, boolean queryStringInQuotes,
139             boolean appendNameToUri) {
140         final Uri uri;
141         final String queryString;
142         final String[] queryArgs;
143 
144         if (appendNameToUri) {
145             uri = Uri.withAppendedPath(getBaseUriForType(type), name);
146             queryString = null;
147             queryArgs = null;
148         } else {
149             uri = getBaseUriForType(type);
150             queryString = queryStringInQuotes ? "(name=?)" : "name=?";
151             queryArgs = new String[]{name};
152         }
153 
154         Cursor cursor = getContext().getContentResolver().query(uri, NAME_VALUE_COLUMNS,
155                 queryString, queryArgs, null);
156 
157         if (cursor == null) {
158             return null;
159         }
160 
161         try {
162             if (cursor.moveToFirst()) {
163                 final int valueColumnIdx = cursor.getColumnIndex(Settings.NameValueTable.VALUE);
164                 return cursor.getString(valueColumnIdx);
165             }
166         } finally {
167             cursor.close();
168         }
169 
170         return null;
171     }
172 
resetSettingsViaShell(int type, int resetMode)173     protected static void resetSettingsViaShell(int type, int resetMode) throws IOException {
174         final String modeString;
175         switch (resetMode) {
176             case Settings.RESET_MODE_UNTRUSTED_DEFAULTS: {
177                 modeString = "untrusted_defaults";
178             } break;
179 
180             case Settings.RESET_MODE_UNTRUSTED_CHANGES: {
181                 modeString = "untrusted_clear";
182             } break;
183 
184             case Settings.RESET_MODE_TRUSTED_DEFAULTS: {
185                 modeString = "trusted_defaults";
186             } break;
187 
188             default: {
189                 throw new IllegalArgumentException("Invalid reset mode: " + resetMode);
190             }
191         }
192 
193         switch (type) {
194             case SETTING_TYPE_GLOBAL: {
195                 executeShellCommand("settings reset global " + modeString);
196             } break;
197 
198             case SETTING_TYPE_SECURE: {
199                 executeShellCommand("settings reset secure " + modeString);
200             } break;
201 
202             default: {
203                 throw new IllegalArgumentException("Invalid type: " + type);
204             }
205         }
206     }
207 
resetToDefaultsViaShell(int type, String packageName)208     protected static void resetToDefaultsViaShell(int type, String packageName) throws IOException {
209         resetToDefaultsViaShell(type, packageName, null);
210     }
211 
resetToDefaultsViaShell(int type, String packageName, String tag)212     protected static void resetToDefaultsViaShell(int type, String packageName, String tag)
213             throws IOException {
214         switch (type) {
215             case SETTING_TYPE_GLOBAL: {
216                 executeShellCommand("settings reset global " + packageName + " "
217                         + (tag != null ? tag : ""));
218             } break;
219 
220             case SETTING_TYPE_SECURE: {
221                 executeShellCommand("settings reset secure " + packageName + " "
222                         + (tag != null ? tag : ""));
223             } break;
224 
225             case SETTING_TYPE_SYSTEM: {
226                 executeShellCommand("settings reset system " + packageName + " "
227                         + (tag != null ? tag : ""));
228             } break;
229 
230             default: {
231                 throw new IllegalArgumentException("Invalid type: " + type);
232             }
233         }
234     }
235 
getSetting(int type, String name)236     protected String getSetting(int type, String name) {
237         switch (type) {
238             case SETTING_TYPE_GLOBAL: {
239                 return Settings.Global.getString(getContext().getContentResolver(), name);
240             }
241 
242             case SETTING_TYPE_SECURE: {
243                 return Settings.Secure.getString(getContext().getContentResolver(), name);
244             }
245 
246             case SETTING_TYPE_SYSTEM: {
247                 return Settings.System.getString(getContext().getContentResolver(), name);
248             }
249 
250             default: {
251                 throw new IllegalArgumentException("Invalid type: " + type);
252             }
253         }
254     }
255 
putSetting(int type, String name, String value)256     protected void putSetting(int type, String name, String value) {
257         switch (type) {
258             case SETTING_TYPE_GLOBAL: {
259                 Settings.Global.putString(getContext().getContentResolver(), name, value);
260             } break;
261 
262             case SETTING_TYPE_SECURE: {
263                 Settings.Secure.putString(getContext().getContentResolver(), name, value);
264             } break;
265 
266             case SETTING_TYPE_SYSTEM: {
267                 Settings.System.putString(getContext().getContentResolver(), name, value);
268             } break;
269 
270             default: {
271                 throw new IllegalArgumentException("Invalid type: " + type);
272             }
273         }
274     }
275 
setSettingViaShell(int type, String name, String value, boolean makeDefault)276     protected static void setSettingViaShell(int type, String name, String value,
277             boolean makeDefault) throws IOException {
278         setSettingViaShell(type, name, value, null, makeDefault);
279     }
280 
setSettingViaShell(int type, String name, String value, String token, boolean makeDefault)281     protected static void setSettingViaShell(int type, String name, String value,
282             String token, boolean makeDefault) throws IOException {
283         switch (type) {
284             case SETTING_TYPE_GLOBAL: {
285                 executeShellCommand("settings put global " + name + " "
286                         + value + (token != null ? " " + token : "")
287                         + (makeDefault ? " default" : ""));
288 
289             } break;
290 
291             case SETTING_TYPE_SECURE: {
292                 executeShellCommand("settings put secure " + name + " "
293                         + value + (token != null ? " " + token : "")
294                         + (makeDefault ? " default" : ""));
295             } break;
296 
297             case SETTING_TYPE_SYSTEM: {
298                 executeShellCommand("settings put system " + name + " "
299                         + value + (token != null ? " " + token : "")
300                         + (makeDefault ? " default" : ""));
301             } break;
302 
303             default: {
304                 throw new IllegalArgumentException("Invalid type: " + type);
305             }
306         }
307     }
308 
getContext()309     protected Context getContext() {
310         return InstrumentationRegistry.getContext();
311     }
312 
getSecondaryUserId()313     protected int getSecondaryUserId() {
314         if (mSecondaryUserId == Integer.MIN_VALUE) {
315             UserManager userManager = (UserManager) getContext()
316                     .getSystemService(Context.USER_SERVICE);
317             List<UserInfo> users = userManager.getUsers();
318             final int userCount = users.size();
319             for (int i = 0; i < userCount; i++) {
320                 UserInfo user = users.get(i);
321                 if (!user.isPrimary() && !user.isManagedProfile()) {
322                     mSecondaryUserId = user.id;
323                     return mSecondaryUserId;
324                 }
325             }
326         }
327         if (mSecondaryUserId == Integer.MIN_VALUE) {
328             mSecondaryUserId =  UserHandle.USER_SYSTEM;
329         }
330         return mSecondaryUserId;
331     }
332 
getBaseUriForType(int type)333     protected static Uri getBaseUriForType(int type) {
334         switch (type) {
335             case SETTING_TYPE_GLOBAL: {
336                 return Settings.Global.CONTENT_URI;
337             }
338 
339             case SETTING_TYPE_SECURE: {
340                 return Settings.Secure.CONTENT_URI;
341             }
342 
343             case SETTING_TYPE_SYSTEM: {
344                 return Settings.System.CONTENT_URI;
345             }
346 
347             default: {
348                 throw new IllegalArgumentException("Invalid type: " + type);
349             }
350         }
351     }
352 
executeShellCommand(String command)353     protected static void executeShellCommand(String command) throws IOException {
354         InputStream is = new FileInputStream(InstrumentationRegistry.getInstrumentation()
355                 .getUiAutomation().executeShellCommand(command).getFileDescriptor());
356         Streams.readFully(is);
357     }
358 }
359