16d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti/*
26d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * Copyright (C) 2016 The Android Open Source Project
36d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *
46d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * Licensed under the Apache License, Version 2.0 (the "License");
56d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * you may not use this file except in compliance with the License.
66d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * You may obtain a copy of the License at
76d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *
86d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *      http://www.apache.org/licenses/LICENSE-2.0
96d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *
106d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * Unless required by applicable law or agreed to in writing, software
116d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * distributed under the License is distributed on an "AS IS" BASIS,
126d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * See the License for the specific language governing permissions and
146d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * limitations under the License.
156d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti */
166d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti
177df1a82802cdf842d07a86a80383c7d5ea7ae53aLorenzo Colittipackage com.android.internal.util.test;
186d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti
196d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colittiimport android.net.Uri;
206d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colittiimport android.os.Bundle;
216d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colittiimport android.provider.Settings;
226d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colittiimport android.test.mock.MockContentProvider;
236d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colittiimport android.util.Log;
246d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti
256d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colittiimport java.util.HashMap;
266d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti
276d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti/**
286d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * Fake for system settings.
296d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *
306d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * To use, ensure that the Context used by the test code returns a ContentResolver that uses this
316d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * provider for the Settings authority:
326d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *
336d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *   class MyTestContext extends MockContext {
346d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *       ...
356d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *       private final MockContentResolver mContentResolver;
366d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *       public MyTestContext(...) {
376d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *           ...
386d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *           mContentResolver = new MockContentResolver();
396d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *           mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
406d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *       }
416d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *       ...
426d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *       @Override
436d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *       public ContentResolver getContentResolver() {
446d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *           return mContentResolver;
456d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *       }
466d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *
476d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * As long as the code under test is using the test Context, the actual code under test does not
486d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * need to be modified, and can access Settings using the normal static methods:
496d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *
506d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *   Settings.Global.getInt(cr, "my_setting", 0);  // Returns 0.
516d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *   Settings.Global.putInt(cr, "my_setting", 5);
526d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *   Settings.Global.getInt(cr, "my_setting", 0);  // Returns 5.
536d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *
546d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * Note that this class cannot be used in the same process as real settings. This is because it
556d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * works by passing an alternate ContentResolver to Settings operations. Unfortunately, the Settings
566d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * class only fetches the content provider from the passed-in ContentResolver the first time it's
57aa3c30dfa2459fb093ac5d7075b5066f41b49f2eSudheer Shanka * used, and after that stores it in a per-process static. If this needs to be used in this case,
58aa3c30dfa2459fb093ac5d7075b5066f41b49f2eSudheer Shanka * then call {@link #clearSettingsProvider()} before and after using this.
596d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *
606d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * TODO: evaluate implementing settings change notifications. This would require:
616d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *
626d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 1. Making ContentResolver#registerContentObserver non-final and overriding it in
636d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti *    MockContentResolver.
646d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 2. Making FakeSettingsProvider take a ContentResolver argument.
656d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti * 3. Calling ContentResolver#notifyChange(getUriFor(table, arg), ...) on every settings change.
666d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti */
676d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colittipublic class FakeSettingsProvider extends MockContentProvider {
686d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti
696d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti    private static final String TAG = FakeSettingsProvider.class.getSimpleName();
706d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti    private static final boolean DBG = false;
716d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti    private static final String[] TABLES = { "system", "secure", "global" };
726d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti
736d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti    private final HashMap<String, HashMap<String, String>> mTables = new HashMap<>();
746d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti
756d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti    public FakeSettingsProvider() {
766d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti        for (int i = 0; i < TABLES.length; i++) {
776d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti            mTables.put(TABLES[i], new HashMap<String, String>());
786d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti        }
796d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti    }
806d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti
816d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti    private Uri getUriFor(String table, String key) {
826d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti        switch (table) {
836d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti            case "system":
846d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                return Settings.System.getUriFor(key);
856d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti            case "secure":
866d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                return Settings.Secure.getUriFor(key);
876d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti            case "global":
886d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                return Settings.Global.getUriFor(key);
896d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti            default:
906d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                throw new UnsupportedOperationException("Unknown settings table " + table);
916d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti        }
926d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti    }
936d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti
94aa3c30dfa2459fb093ac5d7075b5066f41b49f2eSudheer Shanka    /**
95aa3c30dfa2459fb093ac5d7075b5066f41b49f2eSudheer Shanka     * This needs to be called before and after using the FakeSettingsProvider class.
96aa3c30dfa2459fb093ac5d7075b5066f41b49f2eSudheer Shanka     */
97aa3c30dfa2459fb093ac5d7075b5066f41b49f2eSudheer Shanka    public static void clearSettingsProvider() {
98aa3c30dfa2459fb093ac5d7075b5066f41b49f2eSudheer Shanka        Settings.Secure.clearProviderForTest();
99aa3c30dfa2459fb093ac5d7075b5066f41b49f2eSudheer Shanka        Settings.Global.clearProviderForTest();
100aa3c30dfa2459fb093ac5d7075b5066f41b49f2eSudheer Shanka        Settings.System.clearProviderForTest();
101aa3c30dfa2459fb093ac5d7075b5066f41b49f2eSudheer Shanka    }
102aa3c30dfa2459fb093ac5d7075b5066f41b49f2eSudheer Shanka
1036d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti    public Bundle call(String method, String arg, Bundle extras) {
1046d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti        // Methods are "GET_system", "GET_global", "PUT_secure", etc.
1056d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti        String[] commands = method.split("_", 2);
1066d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti        String op = commands[0];
1076d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti        String table = commands[1];
1086d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti
1096d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti        Bundle out = new Bundle();
1106d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti        String value;
1116d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti        switch (op) {
1126d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti            case "GET":
1136d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                value = mTables.get(table).get(arg);
1146d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                if (value != null) {
1156d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                    if (DBG) {
1166d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                        Log.d(TAG, String.format("Returning fake setting %s.%s = %s",
1176d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                                table, arg, value));
1186d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                    }
1196d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                    out.putString(Settings.NameValueTable.VALUE, value);
1206d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                }
1216d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                break;
1226d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti            case "PUT":
1236d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                value = extras.getString(Settings.NameValueTable.VALUE, null);
1246d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                if (DBG) {
1256d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                    Log.d(TAG, String.format("Inserting fake setting %s.%s = %s",
1266d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                            table, arg, value));
1276d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                }
1286d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                if (value != null) {
1296d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                    mTables.get(table).put(arg, value);
1306d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                } else {
1316d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                    mTables.get(table).remove(arg);
1326d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                }
1336d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                break;
1346d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti            default:
1356d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti                throw new UnsupportedOperationException("Unknown command " + method);
1366d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti        }
1376d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti
1386d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti        return out;
1396d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti    }
1406d553f6dfdcc188fa6b17d4abb11d6222009a8fbLorenzo Colitti}
141