1 /* 2 * Copyright (C) 2022 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.car.telemetry; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyBoolean; 23 import static org.mockito.ArgumentMatchers.eq; 24 import static org.mockito.ArgumentMatchers.isNull; 25 import static org.mockito.Mockito.doAnswer; 26 import static org.mockito.Mockito.when; 27 28 import android.content.BroadcastReceiver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.content.pm.ApplicationInfo; 33 import android.content.pm.PackageInfo; 34 import android.content.pm.PackageManager; 35 import android.net.Uri; 36 import android.os.Looper; 37 import android.os.UserHandle; 38 import android.os.UserManager; 39 40 import com.android.car.test.FakeHandlerWrapper; 41 42 import org.junit.Before; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 import org.mockito.Mock; 46 import org.mockito.invocation.InvocationOnMock; 47 import org.mockito.junit.MockitoJUnitRunner; 48 49 import java.util.ArrayList; 50 import java.util.Iterator; 51 import java.util.List; 52 import java.util.concurrent.atomic.AtomicReference; 53 54 @RunWith(MockitoJUnitRunner.class) 55 public class UidPackageMapperTest { 56 private static final int PACKAGE_ALL_FLAGS = 57 PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_ANY_USER; 58 59 private static final int MAX_REMOVED_APPS_COUNT = 1; 60 61 // List of PackageInfo for uid/packageName. 62 private static final PackageInfo sPackageA = buildPackageInfo(1, "com.android.a", false); 63 private static final PackageInfo sPackageB = buildPackageInfo(1, "com.android.b", false); 64 private static final PackageInfo sPackageC = buildPackageInfo(2, "com.android.c", false); 65 private static final PackageInfo sPackageApex = buildPackageInfo(4, "com.andr.apex", true); 66 private static final PackageInfo sPackageN = buildPackageInfo(5, "com.android.n", false); 67 private static final PackageInfo sPackageX = buildPackageInfo(10, "com.android.x", false); 68 69 private static final UserHandle sUserHandle1 = new UserHandle(/* userId= */ 1); 70 private static final UserHandle sUserHandle2New = new UserHandle(/* userId= */ 2); 71 72 @Mock private PackageManager mMockPackageManager; 73 @Mock private UserManager mMockUserManager; 74 @Mock private Context mMockContext; 75 76 private final AtomicReference<BroadcastReceiver> mUserUpdateReceiver = new AtomicReference<>(); 77 private final AtomicReference<BroadcastReceiver> mAppUpdateReceiver = new AtomicReference<>(); 78 79 private FakeHandlerWrapper mDirectHandler = 80 new FakeHandlerWrapper(Looper.getMainLooper(), FakeHandlerWrapper.Mode.IMMEDIATE); 81 private UidPackageMapper mUidMapper; // subject 82 83 @Before setUp()84 public void setUp() { 85 when(mMockUserManager.getUserHandles(anyBoolean())).thenReturn(List.of(sUserHandle1)); 86 87 // sUserHandle1 packages 88 when(mMockPackageManager.getInstalledPackagesAsUser( 89 eq(PACKAGE_ALL_FLAGS), eq(sUserHandle1.getIdentifier()))) 90 .thenReturn(List.of(sPackageA, sPackageB, sPackageC)); 91 // sUserHandle2New packages 92 when(mMockPackageManager.getInstalledPackagesAsUser( 93 eq(PACKAGE_ALL_FLAGS), eq(sUserHandle2New.getIdentifier()))) 94 .thenReturn(List.of(sPackageX)); 95 // Apex packages 96 when(mMockPackageManager.getInstalledPackages(eq(PackageManager.MATCH_APEX))) 97 .thenReturn(List.of(sPackageApex)); 98 99 when(mMockContext.getSystemService(UserManager.class)).thenReturn(mMockUserManager); 100 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 101 102 doAnswer(this::onRegisterReceiverForAllUsers) 103 .when(mMockContext) 104 .registerReceiverForAllUsers(any(), any(), isNull(), isNull()); 105 106 mUidMapper = 107 new UidPackageMapper( 108 mMockContext, mDirectHandler.getMockHandler(), MAX_REMOVED_APPS_COUNT); 109 } 110 onRegisterReceiverForAllUsers(InvocationOnMock invocation)111 private Intent onRegisterReceiverForAllUsers(InvocationOnMock invocation) { 112 IntentFilter filter = invocation.getArgument(1); 113 if (toList(filter.actionsIterator()).contains(Intent.ACTION_PACKAGE_ADDED)) { 114 mAppUpdateReceiver.set(invocation.getArgument(0)); 115 } 116 if (toList(filter.actionsIterator()).contains(Intent.ACTION_USER_INITIALIZE)) { 117 mUserUpdateReceiver.set(invocation.getArgument(0)); 118 } 119 return null; // should return Intent, but the result is not used. 120 } 121 122 @Test testInitPullsAllPackages()123 public void testInitPullsAllPackages() { 124 mUidMapper.init(); 125 126 assertThat(mUidMapper.getPackagesForUid(1)) 127 .containsExactly("com.android.a", "com.android.b"); 128 assertThat(mUidMapper.getPackagesForUid(2)).containsExactly("com.android.c"); 129 assertThat(mUidMapper.getPackagesForUid(4)).containsExactly("com.andr.apex"); 130 assertThat(mUidMapper.getPackagesForUid(5)).isEmpty(); 131 assertThat(mUidMapper.getPackagesForUid(10)).isEmpty(); 132 } 133 134 @Test testOnAppInstalled()135 public void testOnAppInstalled() { 136 mUidMapper.init(); 137 138 broadcastAppAdded(sPackageN); // uid = 5 139 140 assertThat(mUidMapper.getPackagesForUid(5)).containsExactly("com.android.n"); 141 assertThat(mUidMapper.getPackagesForUid(1)) // other apps are there too 142 .containsExactly("com.android.a", "com.android.b"); 143 } 144 145 @Test testOnAppRemoved()146 public void testOnAppRemoved() { 147 mUidMapper.init(); 148 149 broadcastAppRemoved(sPackageB); 150 151 broadcastAppRemoved(sPackageC); // To remove B from the cache, remove C too. 152 153 // "com.android.b" is removed, and deleted from UidPackageMapper cache too, 154 // because MAX_REMOVED_APPS_COUNT = 1. 155 assertThat(mUidMapper.getPackagesForUid(1)).containsExactly("com.android.a"); 156 // "com.android.c" is still cached in UidPackageMapper, even if the app is removed. 157 assertThat(mUidMapper.getPackagesForUid(2)).containsExactly("com.android.c"); 158 } 159 160 // Tests if cached uninstalled apps are handled well, when installed back again. 161 @Test testOnAppRemovedThenInstalled()162 public void testOnAppRemovedThenInstalled() { 163 mUidMapper.init(); 164 165 broadcastAppRemoved(sPackageB); 166 167 broadcastAppAdded(sPackageB); 168 169 // Remove "sPackageC" to trigger cache cleanup, it should try to remove "sPackageB", 170 // but mustn't, as B is installed again. 171 broadcastAppRemoved(sPackageC); 172 173 // Remove "sPackageApex" to trigger to cleanup "sPackageC". 174 broadcastAppRemoved(sPackageApex); 175 176 assertThat(mUidMapper.getPackagesForUid(1)) 177 .containsExactly("com.android.a", "com.android.b"); 178 assertThat(mUidMapper.getPackagesForUid(2)).isEmpty(); // sPackageC is cleaned-up from cache 179 } 180 181 @Test testOnUserAdded()182 public void testOnUserAdded() { 183 mUidMapper.init(); 184 when(mMockUserManager.getUserHandles(anyBoolean())) 185 .thenReturn(List.of(sUserHandle1, sUserHandle2New)); 186 187 Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE); 188 mUserUpdateReceiver.get().onReceive(mMockContext, intent); 189 190 assertThat(mUidMapper.getPackagesForUid(1)) 191 .containsExactly("com.android.a", "com.android.b"); 192 // "com.android.x" already exists in sUserHandle2New. 193 assertThat(mUidMapper.getPackagesForUid(10)).containsExactly("com.android.x"); 194 } 195 broadcastAppAdded(PackageInfo packageInfo)196 private void broadcastAppAdded(PackageInfo packageInfo) { 197 Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED); 198 intent.setData(Uri.fromParts("package", packageInfo.packageName, null)); 199 intent.putExtra(Intent.EXTRA_UID, packageInfo.applicationInfo.uid); 200 mAppUpdateReceiver.get().onReceive(mMockContext, intent); 201 } 202 broadcastAppRemoved(PackageInfo packageInfo)203 private void broadcastAppRemoved(PackageInfo packageInfo) { 204 Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED); 205 intent.setData(Uri.fromParts("package", packageInfo.packageName, null)); 206 intent.putExtra(Intent.EXTRA_UID, packageInfo.applicationInfo.uid); 207 mAppUpdateReceiver.get().onReceive(mMockContext, intent); 208 } 209 210 /** Converts iterator to a list. */ toList(Iterator<T> iterator)211 private static <T> List<T> toList(Iterator<T> iterator) { 212 ArrayList<T> result = new ArrayList<>(); 213 iterator.forEachRemaining(result::add); 214 return result; 215 } 216 buildPackageInfo(int uid, String packageName, boolean isApex)217 private static PackageInfo buildPackageInfo(int uid, String packageName, boolean isApex) { 218 PackageInfo info = new PackageInfo(); 219 info.packageName = packageName; 220 info.applicationInfo = new ApplicationInfo(); 221 info.applicationInfo.uid = uid; 222 info.isApex = isApex; 223 return info; 224 } 225 } 226