1 /*
2  * Copyright (C) 2018 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 
17 package com.android.providers.settings;
18 
19 import static junit.framework.Assert.assertEquals;
20 import static junit.framework.Assert.assertNotNull;
21 import static junit.framework.Assert.assertNull;
22 
23 import android.content.ContentResolver;
24 import android.os.Bundle;
25 import android.provider.Settings;
26 
27 import androidx.test.InstrumentationRegistry;
28 import androidx.test.runner.AndroidJUnit4;
29 
30 import libcore.io.Streams;
31 
32 import org.junit.After;
33 import org.junit.Before;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 
37 import java.io.FileInputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 
41 /**
42  * Tests for {@link DeviceConfigService}.
43  */
44 @RunWith(AndroidJUnit4.class)
45 public class DeviceConfigServiceTest {
46     private static final String sNamespace = "namespace1";
47     private static final String sKey = "key1";
48     private static final String sValue = "value1";
49 
50     private ContentResolver mContentResolver;
51 
52     @Before
setUp()53     public void setUp() {
54         mContentResolver = InstrumentationRegistry.getContext().getContentResolver();
55     }
56 
57     @After
cleanUp()58     public void cleanUp() {
59         deleteFromContentProvider(mContentResolver, sNamespace, sKey);
60     }
61 
62     @Test
testPut()63     public void testPut() throws Exception {
64         final String newNamespace = "namespace2";
65         final String newValue = "value2";
66 
67         String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
68         assertNull(result);
69 
70         try {
71             executeShellCommand("device_config put " + sNamespace + " " + sKey + " " + sValue);
72             executeShellCommand("device_config put " + newNamespace + " " + sKey + " " + newValue);
73 
74             result = getFromContentProvider(mContentResolver, sNamespace, sKey);
75             assertEquals(sValue, result);
76             result = getFromContentProvider(mContentResolver, newNamespace, sKey);
77             assertEquals(newValue, result);
78         } finally {
79             deleteFromContentProvider(mContentResolver, newNamespace, sKey);
80         }
81     }
82 
83     @Test
testPut_invalidArgs()84     public void testPut_invalidArgs() throws Exception {
85         // missing sNamespace
86         executeShellCommand("device_config put " + sKey + " " + sValue);
87         String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
88         // still null
89         assertNull(result);
90 
91         // too many arguments
92         executeShellCommand(
93                 "device_config put " + sNamespace + " " + sKey + " " + sValue + " extra_arg");
94         result = getFromContentProvider(mContentResolver, sNamespace, sKey);
95         // still null
96         assertNull(result);
97     }
98 
99     @Test
testDelete()100     public void testDelete() throws Exception {
101         final String newNamespace = "namespace2";
102 
103         putWithContentProvider(mContentResolver, sNamespace, sKey, sValue);
104         putWithContentProvider(mContentResolver, newNamespace, sKey, sValue);
105         String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
106         assertEquals(sValue, result);
107         result = getFromContentProvider(mContentResolver, newNamespace, sKey);
108         assertEquals(sValue, result);
109 
110         try {
111             executeShellCommand("device_config delete " + sNamespace + " " + sKey);
112             // sKey is deleted from sNamespace
113             result = getFromContentProvider(mContentResolver, sNamespace, sKey);
114             assertNull(result);
115             // sKey is not deleted from newNamespace
116             result = getFromContentProvider(mContentResolver, newNamespace, sKey);
117             assertEquals(sValue, result);
118         } finally {
119             deleteFromContentProvider(mContentResolver, newNamespace, sKey);
120         }
121     }
122 
123     @Test
testDelete_invalidArgs()124     public void testDelete_invalidArgs() throws Exception {
125         putWithContentProvider(mContentResolver, sNamespace, sKey, sValue);
126         String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
127         assertEquals(sValue, result);
128 
129         // missing sNamespace
130         executeShellCommand("device_config delete " + sKey);
131         result = getFromContentProvider(mContentResolver, sNamespace, sKey);
132         // sValue was not deleted
133         assertEquals(sValue, result);
134 
135         // too many arguments
136         executeShellCommand("device_config delete " + sNamespace + " " + sKey + " extra_arg");
137         result = getFromContentProvider(mContentResolver, sNamespace, sKey);
138         // sValue was not deleted
139         assertEquals(sValue, result);
140     }
141 
142     @Test
testReset()143     public void testReset() throws Exception {
144         String newValue = "value2";
145 
146         // make sValue the default value
147         executeShellCommand(
148                 "device_config put " + sNamespace + " " + sKey + " " + sValue + " default");
149         // make newValue the current value (as set by a untrusted package)
150         executeShellCommand(
151                 "device_config put " + sNamespace + " " + sKey + " " + newValue);
152         String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
153         assertEquals(newValue, result);
154 
155         // reset values that were set by untrusted packages
156         executeShellCommand("device_config reset untrusted_defaults " + sNamespace);
157         result = getFromContentProvider(mContentResolver, sNamespace, sKey);
158         // the current value was set by a untrusted package, so it's reset
159         assertEquals(sValue, result);
160     }
161 
executeShellCommand(String command)162     private static void executeShellCommand(String command) throws IOException {
163         InputStream is = new FileInputStream(InstrumentationRegistry.getInstrumentation()
164                 .getUiAutomation().executeShellCommand(command).getFileDescriptor());
165         Streams.readFully(is);
166     }
167 
putWithContentProvider(ContentResolver resolver, String namespace, String key, String value)168     private static void putWithContentProvider(ContentResolver resolver, String namespace,
169             String key, String value) {
170         putWithContentProvider(resolver, namespace, key, value, false);
171     }
172 
putWithContentProvider(ContentResolver resolver, String namespace, String key, String value, boolean makeDefault)173     private static void putWithContentProvider(ContentResolver resolver, String namespace,
174             String key, String value, boolean makeDefault) {
175         String compositeName = namespace + "/" + key;
176         Bundle args = new Bundle();
177         args.putString(Settings.NameValueTable.VALUE, value);
178         if (makeDefault) {
179             args.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
180         }
181         resolver.call(
182                 Settings.Config.CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, compositeName, args);
183     }
184 
getFromContentProvider(ContentResolver resolver, String namespace, String key)185     private static String getFromContentProvider(ContentResolver resolver, String namespace,
186             String key) {
187         String compositeName = namespace + "/" + key;
188         Bundle result = resolver.call(
189                 Settings.Config.CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, compositeName, null);
190         assertNotNull(result);
191         return result.getString(Settings.NameValueTable.VALUE);
192     }
193 
deleteFromContentProvider(ContentResolver resolver, String namespace, String key)194     private static boolean deleteFromContentProvider(ContentResolver resolver, String namespace,
195             String key) {
196         String compositeName = namespace + "/" + key;
197         Bundle result = resolver.call(
198                 Settings.Config.CONTENT_URI,
199                 Settings.CALL_METHOD_DELETE_CONFIG, compositeName, null);
200         assertNotNull(result);
201         return compositeName.equals(result.getString(Settings.NameValueTable.VALUE));
202     }
203 }
204