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.database.ContentObserver;
22import android.database.Cursor;
23import android.net.Uri;
24import android.os.Handler;
25import android.os.Looper;
26import android.os.SystemClock;
27import android.os.UserHandle;
28import android.provider.Settings;
29import android.util.Log;
30
31import java.util.concurrent.atomic.AtomicBoolean;
32
33/**
34 * Tests for the SettingContentProvider.
35 *
36 * Before you run this test you must add a secondary user.
37 */
38public class SettingsProviderTest extends BaseSettingsProviderTest {
39    private static final String LOG_TAG = "SettingsProviderTest";
40
41    private static final long WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec
42
43    private static final String[] NAME_VALUE_COLUMNS = new String[]{
44            Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE
45    };
46
47    private final Object mLock = new Object();
48
49    public void testSetAndGetGlobalViaFrontEndApiForOwnerUser() throws Exception {
50        performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_GLOBAL, UserHandle.USER_OWNER);
51    }
52
53    public void testSetAndGetGlobalViaFrontEndApiForNonOwnerUser() throws Exception {
54        if (mSecondaryUserId == UserHandle.USER_OWNER) {
55            Log.w(LOG_TAG, "No secondary user. Skipping "
56                    + "testSetAndGetGlobalViaFrontEndApiForNonOwnerUser");
57            return;
58        }
59        performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_GLOBAL, mSecondaryUserId);
60    }
61
62    public void testSetAndGetSecureViaFrontEndApiForOwnerUser() throws Exception {
63        performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SECURE, UserHandle.USER_OWNER);
64    }
65
66    public void testSetAndGetSecureViaFrontEndApiForNonOwnerUser() throws Exception {
67        if (mSecondaryUserId == UserHandle.USER_OWNER) {
68            Log.w(LOG_TAG, "No secondary user. Skipping "
69                    + "testSetAndGetSecureViaFrontEndApiForNonOwnerUser");
70            return;
71        }
72        performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SECURE, mSecondaryUserId);
73    }
74
75    public void testSetAndGetSystemViaFrontEndApiForOwnerUser() throws Exception {
76        performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SYSTEM, UserHandle.USER_OWNER);
77    }
78
79    public void testSetAndGetSystemViaFrontEndApiForNonOwnerUser() throws Exception {
80        if (mSecondaryUserId == UserHandle.USER_OWNER) {
81            Log.w(LOG_TAG, "No secondary user. Skipping "
82                    + "testSetAndGetSystemViaFrontEndApiForNonOwnerUser");
83            return;
84        }
85        performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SYSTEM, mSecondaryUserId);
86    }
87
88    public void testSetAndGetGlobalViaProviderApi() throws Exception {
89        performSetAndGetSettingTestViaProviderApi(SETTING_TYPE_GLOBAL);
90    }
91
92    public void testSetAndGetSecureViaProviderApi() throws Exception {
93        performSetAndGetSettingTestViaProviderApi(SETTING_TYPE_SECURE);
94    }
95
96    public void testSetAndGetSystemViaProviderApi() throws Exception {
97        performSetAndGetSettingTestViaProviderApi(SETTING_TYPE_SYSTEM);
98    }
99
100    public void testSelectAllGlobalViaProviderApi() throws Exception {
101        setSettingViaProviderApiAndAssertSuccessfulChange(SETTING_TYPE_GLOBAL,
102                FAKE_SETTING_NAME, FAKE_SETTING_VALUE, false);
103        try {
104            queryAllSettingsViaProviderApiSettingAndAssertSettingPresent(SETTING_TYPE_GLOBAL,
105                    FAKE_SETTING_NAME);
106        } finally {
107            deleteStringViaProviderApi(SETTING_TYPE_GLOBAL, FAKE_SETTING_NAME);
108        }
109    }
110
111    public void testSelectAllSecureViaProviderApi() throws Exception {
112        setSettingViaProviderApiAndAssertSuccessfulChange(SETTING_TYPE_SECURE,
113                FAKE_SETTING_NAME, FAKE_SETTING_VALUE, false);
114        try {
115            queryAllSettingsViaProviderApiSettingAndAssertSettingPresent(SETTING_TYPE_SECURE,
116                    FAKE_SETTING_NAME);
117        } finally {
118            deleteStringViaProviderApi(SETTING_TYPE_SECURE, FAKE_SETTING_NAME);
119        }
120    }
121
122    public void testSelectAllSystemViaProviderApi() throws Exception {
123        setSettingViaProviderApiAndAssertSuccessfulChange(SETTING_TYPE_SYSTEM,
124                FAKE_SETTING_NAME, FAKE_SETTING_VALUE, true);
125        try {
126            queryAllSettingsViaProviderApiSettingAndAssertSettingPresent(SETTING_TYPE_SYSTEM,
127                    FAKE_SETTING_NAME);
128        } finally {
129            deleteStringViaProviderApi(SETTING_TYPE_SYSTEM, FAKE_SETTING_NAME);
130        }
131    }
132
133    public void testQueryUpdateDeleteGlobalViaProviderApi() throws Exception {
134        doTestQueryUpdateDeleteGlobalViaProviderApiForType(SETTING_TYPE_GLOBAL);
135    }
136
137    public void testQueryUpdateDeleteSecureViaProviderApi() throws Exception {
138        doTestQueryUpdateDeleteGlobalViaProviderApiForType(SETTING_TYPE_SECURE);
139    }
140
141    public void testQueryUpdateDeleteSystemViaProviderApi() throws Exception {
142        doTestQueryUpdateDeleteGlobalViaProviderApiForType(SETTING_TYPE_SYSTEM);
143    }
144
145    public void testBulkInsertGlobalViaProviderApi() throws Exception {
146        toTestBulkInsertViaProviderApiForType(SETTING_TYPE_GLOBAL);
147    }
148
149    public void testBulkInsertSystemViaProviderApi() throws Exception {
150        toTestBulkInsertViaProviderApiForType(SETTING_TYPE_SYSTEM);
151    }
152
153    public void testBulkInsertSecureViaProviderApi() throws Exception {
154        toTestBulkInsertViaProviderApiForType(SETTING_TYPE_SECURE);
155    }
156
157    public void testAppCannotRunsSystemOutOfMemoryWritingSystemSettings() throws Exception {
158        int insertedCount = 0;
159        try {
160            for (; insertedCount < 1200; insertedCount++) {
161                Log.w(LOG_TAG, "Adding app specific setting: " + insertedCount);
162                insertStringViaProviderApi(SETTING_TYPE_SYSTEM,
163                        String.valueOf(insertedCount), FAKE_SETTING_VALUE, false);
164            }
165            fail("Adding app specific settings must be bound.");
166        } catch (Exception e) {
167            for (; insertedCount >= 0; insertedCount--) {
168                Log.w(LOG_TAG, "Removing app specific setting: " + insertedCount);
169                deleteStringViaProviderApi(SETTING_TYPE_SYSTEM,
170                        String.valueOf(insertedCount));
171            }
172        }
173    }
174
175    public void testQueryStringInBracketsGlobalViaProviderApiForType() throws Exception {
176        doTestQueryStringInBracketsViaProviderApiForType(SETTING_TYPE_GLOBAL);
177    }
178
179    public void testQueryStringInBracketsSecureViaProviderApiForType() throws Exception {
180        doTestQueryStringInBracketsViaProviderApiForType(SETTING_TYPE_SECURE);
181    }
182
183    public void testQueryStringInBracketsSystemViaProviderApiForType() throws Exception {
184        doTestQueryStringInBracketsViaProviderApiForType(SETTING_TYPE_SYSTEM);
185    }
186
187    public void testQueryStringWithAppendedNameToUriViaProviderApi() throws Exception {
188        // Make sure we have a clean slate.
189        deleteStringViaProviderApi(SETTING_TYPE_SYSTEM, FAKE_SETTING_NAME);
190
191        try {
192            // Insert the setting.
193            final Uri uri = insertStringViaProviderApi(SETTING_TYPE_SYSTEM, FAKE_SETTING_NAME,
194                    FAKE_SETTING_VALUE, false);
195            Uri expectUri = Uri.withAppendedPath(getBaseUriForType(SETTING_TYPE_SYSTEM),
196                    FAKE_SETTING_NAME);
197            assertEquals("Did not get expected Uri.", expectUri, uri);
198
199            // Make sure the first setting is there.
200            String firstValue = queryStringViaProviderApi(SETTING_TYPE_SYSTEM, FAKE_SETTING_NAME,
201                    false, true);
202            assertEquals("Setting must be present", FAKE_SETTING_VALUE, firstValue);
203        } finally {
204            // Clean up.
205            deleteStringViaProviderApi(SETTING_TYPE_SYSTEM, FAKE_SETTING_NAME);
206        }
207    }
208
209    private void doTestQueryStringInBracketsViaProviderApiForType(int type) {
210        // Make sure we have a clean slate.
211        deleteStringViaProviderApi(type, FAKE_SETTING_NAME);
212
213        try {
214            // Insert the setting.
215            final Uri uri = insertStringViaProviderApi(type, FAKE_SETTING_NAME,
216                    FAKE_SETTING_VALUE, false);
217            Uri expectUri = Uri.withAppendedPath(getBaseUriForType(type), FAKE_SETTING_NAME);
218            assertEquals("Did not get expected Uri.", expectUri, uri);
219
220            // Make sure the first setting is there.
221            String firstValue = queryStringViaProviderApi(type, FAKE_SETTING_NAME, true, false);
222            assertEquals("Setting must be present", FAKE_SETTING_VALUE, firstValue);
223        } finally {
224            // Clean up.
225            deleteStringViaProviderApi(type, FAKE_SETTING_NAME);
226        }
227    }
228
229    private void toTestBulkInsertViaProviderApiForType(int type) {
230        // Make sure we have a clean slate.
231        deleteStringViaProviderApi(type, FAKE_SETTING_NAME);
232        deleteStringViaProviderApi(type, FAKE_SETTING_NAME_1);
233        deleteStringViaProviderApi(type, FAKE_SETTING_NAME_2);
234
235        try {
236            Uri uri = getBaseUriForType(type);
237            ContentValues[] allValues = new ContentValues[3];
238
239            // Insert the first setting.
240            ContentValues firstValues = new ContentValues();
241            firstValues.put(Settings.NameValueTable.NAME, FAKE_SETTING_NAME);
242            firstValues.put(Settings.NameValueTable.VALUE, FAKE_SETTING_VALUE);
243            allValues[0] = firstValues;
244
245            // Insert the second setting.
246            ContentValues secondValues = new ContentValues();
247            secondValues.put(Settings.NameValueTable.NAME, FAKE_SETTING_NAME_1);
248            secondValues.put(Settings.NameValueTable.VALUE, FAKE_SETTING_VALUE_1);
249            allValues[1] = secondValues;
250
251            // Insert the third setting. (null)
252            ContentValues thirdValues = new ContentValues();
253            thirdValues.put(Settings.NameValueTable.NAME, FAKE_SETTING_NAME_2);
254            thirdValues.put(Settings.NameValueTable.VALUE, FAKE_SETTING_VALUE_2);
255            allValues[2] = thirdValues;
256
257            // Verify insertion count.
258            final int insertCount = getContext().getContentResolver().bulkInsert(uri, allValues);
259            assertSame("Couldn't insert both values", 3, insertCount);
260
261            // Make sure the first setting is there.
262            String firstValue = queryStringViaProviderApi(type, FAKE_SETTING_NAME);
263            assertEquals("First setting must be present", FAKE_SETTING_VALUE, firstValue);
264
265            // Make sure the second setting is there.
266            String secondValue = queryStringViaProviderApi(type, FAKE_SETTING_NAME_1);
267            assertEquals("Second setting must be present", FAKE_SETTING_VALUE_1, secondValue);
268
269            // Make sure the third setting is there.
270            String thirdValue = queryStringViaProviderApi(type, FAKE_SETTING_NAME_2);
271            assertEquals("Third setting must be present", FAKE_SETTING_VALUE_2, thirdValue);
272        } finally {
273            // Clean up.
274            deleteStringViaProviderApi(type, FAKE_SETTING_NAME);
275            deleteStringViaProviderApi(type, FAKE_SETTING_NAME_1);
276            deleteStringViaProviderApi(type, FAKE_SETTING_NAME_2);
277        }
278    }
279
280    private void doTestQueryUpdateDeleteGlobalViaProviderApiForType(int type) throws Exception {
281        // Make sure it is not there.
282        deleteStringViaProviderApi(type, FAKE_SETTING_NAME);
283
284        // Now selection should return nothing.
285        String value = queryStringViaProviderApi(type, FAKE_SETTING_NAME);
286        assertNull("Setting should not be present.", value);
287
288        // Insert the setting.
289        Uri uri = insertStringViaProviderApi(type,
290                FAKE_SETTING_NAME, FAKE_SETTING_VALUE, false);
291        Uri expectUri = Uri.withAppendedPath(getBaseUriForType(type), FAKE_SETTING_NAME);
292        assertEquals("Did not get expected Uri.", expectUri, uri);
293
294        // Now selection should return the setting.
295        value = queryStringViaProviderApi(type, FAKE_SETTING_NAME);
296        assertEquals("Setting should be present.", FAKE_SETTING_VALUE, value);
297
298        // Update the setting.
299        final int changeCount = updateStringViaProviderApiSetting(type,
300                FAKE_SETTING_NAME, FAKE_SETTING_VALUE_1);
301        assertEquals("Did not get expected change count.", 1, changeCount);
302
303        // Now selection should return the new setting.
304        value = queryStringViaProviderApi(type, FAKE_SETTING_NAME);
305        assertEquals("Setting should be present.", FAKE_SETTING_VALUE_1, value);
306
307        // Delete the setting.
308        final int deletedCount = deleteStringViaProviderApi(type,
309                FAKE_SETTING_NAME);
310        assertEquals("Did not get expected deleted count", 1, deletedCount);
311
312        // Now selection should return nothing.
313        value = queryStringViaProviderApi(type, FAKE_SETTING_NAME);
314        assertNull("Setting should not be present.", value);
315    }
316
317    private void performSetAndGetSettingTestViaFrontEndApi(int type, int userId)
318            throws Exception {
319        try {
320            // Change the setting and assert a successful change.
321            setSettingViaFrontEndApiAndAssertSuccessfulChange(type, FAKE_SETTING_NAME,
322                    FAKE_SETTING_VALUE, userId);
323        } finally {
324            // Remove the setting.
325            setStringViaFrontEndApiSetting(type, FAKE_SETTING_NAME, null, userId);
326        }
327    }
328
329    private void performSetAndGetSettingTestViaProviderApi(int type)
330            throws Exception {
331        try {
332            // Change the setting and assert a successful change.
333            setSettingViaProviderApiAndAssertSuccessfulChange(type, FAKE_SETTING_NAME,
334                    FAKE_SETTING_VALUE, true);
335        } finally {
336            // Remove the setting.
337            setSettingViaProviderApiAndAssertSuccessfulChange(type, FAKE_SETTING_NAME, null,
338                    true);
339        }
340    }
341
342    private void setSettingViaFrontEndApiAndAssertSuccessfulChange(final int type,
343            final String name, final String value, final int userId) throws Exception {
344        setSettingAndAssertSuccessfulChange(new Runnable() {
345            @Override
346            public void run() {
347                setStringViaFrontEndApiSetting(type, name, value, userId);
348            }
349        }, type, name, value, userId);
350    }
351
352    private void setSettingViaProviderApiAndAssertSuccessfulChange(final int type,
353            final String name, final String value, final boolean withTableRowUri)
354            throws Exception {
355        setSettingAndAssertSuccessfulChange(new Runnable() {
356            @Override
357            public void run() {
358                insertStringViaProviderApi(type, name, value, withTableRowUri);
359            }
360        }, type, name, value, UserHandle.USER_OWNER);
361    }
362
363    private void setSettingAndAssertSuccessfulChange(Runnable setCommand, final int type,
364            final String name, final String value, final int userId) throws Exception {
365        ContentResolver contentResolver = getContext().getContentResolver();
366
367        final Uri settingUri = getBaseUriForType(type);
368
369        final AtomicBoolean success = new AtomicBoolean();
370
371        ContentObserver contentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
372            public void onChange(boolean selfChange, Uri changeUri, int changeId) {
373                Log.i(LOG_TAG, "onChange(" + selfChange + ", " + changeUri + ", " + changeId + ")");
374                assertEquals("Wrong change Uri", changeUri, settingUri);
375                assertEquals("Wrong user id", userId, changeId);
376                String changeValue = getStringViaFrontEndApiSetting(type, name, userId);
377                assertEquals("Wrong setting value", value, changeValue);
378
379                success.set(true);
380
381                synchronized (mLock) {
382                    mLock.notifyAll();
383                }
384            }
385        };
386
387        contentResolver.registerContentObserver(settingUri, false, contentObserver, userId);
388
389        try {
390            setCommand.run();
391
392            final long startTimeMillis = SystemClock.uptimeMillis();
393            synchronized (mLock) {
394                if (success.get()) {
395                    return;
396                }
397                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
398                if (elapsedTimeMillis > WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS) {
399                    fail("Could not change setting for "
400                            + WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS + " ms");
401                }
402                final long remainingTimeMillis = WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS
403                        - elapsedTimeMillis;
404                try {
405                    mLock.wait(remainingTimeMillis);
406                } catch (InterruptedException ie) {
407                    /* ignore */
408                }
409            }
410        } finally {
411            contentResolver.unregisterContentObserver(contentObserver);
412        }
413    }
414
415    private void queryAllSettingsViaProviderApiSettingAndAssertSettingPresent(int type,
416            String name) {
417        Uri uri = getBaseUriForType(type);
418
419        Cursor cursor = getContext().getContentResolver().query(uri, NAME_VALUE_COLUMNS,
420                null, null, null);
421
422        if (cursor == null || !cursor.moveToFirst()) {
423            fail("Nothing selected");
424        }
425
426        try {
427            final int nameColumnIdx = cursor.getColumnIndex(Settings.NameValueTable.NAME);
428
429            while (cursor.moveToNext()) {
430                String currentName = cursor.getString(nameColumnIdx);
431                if (name.equals(currentName)) {
432                    return;
433                }
434            }
435
436            fail("Not found setting: " + name);
437        } finally {
438            cursor.close();
439        }
440    }
441}
442