Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ba5e334c authored by George Chan's avatar George Chan
Browse files

Added uninstall event metric handling and unit tests.

Change-Id: Ibaead8e08cea509610f644779a3e09de6fc44634
Flag: android.app.background_install_control_callback_api
Bug: 374120984
Test: atest
parent 9e1bc9ea
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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",
+122 −48
Original line number Diff line number Diff line
@@ -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,6 +345,7 @@ public class BinaryTransparencyService extends SystemService {
                        + " packages after considering APEXs.");
            }

            if (!android.app.Flags.backgroundInstallControlCallbackApi()) {
                // proceed with all preloaded apps
                List<IBinaryTransparencyService.AppInfo> allUpdatedPreloadInfo =
                        collectAllUpdatedPreloadInfo(packagesMeasured);
@@ -348,8 +358,7 @@ public class BinaryTransparencyService extends SystemService {
                            + " packages after considering preloads");
                }

            if (!android.app.Flags.backgroundInstallControlCallbackApi()
                    && CompatChanges.isChangeEnabled(LOG_MBA_INFO)) {
                if (CompatChanges.isChangeEnabled(LOG_MBA_INFO)) {
                    // lastly measure all newly installed MBAs
                    List<IBinaryTransparencyService.AppInfo> allMbaInfo =
                            collectAllSilentInstalledMbaInfo(packagesMeasured);
@@ -358,6 +367,7 @@ public class BinaryTransparencyService extends SystemService {
                        writeAppInfoToLog(appInfo);
                    }
                }
            }
            long timeSpentMeasuring = System.currentTimeMillis() - currentTimeMs;
            digestAllPackagesLatency.logSample(timeSpentMeasuring);
            if (DEBUG) {
@@ -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,25 +1176,33 @@ 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;

        BicCallbackHandler(BinaryTransparencyServiceImpl impl) {
            mServiceImpl = impl;
        private final IBicAppInfoHelper mBicAppInfoHelper;

        @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);
            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;
            }

            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) {
@@ -1191,15 +1210,52 @@ public class BinaryTransparencyService extends SystemService {
                            + packageName);
                    return;
                }
                int mbaStatus = MBA_STATUS_NEW_INSTALL;
                if (packageState.isUpdatedSystemApp()) {
                return;
                    mbaStatus = MBA_STATUS_UPDATED_PRELOAD;
                }
            List<IBinaryTransparencyService.AppInfo> mbaInfo = mServiceImpl.collectAppInfo(
                    packageState, MBA_STATUS_NEW_INSTALL);
                List<IBinaryTransparencyService.AppInfo> mbaInfo = mBicAppInfoHelper.collectAppInfo(
                        packageState, mbaStatus);
                for (IBinaryTransparencyService.AppInfo appInfo : mbaInfo) {
                mServiceImpl.writeAppInfoToLog(appInfo);
                    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);
+4 −3
Original line number Diff line number Diff line
@@ -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;
+81 −0
Original line number Diff line number Diff line
@@ -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;
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ java_test_host {
    ],
    static_libs: [
        "truth",
        "flag-junit-host",
        "android.app.flags-aconfig-java-host",
    ],
    device_common_data: [
        ":BinaryTransparencyTestApp",
Loading