/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.transparency.test.app; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import android.content.Context; import android.os.Bundle; import android.transparency.BinaryTransparencyManager; import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.IBinaryTransparencyService.AppInfo; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.HashSet; import java.util.HexFormat; import java.util.Set; import java.util.stream.Collectors; @RunWith(AndroidJUnit4.class) public class BinaryTransparencyTest { private static final String TAG = "BinaryTransparencyTest"; private BinaryTransparencyManager mBt; @Before public void setUp() { Context context = InstrumentationRegistry.getInstrumentation().getContext(); mBt = context.getSystemService(BinaryTransparencyManager.class); } @Test public void testCollectAllApexInfo() { // Prepare the expectation received from host's shell command Bundle args = InstrumentationRegistry.getArguments(); assertThat(args).isNotNull(); int number = Integer.valueOf(args.getString("apex-number")); assertThat(number).isGreaterThan(0); var expectedApexNames = new ArrayList(); for (var i = 0; i < number; i++) { String moduleName = args.getString("apex-" + Integer.toString(i)); expectedApexNames.add(moduleName); } assertThat(expectedApexNames).containsNoDuplicates(); // Action var apexInfoList = mBt.collectAllApexInfo(/* includeTestOnly */ true); // Verify actual apex names var actualApexesNames = apexInfoList.stream().map((apex) -> apex.moduleName) .collect(Collectors.toList()); assertThat(actualApexesNames).containsExactlyElementsIn(expectedApexNames); // Perform more valitidy checks var digestsSeen = new HashSet(); var hexFormatter = HexFormat.of(); for (var apex : apexInfoList) { Log.d(TAG, "Verifying " + apex.packageName + " / " + apex.moduleName); assertThat(apex.longVersion).isGreaterThan(0); assertThat(apex.digestAlgorithm).isGreaterThan(0); assertThat(apex.signerDigests).asList().containsNoneOf(null, ""); assertThat(apex.digest).isNotNull(); String digestHex = hexFormatter.formatHex(apex.digest); boolean isNew = digestsSeen.add(digestHex); assertWithMessage( "Digest should be unique, but received a dup: " + digestHex) .that(isNew).isTrue(); } } @Test public void testCollectAllUpdatedPreloadInfo() { var preloadInfoList = mBt.collectAllUpdatedPreloadInfo(new Bundle()); assertThat(preloadInfoList).isNotEmpty(); // because we just installed from the host side AppInfo updatedPreload = null; for (var preload : preloadInfoList) { Log.d(TAG, "Received " + preload.packageName); if (preload.packageName.equals("com.android.egg")) { assertWithMessage("Received the same package").that(updatedPreload).isNull(); updatedPreload = preload; } } // Verify assertThat(updatedPreload.longVersion).isGreaterThan(0); assertThat(updatedPreload.digestAlgorithm).isGreaterThan(0); assertThat(updatedPreload.digest).isNotEmpty(); assertThat(updatedPreload.mbaStatus).isEqualTo(/* MBA_STATUS_UPDATED_PRELOAD */ 2); assertThat(updatedPreload.signerDigests).asList().containsNoneOf(null, ""); } @Test public void testCollectAllSilentInstalledMbaInfo() { // Action var appInfoList = mBt.collectAllSilentInstalledMbaInfo(new Bundle()); // Verify assertThat(appInfoList).isNotEmpty(); // because we just installed from the host side var expectedAppNames = Set.of("com.android.apkverity", "com.android.egg"); var actualAppNames = appInfoList.stream().map((appInfo) -> appInfo.packageName) .collect(Collectors.toList()); assertThat(actualAppNames).containsAtLeastElementsIn(expectedAppNames); var actualSplitNames = new ArrayList(); for (var appInfo : appInfoList) { Log.d(TAG, "Received " + appInfo.packageName + " as a silent install"); if (expectedAppNames.contains(appInfo.packageName)) { assertThat(appInfo.longVersion).isGreaterThan(0); assertThat(appInfo.digestAlgorithm).isGreaterThan(0); assertThat(appInfo.digest).isNotEmpty(); assertThat(appInfo.mbaStatus).isEqualTo(/* MBA_STATUS_NEW_INSTALL */ 3); assertThat(appInfo.signerDigests).asList().containsNoneOf(null, ""); if (appInfo.splitName != null) { actualSplitNames.add(appInfo.splitName); } } } assertThat(actualSplitNames).containsExactly("feature_x"); // Name of ApkVerityTestAppSplit } }