summaryrefslogtreecommitdiff
path: root/tests/testables/src/android/testing/TestableSettingsProvider.java
blob: b158476bd438ccefd26f61e86279b2fc88e6067d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the
 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package android.testing;

import android.content.ContentProviderClient;
import android.content.Context;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.test.mock.MockContentProvider;
import android.util.Log;

import java.util.HashMap;

import static org.junit.Assert.*;

/**
 * Allows calls to android.provider.Settings to be tested easier.
 *
 * This provides a simple copy-on-write implementation of settings that gets cleared
 * at the end of each test.
 */
public class TestableSettingsProvider extends MockContentProvider {

    private static final String TAG = "TestableSettingsProvider";
    private static final boolean DEBUG = false;
    private static final String MY_UNIQUE_KEY = "Key_" + TestableSettingsProvider.class.getName();
    private static TestableSettingsProvider sInstance;

    private final ContentProviderClient mSettings;

    private final HashMap<String, String> mValues = new HashMap<>();

    private TestableSettingsProvider(ContentProviderClient settings) {
        mSettings = settings;
    }

    void clearValuesAndCheck(Context context) {
        int userId = UserHandle.myUserId();
        mValues.put(key("global", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
        mValues.put(key("secure", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
        mValues.put(key("system", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);

        Settings.Global.clearProviderForTest();
        Settings.Secure.clearProviderForTest();
        Settings.System.clearProviderForTest();
        // Verify that if any test is using TestableContext, they all have the correct settings
        // provider.
        assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY,
                Settings.Global.getString(context.getContentResolver(), MY_UNIQUE_KEY));
        assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY,
                Settings.Secure.getString(context.getContentResolver(), MY_UNIQUE_KEY));
        assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY,
                Settings.System.getString(context.getContentResolver(), MY_UNIQUE_KEY));

        mValues.clear();
    }

    public Bundle call(String method, String arg, Bundle extras) {
        // Methods are "GET_system", "GET_global", "PUT_secure", etc.
        final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, 0);
        final String[] commands = method.split("_", 2);
        final String op = commands[0];
        final String table = commands[1];

            String k = key(table, arg, userId);
            String value;
            Bundle out = new Bundle();
            switch (op) {
                case "GET":
                    if (mValues.containsKey(k)) {
                        value = mValues.get(k);
                        if (value != null) {
                            out.putString(Settings.NameValueTable.VALUE, value);
                        }
                    } else {
                        // Fall through to real settings.
                        try {
                            if (DEBUG) Log.d(TAG, "Falling through to real settings " + method);
                            // TODO: Add our own version of caching to handle this.
                            Bundle call = mSettings.call(method, arg, extras);
                            call.remove(Settings.CALL_METHOD_TRACK_GENERATION_KEY);
                            return call;
                        } catch (RemoteException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    break;
                case "PUT":
                    value = extras.getString(Settings.NameValueTable.VALUE, null);
                    mValues.put(k, value);
                    break;
                default:
                    throw new UnsupportedOperationException("Unknown command " + method);
            }
            return out;
    }

    private static String key(String table, String key, int userId) {
        if ("global".equals(table)) {
            return table + "_" + key;
        } else {
            return table + "_" + userId + "_" + key;
        }

    }

    /**
     * Since the settings provider is cached inside android.provider.Settings, this must
     * be gotten statically to ensure there is only one instance referenced.
     */
    static TestableSettingsProvider getFakeSettingsProvider(ContentProviderClient settings) {
        if (sInstance == null) {
            sInstance = new TestableSettingsProvider(settings);
        }
        return sInstance;
    }
}