1 /* 2 * Copyright (C) 2023 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 android.transparency.test.app; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static com.google.common.truth.Truth.assertWithMessage; 21 22 import android.content.Context; 23 import android.os.Bundle; 24 import android.transparency.BinaryTransparencyManager; 25 import android.util.Log; 26 27 import androidx.test.platform.app.InstrumentationRegistry; 28 import androidx.test.runner.AndroidJUnit4; 29 30 import com.android.internal.os.IBinaryTransparencyService.AppInfo; 31 32 import org.junit.Before; 33 import org.junit.Test; 34 import org.junit.runner.RunWith; 35 36 import java.util.ArrayList; 37 import java.util.HashSet; 38 import java.util.HexFormat; 39 import java.util.Set; 40 import java.util.stream.Collectors; 41 42 @RunWith(AndroidJUnit4.class) 43 public class BinaryTransparencyTest { 44 private static final String TAG = "BinaryTransparencyTest"; 45 46 private BinaryTransparencyManager mBt; 47 48 @Before setUp()49 public void setUp() { 50 Context context = InstrumentationRegistry.getInstrumentation().getContext(); 51 mBt = context.getSystemService(BinaryTransparencyManager.class); 52 } 53 54 @Test testCollectAllApexInfo()55 public void testCollectAllApexInfo() { 56 // Prepare the expectation received from host's shell command 57 Bundle args = InstrumentationRegistry.getArguments(); 58 assertThat(args).isNotNull(); 59 int number = Integer.valueOf(args.getString("apex-number")); 60 assertThat(number).isGreaterThan(0); 61 var expectedApexNames = new ArrayList<String>(); 62 for (var i = 0; i < number; i++) { 63 String moduleName = args.getString("apex-" + Integer.toString(i)); 64 expectedApexNames.add(moduleName); 65 } 66 assertThat(expectedApexNames).containsNoDuplicates(); 67 68 // Action 69 var apexInfoList = mBt.collectAllApexInfo(/* includeTestOnly */ true); 70 71 // Verify actual apex names 72 var actualApexesNames = apexInfoList.stream().map((apex) -> apex.moduleName) 73 .collect(Collectors.toList()); 74 assertThat(actualApexesNames).containsExactlyElementsIn(expectedApexNames); 75 76 // Perform more valitidy checks 77 var digestsSeen = new HashSet<String>(); 78 var hexFormatter = HexFormat.of(); 79 for (var apex : apexInfoList) { 80 Log.d(TAG, "Verifying " + apex.packageName + " / " + apex.moduleName); 81 82 assertThat(apex.longVersion).isGreaterThan(0); 83 assertThat(apex.digestAlgorithm).isGreaterThan(0); 84 assertThat(apex.signerDigests).asList().containsNoneOf(null, ""); 85 86 assertThat(apex.digest).isNotNull(); 87 String digestHex = hexFormatter.formatHex(apex.digest); 88 boolean isNew = digestsSeen.add(digestHex); 89 assertWithMessage( 90 "Digest should be unique, but received a dup: " + digestHex) 91 .that(isNew).isTrue(); 92 } 93 } 94 95 @Test testCollectAllUpdatedPreloadInfo()96 public void testCollectAllUpdatedPreloadInfo() { 97 var preloadInfoList = mBt.collectAllUpdatedPreloadInfo(new Bundle()); 98 assertThat(preloadInfoList).isNotEmpty(); // because we just installed from the host side 99 AppInfo updatedPreload = null; 100 for (var preload : preloadInfoList) { 101 Log.d(TAG, "Received " + preload.packageName); 102 if (preload.packageName.equals("com.android.egg")) { 103 assertWithMessage("Received the same package").that(updatedPreload).isNull(); 104 updatedPreload = preload; 105 } 106 } 107 108 // Verify 109 assertThat(updatedPreload.longVersion).isGreaterThan(0); 110 assertThat(updatedPreload.digestAlgorithm).isGreaterThan(0); 111 assertThat(updatedPreload.digest).isNotEmpty(); 112 assertThat(updatedPreload.mbaStatus).isEqualTo(/* MBA_STATUS_UPDATED_PRELOAD */ 2); 113 assertThat(updatedPreload.signerDigests).asList().containsNoneOf(null, ""); 114 } 115 116 @Test testCollectAllSilentInstalledMbaInfo()117 public void testCollectAllSilentInstalledMbaInfo() { 118 // Action 119 var appInfoList = mBt.collectAllSilentInstalledMbaInfo(new Bundle()); 120 121 // Verify 122 assertThat(appInfoList).isNotEmpty(); // because we just installed from the host side 123 124 var expectedAppNames = Set.of("com.android.apkverity", "com.android.egg"); 125 var actualAppNames = appInfoList.stream().map((appInfo) -> appInfo.packageName) 126 .collect(Collectors.toList()); 127 assertThat(actualAppNames).containsAtLeastElementsIn(expectedAppNames); 128 129 var actualSplitNames = new ArrayList<String>(); 130 for (var appInfo : appInfoList) { 131 Log.d(TAG, "Received " + appInfo.packageName + " as a silent install"); 132 if (expectedAppNames.contains(appInfo.packageName)) { 133 assertThat(appInfo.longVersion).isGreaterThan(0); 134 assertThat(appInfo.digestAlgorithm).isGreaterThan(0); 135 assertThat(appInfo.digest).isNotEmpty(); 136 assertThat(appInfo.mbaStatus).isEqualTo(/* MBA_STATUS_NEW_INSTALL */ 3); 137 assertThat(appInfo.signerDigests).asList().containsNoneOf(null, ""); 138 139 if (appInfo.splitName != null) { 140 actualSplitNames.add(appInfo.splitName); 141 } 142 } 143 } 144 assertThat(actualSplitNames).containsExactly("feature_x"); // Name of ApkVerityTestAppSplit 145 } 146 } 147