1 /* 2 * Copyright (C) 2024 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 android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR; 20 import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER; 21 import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED; 22 import static android.app.AppOpsManager.OP_FLAG_SELF; 23 import static android.app.AppOpsManager.UID_STATE_FOREGROUND; 24 import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE; 25 26 import static com.google.common.truth.Truth.assertThat; 27 28 import android.app.AppOpsManager; 29 import android.companion.virtual.VirtualDeviceManager; 30 import android.content.Context; 31 import android.os.FileUtils; 32 import android.os.Process; 33 import android.permission.flags.Flags; 34 import android.platform.test.annotations.RequiresFlagsEnabled; 35 import android.platform.test.flag.junit.CheckFlagsRule; 36 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 37 38 import androidx.test.ext.junit.runners.AndroidJUnit4; 39 import androidx.test.platform.app.InstrumentationRegistry; 40 41 import org.junit.After; 42 import org.junit.Before; 43 import org.junit.Rule; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 47 import java.io.File; 48 import java.util.List; 49 50 @RunWith(AndroidJUnit4.class) 51 public class DiscreteAppOpPersistenceTest { 52 private DiscreteRegistry mDiscreteRegistry; 53 private final Object mLock = new Object(); 54 private File mMockDataDirectory; 55 private final Context mContext = 56 InstrumentationRegistry.getInstrumentation().getTargetContext(); 57 58 @Rule 59 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 60 61 @Before setUp()62 public void setUp() { 63 mMockDataDirectory = mContext.getDir("mock_data", Context.MODE_PRIVATE); 64 mDiscreteRegistry = new DiscreteRegistry(mLock, mMockDataDirectory); 65 mDiscreteRegistry.systemReady(); 66 } 67 68 @After cleanUp()69 public void cleanUp() { 70 mDiscreteRegistry.writeAndClearAccessHistory(); 71 FileUtils.deleteContents(mMockDataDirectory); 72 } 73 74 @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_APP_OP_NEW_SCHEMA_ENABLED) 75 @Test defaultDevice_recordAccess_persistToDisk()76 public void defaultDevice_recordAccess_persistToDisk() { 77 int uid = Process.myUid(); 78 String packageName = mContext.getOpPackageName(); 79 int op = AppOpsManager.OP_CAMERA; 80 String deviceId = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT; 81 long accessTime = System.currentTimeMillis(); 82 long duration = 60000L; 83 int uidState = UID_STATE_FOREGROUND; 84 int opFlags = OP_FLAGS_ALL_TRUSTED; 85 int attributionFlags = ATTRIBUTION_FLAG_ACCESSOR; 86 int attributionChainId = AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE; 87 88 mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags, 89 uidState, accessTime, duration, attributionFlags, attributionChainId, 90 DiscreteRegistry.ACCESS_TYPE_FINISH_OP); 91 92 // Verify in-memory object is correct 93 fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime, 94 duration, uidState, opFlags, attributionFlags, attributionChainId); 95 96 // Write to disk and clear the in-memory object 97 mDiscreteRegistry.writeAndClearAccessHistory(); 98 99 // Verify the storage file is created and then verify its content is correct 100 File[] files = FileUtils.listFilesOrEmpty(mMockDataDirectory); 101 assertThat(files.length).isEqualTo(1); 102 fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime, 103 duration, uidState, opFlags, attributionFlags, attributionChainId); 104 } 105 106 @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_APP_OP_NEW_SCHEMA_ENABLED) 107 @Test externalDevice_recordAccess_persistToDisk()108 public void externalDevice_recordAccess_persistToDisk() { 109 int uid = Process.myUid(); 110 String packageName = mContext.getOpPackageName(); 111 int op = AppOpsManager.OP_CAMERA; 112 String deviceId = "companion:1"; 113 long accessTime = System.currentTimeMillis(); 114 long duration = -1; 115 int uidState = UID_STATE_FOREGROUND_SERVICE; 116 int opFlags = OP_FLAG_SELF; 117 int attributionFlags = ATTRIBUTION_FLAG_RECEIVER; 118 int attributionChainId = 10; 119 120 mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags, 121 uidState, accessTime, duration, attributionFlags, attributionChainId, 122 DiscreteRegistry.ACCESS_TYPE_START_OP); 123 124 fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime, 125 duration, uidState, opFlags, attributionFlags, attributionChainId); 126 127 mDiscreteRegistry.writeAndClearAccessHistory(); 128 129 File[] files = FileUtils.listFilesOrEmpty(mMockDataDirectory); 130 assertThat(files.length).isEqualTo(1); 131 fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime, 132 duration, uidState, opFlags, attributionFlags, attributionChainId); 133 } 134 fetchDiscreteOpsAndValidate(int expectedUid, String expectedPackageName, int expectedOp, String expectedDeviceId, String expectedAttrTag, long expectedAccessTime, long expectedAccessDuration, int expectedUidState, int expectedOpFlags, int expectedAttrFlags, int expectedAttrChainId)135 private void fetchDiscreteOpsAndValidate(int expectedUid, String expectedPackageName, 136 int expectedOp, String expectedDeviceId, String expectedAttrTag, 137 long expectedAccessTime, long expectedAccessDuration, int expectedUidState, 138 int expectedOpFlags, int expectedAttrFlags, int expectedAttrChainId) { 139 DiscreteRegistry.DiscreteOps discreteOps = mDiscreteRegistry.getAllDiscreteOps(); 140 141 assertThat(discreteOps.isEmpty()).isFalse(); 142 assertThat(discreteOps.mUids.size()).isEqualTo(1); 143 144 DiscreteRegistry.DiscreteUidOps discreteUidOps = discreteOps.mUids.get(expectedUid); 145 assertThat(discreteUidOps.mPackages.size()).isEqualTo(1); 146 147 DiscreteRegistry.DiscretePackageOps discretePackageOps = 148 discreteUidOps.mPackages.get(expectedPackageName); 149 assertThat(discretePackageOps.mPackageOps.size()).isEqualTo(1); 150 151 DiscreteRegistry.DiscreteOp discreteOp = discretePackageOps.mPackageOps.get(expectedOp); 152 assertThat(discreteOp.mDeviceAttributedOps.size()).isEqualTo(1); 153 154 DiscreteRegistry.DiscreteDeviceOp discreteDeviceOp = 155 discreteOp.mDeviceAttributedOps.get(expectedDeviceId); 156 assertThat(discreteDeviceOp.mAttributedOps.size()).isEqualTo(1); 157 158 List<DiscreteRegistry.DiscreteOpEvent> discreteOpEvents = 159 discreteDeviceOp.mAttributedOps.get(expectedAttrTag); 160 assertThat(discreteOpEvents.size()).isEqualTo(1); 161 162 DiscreteRegistry.DiscreteOpEvent discreteOpEvent = discreteOpEvents.get(0); 163 assertThat(discreteOpEvent.mNoteTime).isEqualTo(expectedAccessTime); 164 assertThat(discreteOpEvent.mNoteDuration).isEqualTo(expectedAccessDuration); 165 assertThat(discreteOpEvent.mUidState).isEqualTo(expectedUidState); 166 assertThat(discreteOpEvent.mOpFlag).isEqualTo(expectedOpFlags); 167 assertThat(discreteOpEvent.mAttributionFlags).isEqualTo(expectedAttrFlags); 168 assertThat(discreteOpEvent.mAttributionChainId).isEqualTo(expectedAttrChainId); 169 } 170 } 171