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