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.server.appop;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertTrue;
23 import static org.mockito.ArgumentMatchers.anyInt;
24 import static org.mockito.ArgumentMatchers.isNull;
25 import static org.mockito.Mockito.eq;
26 import static org.mockito.Mockito.mock;
27 import static org.mockito.Mockito.reset;
28 import static org.mockito.Mockito.timeout;
29 import static org.mockito.Mockito.verify;
30 import static org.mockito.Mockito.verifyNoMoreInteractions;
31 
32 import android.app.AppOpsManager;
33 import android.app.AppOpsManager.OnOpActiveChangedListener;
34 import android.content.Context;
35 import android.os.Process;
36 
37 import androidx.test.InstrumentationRegistry;
38 import androidx.test.filters.SmallTest;
39 import androidx.test.runner.AndroidJUnit4;
40 
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 
44 import java.util.List;
45 
46 /**
47  * Tests app ops version upgrades
48  */
49 @SmallTest
50 @RunWith(AndroidJUnit4.class)
51 public class AppOpsActiveWatcherTest {
52 
53     private static final long NOTIFICATION_TIMEOUT_MILLIS = 5000;
54 
55     @Test
testWatchActiveOps()56     public void testWatchActiveOps() {
57         // Create a mock listener
58         final OnOpActiveChangedListener listener = mock(OnOpActiveChangedListener.class);
59 
60         // Start watching active ops
61         final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
62         appOpsManager.startWatchingActive(new String[] {AppOpsManager.OPSTR_CAMERA,
63                 AppOpsManager.OPSTR_RECORD_AUDIO}, getContext().getMainExecutor(), listener);
64 
65         // Start the op
66         appOpsManager.startOp(AppOpsManager.OP_CAMERA);
67 
68         // Verify that we got called for the op being active
69         verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
70                 .times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA),
71                 eq(Process.myUid()), eq(getContext().getPackageName()),
72                 isNull(), eq(true), anyInt(), anyInt());
73 
74         // This should be the only callback we got
75         verifyNoMoreInteractions(listener);
76 
77         // Start with a clean slate
78         reset(listener);
79 
80         // Verify that the op is active
81         assertThat(appOpsManager.isOperationActive(AppOpsManager.OP_CAMERA,
82                 Process.myUid(), getContext().getPackageName())).isTrue();
83 
84         // Finish the op
85         appOpsManager.finishOp(AppOpsManager.OP_CAMERA);
86 
87         // Verify that we got called for the op being active
88         verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
89                 .times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA),
90                 eq(Process.myUid()), eq(getContext().getPackageName()), isNull(),
91                 eq(false), anyInt(), anyInt());
92 
93         // Verify that the op is not active
94         assertThat(appOpsManager.isOperationActive(AppOpsManager.OP_CAMERA,
95                 Process.myUid(), getContext().getPackageName())).isFalse();
96 
97         // This should be the only callback we got
98         verifyNoMoreInteractions(listener);
99 
100         // Start with a clean slate
101         reset(listener);
102 
103         // Stop watching active ops
104         appOpsManager.stopWatchingActive(listener);
105 
106         // Start the op
107         appOpsManager.startOp(AppOpsManager.OP_CAMERA);
108 
109         // We should not be getting any callbacks
110         verifyNoMoreInteractions(listener);
111 
112         // Finish the op
113         appOpsManager.finishOp(AppOpsManager.OP_CAMERA);
114 
115         // We should not be getting any callbacks
116         verifyNoMoreInteractions(listener);
117 
118         // Start watching op again
119         appOpsManager.startWatchingActive(new String[] {AppOpsManager.OPSTR_CAMERA},
120                 getContext().getMainExecutor(), listener);
121 
122         // Start the op
123         appOpsManager.startOp(AppOpsManager.OP_CAMERA);
124 
125         // We should get the callback again (and since we reset the listener, we therefore expect 1)
126         verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
127                 .times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA),
128                 eq(Process.myUid()), eq(getContext().getPackageName()), isNull(),
129                 eq(true), anyInt(), anyInt());
130 
131         // Finish up
132         appOpsManager.finishOp(AppOpsManager.OP_CAMERA);
133         appOpsManager.stopWatchingActive(listener);
134     }
135 
136     @Test
testIsRunning()137     public void testIsRunning() throws Exception {
138         final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
139         // Start the op
140         appOpsManager.startOp(AppOpsManager.OP_CAMERA);
141 
142         assertTrue("Camera should be running", isCameraOn(appOpsManager));
143 
144         // Finish the op
145         appOpsManager.finishOp(AppOpsManager.OP_CAMERA);
146 
147         assertFalse("Camera should not be running", isCameraOn(appOpsManager));
148     }
149 
isCameraOn(AppOpsManager appOpsManager)150     private boolean isCameraOn(AppOpsManager appOpsManager) {
151         List<AppOpsManager.PackageOps> packages
152                 = appOpsManager.getPackagesForOps(new int[] {AppOpsManager.OP_CAMERA});
153         // AppOpsManager can return null when there is no requested data.
154         if (packages != null) {
155             final int numPackages = packages.size();
156             for (int packageInd = 0; packageInd < numPackages; packageInd++) {
157                 AppOpsManager.PackageOps packageOp = packages.get(packageInd);
158                 List<AppOpsManager.OpEntry> opEntries = packageOp.getOps();
159                 if (opEntries != null) {
160                     final int numOps = opEntries.size();
161                     for (int opInd = 0; opInd < numOps; opInd++) {
162                         AppOpsManager.OpEntry opEntry = opEntries.get(opInd);
163                         if (opEntry.getOp() == AppOpsManager.OP_CAMERA) {
164                             if (opEntry.isRunning()) {
165                                 return true;
166                             }
167                         }
168                     }
169                 }
170             }
171         }
172 
173         return false;
174     }
175 
getContext()176     private static Context getContext() {
177         return InstrumentationRegistry.getContext();
178     }
179 }
180