1 /*
2  * Copyright (C) 2014 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 android.permission.cts;
18 
19 import static android.app.AppOpsManager.MODE_ALLOWED;
20 import static android.app.AppOpsManager.MODE_DEFAULT;
21 import static android.app.AppOpsManager.MODE_ERRORED;
22 import static android.app.AppOpsManager.MODE_IGNORED;
23 import static android.app.AppOpsManager.OPSTR_READ_CALENDAR;
24 import static android.app.AppOpsManager.OPSTR_READ_SMS;
25 import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO;
26 import static com.android.compatibility.common.util.AppOpsUtils.allowedOperationLogged;
27 import static com.android.compatibility.common.util.AppOpsUtils.rejectedOperationLogged;
28 import static com.android.compatibility.common.util.AppOpsUtils.setOpMode;
29 
30 import static org.mockito.Mockito.mock;
31 import static org.mockito.Mockito.reset;
32 import static org.mockito.Mockito.timeout;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.verifyZeroInteractions;
35 
36 import android.Manifest.permission;
37 import android.app.AppOpsManager;
38 import android.app.AppOpsManager.OnOpChangedListener;
39 import android.content.Context;
40 import android.os.Process;
41 import android.test.InstrumentationTestCase;
42 import android.test.suitebuilder.annotation.SmallTest;
43 
44 import com.android.compatibility.common.util.AppOpsUtils;
45 
46 import java.util.HashMap;
47 import java.util.HashSet;
48 import java.util.Map;
49 import java.util.Set;
50 
51 public class AppOpsTest extends InstrumentationTestCase {
52     // Notifying OnOpChangedListener callbacks is an async operation, so we define a timeout.
53     private static final int MODE_WATCHER_TIMEOUT_MS = 5000;
54 
55     private AppOpsManager mAppOps;
56     private Context mContext;
57     private String mOpPackageName;
58     private int mMyUid;
59 
60     // These permissions and opStrs must map to the same op codes.
61     private static Map<String, String> permissionToOpStr = new HashMap<>();
62 
63     static {
permissionToOpStr.put(permission.ACCESS_COARSE_LOCATION, AppOpsManager.OPSTR_COARSE_LOCATION)64         permissionToOpStr.put(permission.ACCESS_COARSE_LOCATION,
65                 AppOpsManager.OPSTR_COARSE_LOCATION);
permissionToOpStr.put(permission.ACCESS_FINE_LOCATION, AppOpsManager.OPSTR_FINE_LOCATION)66         permissionToOpStr.put(permission.ACCESS_FINE_LOCATION, AppOpsManager.OPSTR_FINE_LOCATION);
permissionToOpStr.put(permission.READ_CONTACTS, AppOpsManager.OPSTR_READ_CONTACTS)67         permissionToOpStr.put(permission.READ_CONTACTS, AppOpsManager.OPSTR_READ_CONTACTS);
permissionToOpStr.put(permission.WRITE_CONTACTS, AppOpsManager.OPSTR_WRITE_CONTACTS)68         permissionToOpStr.put(permission.WRITE_CONTACTS, AppOpsManager.OPSTR_WRITE_CONTACTS);
permissionToOpStr.put(permission.READ_CALL_LOG, AppOpsManager.OPSTR_READ_CALL_LOG)69         permissionToOpStr.put(permission.READ_CALL_LOG, AppOpsManager.OPSTR_READ_CALL_LOG);
permissionToOpStr.put(permission.WRITE_CALL_LOG, AppOpsManager.OPSTR_WRITE_CALL_LOG)70         permissionToOpStr.put(permission.WRITE_CALL_LOG, AppOpsManager.OPSTR_WRITE_CALL_LOG);
permissionToOpStr.put(permission.READ_CALENDAR, AppOpsManager.OPSTR_READ_CALENDAR)71         permissionToOpStr.put(permission.READ_CALENDAR, AppOpsManager.OPSTR_READ_CALENDAR);
permissionToOpStr.put(permission.WRITE_CALENDAR, AppOpsManager.OPSTR_WRITE_CALENDAR)72         permissionToOpStr.put(permission.WRITE_CALENDAR, AppOpsManager.OPSTR_WRITE_CALENDAR);
permissionToOpStr.put(permission.CALL_PHONE, AppOpsManager.OPSTR_CALL_PHONE)73         permissionToOpStr.put(permission.CALL_PHONE, AppOpsManager.OPSTR_CALL_PHONE);
permissionToOpStr.put(permission.READ_SMS, AppOpsManager.OPSTR_READ_SMS)74         permissionToOpStr.put(permission.READ_SMS, AppOpsManager.OPSTR_READ_SMS);
permissionToOpStr.put(permission.RECEIVE_SMS, AppOpsManager.OPSTR_RECEIVE_SMS)75         permissionToOpStr.put(permission.RECEIVE_SMS, AppOpsManager.OPSTR_RECEIVE_SMS);
permissionToOpStr.put(permission.RECEIVE_MMS, AppOpsManager.OPSTR_RECEIVE_MMS)76         permissionToOpStr.put(permission.RECEIVE_MMS, AppOpsManager.OPSTR_RECEIVE_MMS);
permissionToOpStr.put(permission.RECEIVE_WAP_PUSH, AppOpsManager.OPSTR_RECEIVE_WAP_PUSH)77         permissionToOpStr.put(permission.RECEIVE_WAP_PUSH, AppOpsManager.OPSTR_RECEIVE_WAP_PUSH);
permissionToOpStr.put(permission.SEND_SMS, AppOpsManager.OPSTR_SEND_SMS)78         permissionToOpStr.put(permission.SEND_SMS, AppOpsManager.OPSTR_SEND_SMS);
permissionToOpStr.put(permission.READ_SMS, AppOpsManager.OPSTR_READ_SMS)79         permissionToOpStr.put(permission.READ_SMS, AppOpsManager.OPSTR_READ_SMS);
permissionToOpStr.put(permission.WRITE_SETTINGS, AppOpsManager.OPSTR_WRITE_SETTINGS)80         permissionToOpStr.put(permission.WRITE_SETTINGS, AppOpsManager.OPSTR_WRITE_SETTINGS);
permissionToOpStr.put(permission.SYSTEM_ALERT_WINDOW, AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW)81         permissionToOpStr.put(permission.SYSTEM_ALERT_WINDOW,
82                 AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW);
permissionToOpStr.put(permission.ACCESS_NOTIFICATIONS, AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS)83         permissionToOpStr.put(permission.ACCESS_NOTIFICATIONS,
84                 AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS);
permissionToOpStr.put(permission.CAMERA, AppOpsManager.OPSTR_CAMERA)85         permissionToOpStr.put(permission.CAMERA, AppOpsManager.OPSTR_CAMERA);
permissionToOpStr.put(permission.RECORD_AUDIO, AppOpsManager.OPSTR_RECORD_AUDIO)86         permissionToOpStr.put(permission.RECORD_AUDIO, AppOpsManager.OPSTR_RECORD_AUDIO);
permissionToOpStr.put(permission.READ_PHONE_STATE, AppOpsManager.OPSTR_READ_PHONE_STATE)87         permissionToOpStr.put(permission.READ_PHONE_STATE, AppOpsManager.OPSTR_READ_PHONE_STATE);
permissionToOpStr.put(permission.ADD_VOICEMAIL, AppOpsManager.OPSTR_ADD_VOICEMAIL)88         permissionToOpStr.put(permission.ADD_VOICEMAIL, AppOpsManager.OPSTR_ADD_VOICEMAIL);
permissionToOpStr.put(permission.USE_SIP, AppOpsManager.OPSTR_USE_SIP)89         permissionToOpStr.put(permission.USE_SIP, AppOpsManager.OPSTR_USE_SIP);
permissionToOpStr.put(permission.PROCESS_OUTGOING_CALLS, AppOpsManager.OPSTR_PROCESS_OUTGOING_CALLS)90         permissionToOpStr.put(permission.PROCESS_OUTGOING_CALLS,
91                 AppOpsManager.OPSTR_PROCESS_OUTGOING_CALLS);
permissionToOpStr.put(permission.BODY_SENSORS, AppOpsManager.OPSTR_BODY_SENSORS)92         permissionToOpStr.put(permission.BODY_SENSORS, AppOpsManager.OPSTR_BODY_SENSORS);
permissionToOpStr.put(permission.READ_CELL_BROADCASTS, AppOpsManager.OPSTR_READ_CELL_BROADCASTS)93         permissionToOpStr.put(permission.READ_CELL_BROADCASTS,
94                 AppOpsManager.OPSTR_READ_CELL_BROADCASTS);
permissionToOpStr.put(permission.READ_EXTERNAL_STORAGE, AppOpsManager.OPSTR_READ_EXTERNAL_STORAGE)95         permissionToOpStr.put(permission.READ_EXTERNAL_STORAGE,
96                 AppOpsManager.OPSTR_READ_EXTERNAL_STORAGE);
permissionToOpStr.put(permission.WRITE_EXTERNAL_STORAGE, AppOpsManager.OPSTR_WRITE_EXTERNAL_STORAGE)97         permissionToOpStr.put(permission.WRITE_EXTERNAL_STORAGE,
98                 AppOpsManager.OPSTR_WRITE_EXTERNAL_STORAGE);
99     }
100 
101     @Override
setUp()102     protected void setUp() throws Exception {
103         super.setUp();
104         mContext = getInstrumentation().getContext();
105         mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
106         mOpPackageName = mContext.getOpPackageName();
107         mMyUid = Process.myUid();
108         assertNotNull(mAppOps);
109 
110         // Reset app ops state for this test package to the system default.
111         AppOpsUtils.reset(mOpPackageName);
112     }
113 
testNoteOpAndCheckOp()114     public void testNoteOpAndCheckOp() throws Exception {
115         setOpMode(mOpPackageName, OPSTR_READ_SMS, MODE_ALLOWED);
116         assertEquals(MODE_ALLOWED, mAppOps.noteOp(OPSTR_READ_SMS, mMyUid, mOpPackageName));
117         assertEquals(MODE_ALLOWED, mAppOps.noteOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
118         assertEquals(MODE_ALLOWED, mAppOps.checkOp(OPSTR_READ_SMS, mMyUid, mOpPackageName));
119         assertEquals(MODE_ALLOWED, mAppOps.checkOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
120 
121         setOpMode(mOpPackageName, OPSTR_READ_SMS, MODE_IGNORED);
122         assertEquals(MODE_IGNORED, mAppOps.noteOp(OPSTR_READ_SMS, mMyUid, mOpPackageName));
123         assertEquals(MODE_IGNORED, mAppOps.noteOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
124         assertEquals(MODE_IGNORED, mAppOps.checkOp(OPSTR_READ_SMS, mMyUid, mOpPackageName));
125         assertEquals(MODE_IGNORED, mAppOps.checkOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
126 
127         setOpMode(mOpPackageName, OPSTR_READ_SMS, MODE_DEFAULT);
128         assertEquals(MODE_DEFAULT, mAppOps.noteOp(OPSTR_READ_SMS, mMyUid, mOpPackageName));
129         assertEquals(MODE_DEFAULT, mAppOps.noteOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
130         assertEquals(MODE_DEFAULT, mAppOps.checkOp(OPSTR_READ_SMS, mMyUid, mOpPackageName));
131         assertEquals(MODE_DEFAULT, mAppOps.checkOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
132 
133         setOpMode(mOpPackageName, OPSTR_READ_SMS, MODE_ERRORED);
134         assertEquals(MODE_ERRORED, mAppOps.noteOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
135         assertEquals(MODE_ERRORED, mAppOps.checkOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
136         try {
137             mAppOps.noteOp(OPSTR_READ_SMS, mMyUid, mOpPackageName);
138             fail("SecurityException expected");
139         } catch (SecurityException expected) {
140         }
141         try {
142             mAppOps.checkOp(OPSTR_READ_SMS, mMyUid, mOpPackageName);
143             fail("SecurityException expected");
144         } catch (SecurityException expected) {
145         }
146     }
147 
testStartOpAndFinishOp()148     public void testStartOpAndFinishOp() throws Exception {
149         setOpMode(mOpPackageName, OPSTR_READ_SMS, MODE_ALLOWED);
150         assertEquals(MODE_ALLOWED, mAppOps.startOp(OPSTR_READ_SMS, mMyUid, mOpPackageName));
151         mAppOps.finishOp(OPSTR_READ_SMS, mMyUid, mOpPackageName);
152         assertEquals(MODE_ALLOWED, mAppOps.startOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
153         mAppOps.finishOp(OPSTR_READ_SMS, mMyUid, mOpPackageName);
154 
155         setOpMode(mOpPackageName, OPSTR_READ_SMS, MODE_IGNORED);
156         assertEquals(MODE_IGNORED, mAppOps.startOp(OPSTR_READ_SMS, mMyUid, mOpPackageName));
157         assertEquals(MODE_IGNORED, mAppOps.startOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
158 
159         setOpMode(mOpPackageName, OPSTR_READ_SMS, MODE_DEFAULT);
160         assertEquals(MODE_DEFAULT, mAppOps.startOp(OPSTR_READ_SMS, mMyUid, mOpPackageName));
161         assertEquals(MODE_DEFAULT, mAppOps.startOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
162 
163         setOpMode(mOpPackageName, OPSTR_READ_SMS, MODE_ERRORED);
164         assertEquals(MODE_ERRORED, mAppOps.startOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
165         try {
166             mAppOps.startOp(OPSTR_READ_SMS, mMyUid, mOpPackageName);
167             fail("SecurityException expected");
168         } catch (SecurityException expected) {
169         }
170     }
171 
testCheckPackagePassesCheck()172     public void testCheckPackagePassesCheck() throws Exception {
173         mAppOps.checkPackage(mMyUid, mOpPackageName);
174         mAppOps.checkPackage(Process.SYSTEM_UID, "android");
175     }
176 
testCheckPackageDoesntPassCheck()177     public void testCheckPackageDoesntPassCheck() throws Exception {
178         try {
179             // Package name doesn't match UID.
180             mAppOps.checkPackage(Process.SYSTEM_UID, mOpPackageName);
181             fail("SecurityException expected");
182         } catch (SecurityException expected) {
183         }
184 
185         try {
186             // Package name doesn't match UID.
187             mAppOps.checkPackage(mMyUid, "android");
188             fail("SecurityException expected");
189         } catch (SecurityException expected) {
190         }
191 
192         try {
193             // Package name missing
194             mAppOps.checkPackage(mMyUid, "");
195             fail("SecurityException expected");
196         } catch (SecurityException expected) {
197         }
198     }
199 
testWatchingMode()200     public void testWatchingMode() throws Exception {
201         OnOpChangedListener watcher = mock(OnOpChangedListener.class);
202         try {
203             setOpMode(mOpPackageName, OPSTR_READ_SMS, MODE_ALLOWED);
204 
205             mAppOps.startWatchingMode(OPSTR_READ_SMS, mOpPackageName, watcher);
206 
207             // Make a change to the app op's mode.
208             reset(watcher);
209             setOpMode(mOpPackageName, OPSTR_READ_SMS, MODE_ERRORED);
210             verify(watcher, timeout(MODE_WATCHER_TIMEOUT_MS))
211                     .onOpChanged(OPSTR_READ_SMS, mOpPackageName);
212 
213             // Make another change to the app op's mode.
214             reset(watcher);
215             setOpMode(mOpPackageName, OPSTR_READ_SMS, MODE_ALLOWED);
216             verify(watcher, timeout(MODE_WATCHER_TIMEOUT_MS))
217                     .onOpChanged(OPSTR_READ_SMS, mOpPackageName);
218 
219             // Set mode to the same value as before - expect no call to the listener.
220             reset(watcher);
221             setOpMode(mOpPackageName, OPSTR_READ_SMS, MODE_ALLOWED);
222             verifyZeroInteractions(watcher);
223 
224             mAppOps.stopWatchingMode(watcher);
225 
226             // Make a change to the app op's mode. Since we already stopped watching the mode, the
227             // listener shouldn't be called.
228             reset(watcher);
229             setOpMode(mOpPackageName, OPSTR_READ_SMS, MODE_ERRORED);
230             verifyZeroInteractions(watcher);
231         } finally {
232             // Clean up registered watcher.
233             mAppOps.stopWatchingMode(watcher);
234         }
235     }
236 
237     @SmallTest
testAllOpsHaveOpString()238     public void testAllOpsHaveOpString() {
239         Set<String> opStrs = new HashSet<>();
240         for (String opStr : AppOpsManager.getOpStrs()) {
241             assertNotNull("Each app op must have an operation string defined", opStr);
242             opStrs.add(opStr);
243         }
244         assertEquals("Not all op strings are unique", AppOpsManager._NUM_OP, opStrs.size());
245     }
246 
247     @SmallTest
testOpCodesUnique()248     public void testOpCodesUnique() {
249         String[] opStrs = AppOpsManager.getOpStrs();
250         Set<Integer> opCodes = new HashSet<>();
251         for (String opStr : opStrs) {
252             opCodes.add(AppOpsManager.strOpToOp(opStr));
253         }
254         assertEquals("Not all app op codes are unique", opStrs.length, opCodes.size());
255     }
256 
257     @SmallTest
testPermissionMapping()258     public void testPermissionMapping() {
259         for (String permission : permissionToOpStr.keySet()) {
260             testPermissionMapping(permission, permissionToOpStr.get(permission));
261         }
262     }
263 
testPermissionMapping(String permission, String opStr)264     private void testPermissionMapping(String permission, String opStr) {
265         // Do the public value => internal op code lookups.
266         String mappedOpStr = AppOpsManager.permissionToOp(permission);
267         assertEquals(mappedOpStr, opStr);
268         int mappedOpCode = AppOpsManager.permissionToOpCode(permission);
269         int mappedOpCode2 = AppOpsManager.strOpToOp(opStr);
270         assertEquals(mappedOpCode, mappedOpCode2);
271 
272         // Do the internal op code => public value lookup (reverse lookup).
273         String permissionMappedBack = AppOpsManager.opToPermission(mappedOpCode);
274         assertEquals(permission, permissionMappedBack);
275     }
276 
277     /**
278      * Test that the app can not change the app op mode for itself.
279      */
280     @SmallTest
testCantSetModeForSelf()281     public void testCantSetModeForSelf() {
282         try {
283             int writeSmsOp = AppOpsManager.permissionToOpCode("android.permission.WRITE_SMS");
284             mAppOps.setMode(writeSmsOp, mMyUid, mOpPackageName, AppOpsManager.MODE_ALLOWED);
285             fail("Was able to set mode for self");
286         } catch (SecurityException expected) {
287         }
288     }
289 
290     @SmallTest
testGetOpsForPackage_opsAreLogged()291     public void testGetOpsForPackage_opsAreLogged() throws Exception {
292         // This test checks if operations get logged by the system. It needs to start with a clean
293         // slate, i.e. these ops can't have been logged previously for this test package. The reason
294         // is that there's no API for clearing the app op logs before a test run. However, the op
295         // logs are cleared when this test package is reinstalled between test runs. To make sure
296         // that other test methods in this class don't affect this test method, here we use
297         // operations that are not used by any other test cases.
298         String mustNotBeLogged = "Operation mustn't be logged before the test runs";
299         assertFalse(mustNotBeLogged, allowedOperationLogged(mOpPackageName, OPSTR_RECORD_AUDIO));
300         assertFalse(mustNotBeLogged, allowedOperationLogged(mOpPackageName, OPSTR_READ_CALENDAR));
301 
302         setOpMode(mOpPackageName, OPSTR_RECORD_AUDIO, MODE_ALLOWED);
303         setOpMode(mOpPackageName, OPSTR_READ_CALENDAR, MODE_ERRORED);
304 
305         // Note an op that's allowed.
306         mAppOps.noteOp(OPSTR_RECORD_AUDIO, mMyUid, mOpPackageName);
307         String mustBeLogged = "Operation must be logged";
308         assertTrue(mustBeLogged, allowedOperationLogged(mOpPackageName, OPSTR_RECORD_AUDIO));
309 
310         // Note another op that's not allowed.
311         mAppOps.noteOpNoThrow(OPSTR_READ_CALENDAR, mMyUid, mOpPackageName);
312         assertTrue(mustBeLogged, allowedOperationLogged(mOpPackageName, OPSTR_RECORD_AUDIO));
313         assertTrue(mustBeLogged, rejectedOperationLogged(mOpPackageName, OPSTR_READ_CALENDAR));
314     }
315 }
316