1f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk/*
2f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk * Copyright (C) 2017 The Android Open Source Project
3f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk *
4f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk * except in compliance with the License. You may obtain a copy of the License at
6f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk *
7f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk *      http://www.apache.org/licenses/LICENSE-2.0
8f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk *
9f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk * Unless required by applicable law or agreed to in writing, software distributed under the
10f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk * KIND, either express or implied. See the License for the specific language governing
12f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk * permissions and limitations under the License.
13f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk */
14f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
15f06a317039a6502252c2b4b1a878520d166a38c6Jason Monkpackage android.testing;
16f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
17f06a317039a6502252c2b4b1a878520d166a38c6Jason Monkimport android.content.ContentProviderClient;
18f06a317039a6502252c2b4b1a878520d166a38c6Jason Monkimport android.content.Context;
19f06a317039a6502252c2b4b1a878520d166a38c6Jason Monkimport android.os.Bundle;
20f06a317039a6502252c2b4b1a878520d166a38c6Jason Monkimport android.os.RemoteException;
21629c20c9991ab8da3732ba554794fffd7922f185Chris Wrenimport android.os.UserHandle;
22f06a317039a6502252c2b4b1a878520d166a38c6Jason Monkimport android.provider.Settings;
23f06a317039a6502252c2b4b1a878520d166a38c6Jason Monkimport android.test.mock.MockContentProvider;
24f06a317039a6502252c2b4b1a878520d166a38c6Jason Monkimport android.util.Log;
25f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
26f06a317039a6502252c2b4b1a878520d166a38c6Jason Monkimport java.util.HashMap;
27f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
28f06a317039a6502252c2b4b1a878520d166a38c6Jason Monkimport static org.junit.Assert.*;
29f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
30f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk/**
31f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk * Allows calls to android.provider.Settings to be tested easier.
32f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk *
33f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk * This provides a simple copy-on-write implementation of settings that gets cleared
34f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk * at the end of each test.
35f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk */
36f06a317039a6502252c2b4b1a878520d166a38c6Jason Monkpublic class TestableSettingsProvider extends MockContentProvider {
37f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
38f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    private static final String TAG = "TestableSettingsProvider";
39f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    private static final boolean DEBUG = false;
40f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    private static final String MY_UNIQUE_KEY = "Key_" + TestableSettingsProvider.class.getName();
41f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    private static TestableSettingsProvider sInstance;
42f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
43f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    private final ContentProviderClient mSettings;
44f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
45f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    private final HashMap<String, String> mValues = new HashMap<>();
46f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
47f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    private TestableSettingsProvider(ContentProviderClient settings) {
48f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk        mSettings = settings;
49f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    }
50f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
51f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    void clearValuesAndCheck(Context context) {
52629c20c9991ab8da3732ba554794fffd7922f185Chris Wren        int userId = UserHandle.myUserId();
53629c20c9991ab8da3732ba554794fffd7922f185Chris Wren        mValues.put(key("global", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
54629c20c9991ab8da3732ba554794fffd7922f185Chris Wren        mValues.put(key("secure", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
55629c20c9991ab8da3732ba554794fffd7922f185Chris Wren        mValues.put(key("system", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
56f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
57f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk        // Verify that if any test is using TestableContext, they all have the correct settings
58f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk        // provider.
59f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk        assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY,
60f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                Settings.Global.getString(context.getContentResolver(), MY_UNIQUE_KEY));
61f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk        assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY,
62f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                Settings.Secure.getString(context.getContentResolver(), MY_UNIQUE_KEY));
63f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk        assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY,
64f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                Settings.System.getString(context.getContentResolver(), MY_UNIQUE_KEY));
65f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
66f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk        mValues.clear();
67f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    }
68f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
69f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    public Bundle call(String method, String arg, Bundle extras) {
70f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk        // Methods are "GET_system", "GET_global", "PUT_secure", etc.
71629c20c9991ab8da3732ba554794fffd7922f185Chris Wren        final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, 0);
72f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk        final String[] commands = method.split("_", 2);
73f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk        final String op = commands[0];
74f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk        final String table = commands[1];
75f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
76629c20c9991ab8da3732ba554794fffd7922f185Chris Wren            String k = key(table, arg, userId);
77f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk            String value;
78f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk            Bundle out = new Bundle();
79f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk            switch (op) {
80f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                case "GET":
81f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                    if (mValues.containsKey(k)) {
82f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                        value = mValues.get(k);
83f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                        if (value != null) {
84f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                            out.putString(Settings.NameValueTable.VALUE, value);
85f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                        }
86f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                    } else {
87f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                        // Fall through to real settings.
88f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                        try {
89f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                            if (DEBUG) Log.d(TAG, "Falling through to real settings " + method);
90f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                            // TODO: Add our own version of caching to handle this.
91f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                            Bundle call = mSettings.call(method, arg, extras);
92f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                            call.remove(Settings.CALL_METHOD_TRACK_GENERATION_KEY);
93f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                            return call;
94f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                        } catch (RemoteException e) {
95f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                            throw new RuntimeException(e);
96f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                        }
97f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                    }
98f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                    break;
99f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                case "PUT":
100f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                    value = extras.getString(Settings.NameValueTable.VALUE, null);
101f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                    mValues.put(k, value);
102f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                    break;
103f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                default:
104f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk                    throw new UnsupportedOperationException("Unknown command " + method);
105f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk            }
106f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk            return out;
107f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    }
108f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
109629c20c9991ab8da3732ba554794fffd7922f185Chris Wren    private static String key(String table, String key, int userId) {
110629c20c9991ab8da3732ba554794fffd7922f185Chris Wren        if ("global".equals(table)) {
111629c20c9991ab8da3732ba554794fffd7922f185Chris Wren            return table + "_" + key;
112629c20c9991ab8da3732ba554794fffd7922f185Chris Wren        } else {
113629c20c9991ab8da3732ba554794fffd7922f185Chris Wren            return table + "_" + userId + "_" + key;
114629c20c9991ab8da3732ba554794fffd7922f185Chris Wren        }
115629c20c9991ab8da3732ba554794fffd7922f185Chris Wren
116f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    }
117f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk
118f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    /**
119f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk     * Since the settings provider is cached inside android.provider.Settings, this must
120f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk     * be gotten statically to ensure there is only one instance referenced.
121f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk     */
122f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    static TestableSettingsProvider getFakeSettingsProvider(ContentProviderClient settings) {
123f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk        if (sInstance == null) {
124f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk            sInstance = new TestableSettingsProvider(settings);
125f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk        }
126f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk        return sInstance;
127f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk    }
128f06a317039a6502252c2b4b1a878520d166a38c6Jason Monk}
129