diff options
author | 2024-11-25 21:52:04 +0000 | |
---|---|---|
committer | 2024-11-25 21:52:04 +0000 | |
commit | ba5e334c46064d76b51107d7faa70ef0bca6a4f3 (patch) | |
tree | e7216f139eee9107c3f07207db44a33dd8b532b5 | |
parent | 9e1bc9ea4b0e9e348d0b46e67388fc2dee00f941 (diff) |
Added uninstall event metric handling and unit tests.
Change-Id: Ibaead8e08cea509610f644779a3e09de6fc44634
Flag: android.app.background_install_control_callback_api
Bug: 374120984
Test: atest
6 files changed, 225 insertions, 51 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index e18470498f39..a6b861302a39 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -966,6 +966,13 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +java_aconfig_library { + name: "android.app.flags-aconfig-java-host", + aconfig_declarations: "android.app.flags-aconfig", + host_supported: true, + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // Broadcast Radio aconfig_declarations { name: "android.hardware.radio.flags-aconfig", diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java index aabbd3bf49ae..d57f72c94164 100644 --- a/services/core/java/com/android/server/BinaryTransparencyService.java +++ b/services/core/java/com/android/server/BinaryTransparencyService.java @@ -101,6 +101,9 @@ import java.util.Map; import java.util.concurrent.Executors; import java.util.stream.Collectors; +import com.android.server.pm.BackgroundInstallControlService; +import com.android.server.pm.BackgroundInstallControlCallbackHelper; + /** * @hide */ @@ -138,6 +141,10 @@ public class BinaryTransparencyService extends SystemService { static final int MBA_STATUS_NEW_INSTALL = 3; // used for indicating newly installed MBAs that are updated (but unused currently) static final int MBA_STATUS_UPDATED_NEW_INSTALL = 4; + // used for indicating preloaded MBAs that are downgraded + static final int MBA_STATUS_DOWNGRADED_PRELOADED = 5; + // used for indicating MBAs that are uninstalled + static final int MBA_STATUS_UNINSTALLED = 6; @VisibleForTesting static final String KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION = @@ -202,7 +209,9 @@ public class BinaryTransparencyService extends SystemService { * @param mbaStatus Assign this value of MBA status to the returned elements. * @return a @{@code List<IBinaryTransparencyService.AppInfo>} */ - private @NonNull List<IBinaryTransparencyService.AppInfo> collectAppInfo( + @VisibleForTesting + @NonNull + List<IBinaryTransparencyService.AppInfo> collectAppInfo( PackageState packageState, int mbaStatus) { // compute content digest if (DEBUG) { @@ -336,27 +345,28 @@ public class BinaryTransparencyService extends SystemService { + " packages after considering APEXs."); } - // proceed with all preloaded apps - List<IBinaryTransparencyService.AppInfo> allUpdatedPreloadInfo = - collectAllUpdatedPreloadInfo(packagesMeasured); - for (IBinaryTransparencyService.AppInfo appInfo : allUpdatedPreloadInfo) { - packagesMeasured.putBoolean(appInfo.packageName, true); - writeAppInfoToLog(appInfo); - } - if (DEBUG) { - Slog.d(TAG, "Measured " + packagesMeasured.size() - + " packages after considering preloads"); - } - - if (!android.app.Flags.backgroundInstallControlCallbackApi() - && CompatChanges.isChangeEnabled(LOG_MBA_INFO)) { - // lastly measure all newly installed MBAs - List<IBinaryTransparencyService.AppInfo> allMbaInfo = - collectAllSilentInstalledMbaInfo(packagesMeasured); - for (IBinaryTransparencyService.AppInfo appInfo : allMbaInfo) { + if (!android.app.Flags.backgroundInstallControlCallbackApi()) { + // proceed with all preloaded apps + List<IBinaryTransparencyService.AppInfo> allUpdatedPreloadInfo = + collectAllUpdatedPreloadInfo(packagesMeasured); + for (IBinaryTransparencyService.AppInfo appInfo : allUpdatedPreloadInfo) { packagesMeasured.putBoolean(appInfo.packageName, true); writeAppInfoToLog(appInfo); } + if (DEBUG) { + Slog.d(TAG, "Measured " + packagesMeasured.size() + + " packages after considering preloads"); + } + + if (CompatChanges.isChangeEnabled(LOG_MBA_INFO)) { + // lastly measure all newly installed MBAs + List<IBinaryTransparencyService.AppInfo> allMbaInfo = + collectAllSilentInstalledMbaInfo(packagesMeasured); + for (IBinaryTransparencyService.AppInfo appInfo : allMbaInfo) { + packagesMeasured.putBoolean(appInfo.packageName, true); + writeAppInfoToLog(appInfo); + } + } } long timeSpentMeasuring = System.currentTimeMillis() - currentTimeMs; digestAllPackagesLatency.logSample(timeSpentMeasuring); @@ -466,7 +476,8 @@ public class BinaryTransparencyService extends SystemService { apexInfo.signerDigests); } - private void writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo) { + @VisibleForTesting + void writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo) { // Must order by the proto's field number. FrameworkStatsLog.write(FrameworkStatsLog.MOBILE_BUNDLED_APP_INFO_GATHERED, appInfo.packageName, @@ -1165,41 +1176,86 @@ public class BinaryTransparencyService extends SystemService { * TODO: Add a host test for testing registration and callback of BicCallbackHandler * b/380002484 */ + @VisibleForTesting static class BicCallbackHandler extends IRemoteCallback.Stub { - private static final String BIC_CALLBACK_HANDLER_TAG = - "BTS.BicCallbackHandler"; - private final BinaryTransparencyServiceImpl mServiceImpl; - static final String FLAGGED_PACKAGE_NAME_KEY = "packageName"; + private static final String BIC_CALLBACK_HANDLER_TAG = TAG + ".BicCallbackHandler"; + + private static final int INSTALL_EVENT_TYPE_UNSET = -1; + + private final IBicAppInfoHelper mBicAppInfoHelper; - BicCallbackHandler(BinaryTransparencyServiceImpl impl) { - mServiceImpl = impl; + @VisibleForTesting + BicCallbackHandler(IBicAppInfoHelper bicAppInfoHelper) { + mBicAppInfoHelper = bicAppInfoHelper; } @Override public void sendResult(Bundle data) { - String packageName = data.getString(FLAGGED_PACKAGE_NAME_KEY); - if (packageName == null) return; - if (DEBUG) { - Slog.d(BIC_CALLBACK_HANDLER_TAG, "background install event detected for " - + packageName); - } - - PackageState packageState = LocalServices.getService(PackageManagerInternal.class) - .getPackageStateInternal(packageName); - if (packageState == null) { - Slog.w(TAG, "Package state is unavailable, ignoring the package " - + packageName); - return; - } - if (packageState.isUpdatedSystemApp()) { + String packageName = data.getString( + BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY); + int installType = data.getInt( + BackgroundInstallControlCallbackHelper.INSTALL_EVENT_TYPE_KEY, + INSTALL_EVENT_TYPE_UNSET); + if (packageName == null || installType == INSTALL_EVENT_TYPE_UNSET) { + Slog.w(BIC_CALLBACK_HANDLER_TAG, "Package name or install type is " + + "unavailable, ignoring event"); return; } - List<IBinaryTransparencyService.AppInfo> mbaInfo = mServiceImpl.collectAppInfo( - packageState, MBA_STATUS_NEW_INSTALL); - for (IBinaryTransparencyService.AppInfo appInfo : mbaInfo) { - mServiceImpl.writeAppInfoToLog(appInfo); + Slog.d(BIC_CALLBACK_HANDLER_TAG, "Detected new bic event for: " + packageName); + if (installType == BackgroundInstallControlService.INSTALL_EVENT_TYPE_INSTALL) { + PackageState packageState = LocalServices.getService(PackageManagerInternal.class) + .getPackageStateInternal(packageName); + if (packageState == null) { + Slog.w(TAG, "Package state is unavailable, ignoring the package " + + packageName); + return; + } + int mbaStatus = MBA_STATUS_NEW_INSTALL; + if (packageState.isUpdatedSystemApp()) { + mbaStatus = MBA_STATUS_UPDATED_PRELOAD; + } + List<IBinaryTransparencyService.AppInfo> mbaInfo = mBicAppInfoHelper.collectAppInfo( + packageState, mbaStatus); + for (IBinaryTransparencyService.AppInfo appInfo : mbaInfo) { + mBicAppInfoHelper.writeAppInfoToLog(appInfo); + } + } else if (installType + == BackgroundInstallControlService.INSTALL_EVENT_TYPE_UNINSTALL) { + IBinaryTransparencyService.AppInfo appInfo + = new IBinaryTransparencyService.AppInfo(); + // since app is already uninstalled we won't be able to retrieve additional + // info on it. + appInfo.packageName = packageName; + appInfo.mbaStatus = MBA_STATUS_UNINSTALLED; + mBicAppInfoHelper.writeAppInfoToLog(appInfo); + } else { + Slog.w(BIC_CALLBACK_HANDLER_TAG, "Unsupported BIC event: " + installType); } } + + /** + * A wrapper of interface for{@link FrameworkStatsLog and ApkDigests} + * for easier testing + */ + @VisibleForTesting + public interface IBicAppInfoHelper { + + /** + * A wrapper of {@link FrameworkStatsLog} + * + * @param appInfo The app info of the changed MBA to be logged + */ + public void writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo); + + /** + * A wrapper of {@link BinaryTransparencyServiceImpl} + * + * @param packageState The packageState provided retrieved from PackageManagerInternal + * @param mbaStatus The MBA status of the package + */ + public List<IBinaryTransparencyService.AppInfo> collectAppInfo( + PackageState packageState, int mbaStatus); + } }; /** @@ -1586,7 +1642,21 @@ public class BinaryTransparencyService extends SystemService { Context.BACKGROUND_INSTALL_CONTROL_SERVICE)); try { iBics.registerBackgroundInstallCallback( - new BicCallbackHandler(mServiceImpl)); + new BicCallbackHandler( + new BicCallbackHandler.IBicAppInfoHelper() { + @Override + public void writeAppInfoToLog( + IBinaryTransparencyService.AppInfo appInfo) { + mServiceImpl.writeAppInfoToLog(appInfo); + } + + @Override + public List<IBinaryTransparencyService.AppInfo> collectAppInfo( + PackageState packageState, int mbaStatus) { + return mServiceImpl.collectAppInfo(packageState, mbaStatus); + } + } + )); } catch (RemoteException e) { Slog.e(TAG, "Failed to register BackgroundInstallControl callback."); } @@ -1633,8 +1703,12 @@ public class BinaryTransparencyService extends SystemService { } String packageName = data.getSchemeSpecificPart(); - // now we've got to check what package is this - if (isPackagePreloaded(packageName) || isPackageAnApex(packageName)) { + + boolean shouldMeasureMba = + !android.app.Flags.backgroundInstallControlCallbackApi() + && isPackagePreloaded(packageName); + + if (shouldMeasureMba || isPackageAnApex(packageName)) { Slog.d(TAG, packageName + " was updated. Scheduling measurement..."); UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext, BinaryTransparencyService.this); diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java index 27c4e9dca586..bc0fc2b0b7d9 100644 --- a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java +++ b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java @@ -33,9 +33,10 @@ import com.android.server.ServiceThread; public class BackgroundInstallControlCallbackHelper { - @VisibleForTesting static final String FLAGGED_PACKAGE_NAME_KEY = "packageName"; - @VisibleForTesting static final String FLAGGED_USER_ID_KEY = "userId"; - @VisibleForTesting static final String INSTALL_EVENT_TYPE_KEY = "installEventType"; + public static final String FLAGGED_PACKAGE_NAME_KEY = "packageName"; + public static final String FLAGGED_USER_ID_KEY = "userId"; + public static final String INSTALL_EVENT_TYPE_KEY = "installEventType"; + private static final String TAG = "BackgroundInstallControlCallbackHelper"; private final Handler mHandler; diff --git a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java index ae78dfe624c6..cc5be7ebba62 100644 --- a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java @@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -40,6 +41,7 @@ import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; +import android.os.Bundle; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.SystemProperties; @@ -50,6 +52,12 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.util.FrameworkStatsLog; +import com.android.internal.os.IBinaryTransparencyService; +import com.android.server.pm.BackgroundInstallControlService; +import com.android.server.pm.BackgroundInstallControlCallbackHelper; +import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.AndroidPackageSplit; +import com.android.server.pm.pkg.PackageStateInternal; import org.junit.After; import org.junit.Assert; @@ -68,6 +76,9 @@ import java.util.List; public class BinaryTransparencyServiceTest { private static final String TAG = "BinaryTransparencyServiceTest"; + private static final String TEST_PKG_NAME = "testPackageName"; + private static final long TEST_VERSION_CODE = 1L; + private Context mContext; private BinaryTransparencyService mBinaryTransparencyService; private BinaryTransparencyService.BinaryTransparencyServiceImpl mTestInterface; @@ -83,6 +94,8 @@ public class BinaryTransparencyServiceTest { private PackageManager mPackageManager; @Mock private PackageManagerInternal mPackageManagerInternal; + @Mock + private BinaryTransparencyService.BicCallbackHandler.IBicAppInfoHelper mBicAppInfoHelper; @Captor private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> @@ -91,6 +104,9 @@ public class BinaryTransparencyServiceTest { private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mFaceAuthenticatorsRegisteredCaptor; + @Captor + private ArgumentCaptor<IBinaryTransparencyService.AppInfo> appInfoCaptor; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -262,4 +278,69 @@ public class BinaryTransparencyServiceTest { eq("") /* softwareVersion */ ); } + + @Test + public void BicCallbackHandler_uploads_mba_metrics() { + Bundle data = setupBicCallbackHandlerTest(false, + BinaryTransparencyService.MBA_STATUS_NEW_INSTALL); + + BinaryTransparencyService.BicCallbackHandler handler = + new BinaryTransparencyService.BicCallbackHandler(mBicAppInfoHelper); + handler.sendResult(data); + + verify(mBicAppInfoHelper, times(1)).writeAppInfoToLog(appInfoCaptor.capture()); + Assert.assertEquals(TEST_PKG_NAME, appInfoCaptor.getValue().packageName); + Assert.assertEquals(TEST_VERSION_CODE, appInfoCaptor.getValue().longVersion); + } + + @Test + public void BicCallbackHandler_uploads_mba_metrics_for_preloads() { + Bundle data = setupBicCallbackHandlerTest(true, + BinaryTransparencyService.MBA_STATUS_UPDATED_PRELOAD); + + BinaryTransparencyService.BicCallbackHandler handler = + new BinaryTransparencyService.BicCallbackHandler(mBicAppInfoHelper); + handler.sendResult(data); + + verify(mBicAppInfoHelper, times(1)).writeAppInfoToLog(appInfoCaptor.capture()); + Assert.assertEquals(TEST_PKG_NAME, appInfoCaptor.getValue().packageName); + Assert.assertEquals(TEST_VERSION_CODE, appInfoCaptor.getValue().longVersion); + } + + @Test + public void BicCallbackHandler_uploads_mba_metrics_for_uninstalls() { + Bundle data = new Bundle(); + data.putString(BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY, + TEST_PKG_NAME); + data.putInt(BackgroundInstallControlCallbackHelper.INSTALL_EVENT_TYPE_KEY, + BackgroundInstallControlService.INSTALL_EVENT_TYPE_UNINSTALL); + + BinaryTransparencyService.BicCallbackHandler handler = + new BinaryTransparencyService.BicCallbackHandler(mBicAppInfoHelper); + handler.sendResult(data); + + verify(mBicAppInfoHelper, times(1)).writeAppInfoToLog(appInfoCaptor.capture()); + Assert.assertEquals(TEST_PKG_NAME ,appInfoCaptor.getValue().packageName); + Assert.assertEquals(BinaryTransparencyService.MBA_STATUS_UNINSTALLED, + appInfoCaptor.getValue().mbaStatus); + } + + private Bundle setupBicCallbackHandlerTest(boolean isUpdatedSystemApp, + int expectedBtsMbaStatus) { + Bundle data = new Bundle(); + data.putString(BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY, + TEST_PKG_NAME); + data.putInt(BackgroundInstallControlCallbackHelper.INSTALL_EVENT_TYPE_KEY, + BackgroundInstallControlService.INSTALL_EVENT_TYPE_INSTALL); + PackageStateInternal mockPackageState = mock(PackageStateInternal.class); + when(mPackageManagerInternal.getPackageStateInternal(TEST_PKG_NAME)) + .thenReturn(mockPackageState); + when(mockPackageState.isUpdatedSystemApp()).thenReturn(isUpdatedSystemApp); + IBinaryTransparencyService.AppInfo appInfo = new IBinaryTransparencyService.AppInfo(); + appInfo.packageName = TEST_PKG_NAME; + appInfo.longVersion = TEST_VERSION_CODE; + when(mBicAppInfoHelper.collectAppInfo(mockPackageState, expectedBtsMbaStatus)) + .thenReturn(List.of(appInfo)); + return data; + } } diff --git a/tests/BinaryTransparencyHostTest/Android.bp b/tests/BinaryTransparencyHostTest/Android.bp index e14e5fea001f..1c8386add1b1 100644 --- a/tests/BinaryTransparencyHostTest/Android.bp +++ b/tests/BinaryTransparencyHostTest/Android.bp @@ -31,6 +31,8 @@ java_test_host { ], static_libs: [ "truth", + "flag-junit-host", + "android.app.flags-aconfig-java-host", ], device_common_data: [ ":BinaryTransparencyTestApp", diff --git a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java index 6e5f08a11ed8..6d8dbcb5c963 100644 --- a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java +++ b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java @@ -24,6 +24,9 @@ import static org.junit.Assert.fail; import android.platform.test.annotations.LargeTest; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.host.HostFlagsValueProvider; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.log.LogUtil.CLog; @@ -34,6 +37,7 @@ import com.android.tradefed.util.CommandResult; import com.android.tradefed.util.CommandStatus; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,6 +53,10 @@ public final class BinaryTransparencyHostTest extends BaseHostJUnit4Test { /** Waiting time for the job to be scheduled */ private static final int JOB_CREATION_MAX_SECONDS = 30; + @Rule + public final CheckFlagsRule mCheckFlagsRule = + HostFlagsValueProvider.createCheckFlagsRule(this::getDevice); + @Before public void setUp() throws Exception { cancelPendingJob(); @@ -123,6 +131,7 @@ public final class BinaryTransparencyHostTest extends BaseHostJUnit4Test { } } + @RequiresFlagsDisabled(android.app.Flags.FLAG_BACKGROUND_INSTALL_CONTROL_CALLBACK_API) @Test public void testPreloadUpdateTriggersJobScheduling() throws Exception { try { |