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