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