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

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

Updated BackgroundInstallControlService to use historical sessions to

retrieve install start time.

Test: atest BinaryTransparencyHostTest BackgroundInstallControlServiceHostTest BackgroundInstallControlServiceTest BackgroundInstallControlCallbackHelperTest
Bug: 296058502
Change-Id: I663e80f9ccb98e825468980195d6267386f86184
parent 22735c97
Loading
Loading
Loading
Loading
+55 −17
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IBackgroundInstallControlService;
import android.content.pm.IBackgroundInstallControlService;
import android.content.pm.InstallSourceInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ParceledListSlice;
@@ -46,6 +47,7 @@ import android.os.UserHandle;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
import android.util.Slog;
import android.util.SparseArrayMap;
import android.util.SparseArrayMap;
import android.util.SparseSetArray;
import android.util.SparseSetArray;
@@ -63,8 +65,10 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.List;
import java.util.ListIterator;
import java.util.ListIterator;
import java.util.Optional;
import java.util.Set;
import java.util.Set;
import java.util.TreeSet;
import java.util.TreeSet;


@@ -103,6 +107,24 @@ public class BackgroundInstallControlService extends SystemService {
    private final SparseArrayMap<String, TreeSet<ForegroundTimeFrame>>
    private final SparseArrayMap<String, TreeSet<ForegroundTimeFrame>>
            mInstallerForegroundTimeFrames = new SparseArrayMap<>();
            mInstallerForegroundTimeFrames = new SparseArrayMap<>();


    @VisibleForTesting
    protected final PackageManagerInternal.PackageListObserver mPackageObserver =
            new PackageManagerInternal.PackageListObserver() {
                @Override
                public void onPackageAdded(String packageName, int uid) {
                    final int userId = UserHandle.getUserId(uid);
                    mHandler.obtainMessage(MSG_PACKAGE_ADDED, userId, 0, packageName)
                            .sendToTarget();
                }

                @Override
                public void onPackageRemoved(String packageName, int uid) {
                    final int userId = UserHandle.getUserId(uid);
                    mHandler.obtainMessage(MSG_PACKAGE_REMOVED, userId, 0, packageName)
                            .sendToTarget();
                }
            };

    public BackgroundInstallControlService(@NonNull Context context) {
    public BackgroundInstallControlService(@NonNull Context context) {
        this(new InjectorImpl(context));
        this(new InjectorImpl(context));
    }
    }
@@ -258,6 +280,7 @@ public class BackgroundInstallControlService extends SystemService {


        String installerPackageName;
        String installerPackageName;
        String initiatingPackageName;
        String initiatingPackageName;

        try {
        try {
            final InstallSourceInfo installInfo = mPackageManager.getInstallSourceInfo(packageName);
            final InstallSourceInfo installInfo = mPackageManager.getInstallSourceInfo(packageName);
            installerPackageName = installInfo.getInstallingPackageName();
            installerPackageName = installInfo.getInstallingPackageName();
@@ -280,7 +303,8 @@ public class BackgroundInstallControlService extends SystemService {


        // convert up-time to current time.
        // convert up-time to current time.
        final long installTimestamp =
        final long installTimestamp =
                System.currentTimeMillis() - (SystemClock.uptimeMillis() - appInfo.createTimestamp);
                System.currentTimeMillis() - (SystemClock.uptimeMillis()
                        - retrieveInstallStartTimestamp(packageName, userId, appInfo));


        if (installedByAdb(initiatingPackageName)
        if (installedByAdb(initiatingPackageName)
                || wasForegroundInstallation(installerPackageName, userId, installTimestamp)) {
                || wasForegroundInstallation(installerPackageName, userId, installTimestamp)) {
@@ -293,6 +317,35 @@ public class BackgroundInstallControlService extends SystemService {
        writeBackgroundInstalledPackagesToDisk();
        writeBackgroundInstalledPackagesToDisk();
    }
    }


    private long retrieveInstallStartTimestamp(String packageName,
                                               int userId, ApplicationInfo appInfo) {
        long installStartTimestamp = appInfo.createTimestamp;

        try {
            Optional<PackageInstaller.SessionInfo> latestInstallSession =
                    getLatestInstallSession(packageName, userId);
            if (latestInstallSession.isEmpty()) {
                Slog.w(TAG, "Package's historical install session not found, falling back "
                        + "to appInfo.createTimestamp: " + packageName);
            } else {
                installStartTimestamp = latestInstallSession.get().getCreatedMillis();
            }
        } catch (Exception e) {
            Slog.w(TAG, "Retrieval of install time from historical session failed, falling "
                    + "back to appInfo.createTimestamp");
            Slog.w(TAG, Log.getStackTraceString(e));
        }
        return installStartTimestamp;
    }

    private Optional<PackageInstaller.SessionInfo> getLatestInstallSession(
            String packageName, int userId) {
        List<PackageInstaller.SessionInfo> historicalSessions =
                mPackageManagerInternal.getHistoricalSessions(userId).getList();
        return historicalSessions.stream().filter(s -> packageName.equals(s.getAppPackageName()))
                .max(Comparator.comparingLong(PackageInstaller.SessionInfo::getCreatedMillis));
    }

    // ADB sets installerPackageName to null, this creates a loophole to bypass BIC which will be
    // ADB sets installerPackageName to null, this creates a loophole to bypass BIC which will be
    // addressed with b/265203007
    // addressed with b/265203007
    private boolean installedByAdb(String initiatingPackageName) {
    private boolean installedByAdb(String initiatingPackageName) {
@@ -496,22 +549,7 @@ public class BackgroundInstallControlService extends SystemService {
            publishBinderService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE, mBinderService);
            publishBinderService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE, mBinderService);
        }
        }


        mPackageManagerInternal.getPackageList(
        mPackageManagerInternal.getPackageList(mPackageObserver);
                new PackageManagerInternal.PackageListObserver() {
                    @Override
                    public void onPackageAdded(String packageName, int uid) {
                        final int userId = UserHandle.getUserId(uid);
                        mHandler.obtainMessage(MSG_PACKAGE_ADDED, userId, 0, packageName)
                                .sendToTarget();
                    }

                    @Override
                    public void onPackageRemoved(String packageName, int uid) {
                        final int userId = UserHandle.getUserId(uid);
                        mHandler.obtainMessage(MSG_PACKAGE_REMOVED, userId, 0, packageName)
                                .sendToTarget();
                    }
                });
    }
    }


    // The foreground time frame (ForegroundTimeFrame) represents the period
    // The foreground time frame (ForegroundTimeFrame) represents the period
+131 −23
Original line number Original line Diff line number Diff line
@@ -43,8 +43,10 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.os.FileUtils;
import android.os.FileUtils;
import android.os.Looper;
import android.os.Looper;
import android.os.RemoteException;
import android.os.RemoteException;
@@ -114,9 +116,6 @@ public final class BackgroundInstallControlServiceTest {
    @Mock
    @Mock
    private BackgroundInstallControlCallbackHelper mCallbackHelper;
    private BackgroundInstallControlCallbackHelper mCallbackHelper;


    @Captor
    private ArgumentCaptor<PackageManagerInternal.PackageListObserver> mPackageListObserverCaptor;

    @Captor
    @Captor
    private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor;
    private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor;


@@ -137,8 +136,8 @@ public final class BackgroundInstallControlServiceTest {
        mUsageEventListener = mUsageEventListenerCaptor.getValue();
        mUsageEventListener = mUsageEventListenerCaptor.getValue();


        mBackgroundInstallControlService.onStart(true);
        mBackgroundInstallControlService.onStart(true);
        verify(mPackageManagerInternal).getPackageList(mPackageListObserverCaptor.capture());

        mPackageListObserver = mPackageListObserverCaptor.getValue();
        mPackageListObserver = mBackgroundInstallControlService.mPackageObserver;
    }
    }


    @After
    @After
@@ -554,6 +553,7 @@ public final class BackgroundInstallControlServiceTest {
        assertEquals(0, foregroundTimeFrames.size());
        assertEquals(0, foregroundTimeFrames.size());
    }
    }


    //package installed, but no UI interaction found
    @Test
    @Test
    public void testHandleUsageEvent_packageAddedNoUsageEvent()
    public void testHandleUsageEvent_packageAddedNoUsageEvent()
            throws NoSuchFieldException, PackageManager.NameNotFoundException {
            throws NoSuchFieldException, PackageManager.NameNotFoundException {
@@ -571,12 +571,10 @@ public final class BackgroundInstallControlServiceTest {
        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
                .thenReturn(appInfo);
                .thenReturn(appInfo);


        long createTimestamp =
                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
        FieldSetter.setField(
        FieldSetter.setField(
                appInfo,
                appInfo,
                ApplicationInfo.class.getDeclaredField("createTimestamp"),
                ApplicationInfo.class.getDeclaredField("createTimestamp"),
                createTimestamp);
                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));


        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
        assertEquals(USER_ID_1, UserHandle.getUserId(uid));
        assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -590,6 +588,10 @@ public final class BackgroundInstallControlServiceTest {
        assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1));
        assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1));
    }
    }


    private long convertToTestAdjustTimestamp(long timestamp) {
        return timestamp - (System.currentTimeMillis() - SystemClock.uptimeMillis());
    }

    @Test
    @Test
    public void testHandleUsageEvent_packageAddedInsideTimeFrame()
    public void testHandleUsageEvent_packageAddedInsideTimeFrame()
            throws NoSuchFieldException, PackageManager.NameNotFoundException {
            throws NoSuchFieldException, PackageManager.NameNotFoundException {
@@ -607,12 +609,10 @@ public final class BackgroundInstallControlServiceTest {
        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
                .thenReturn(appInfo);
                .thenReturn(appInfo);


        long createTimestamp =
                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
        FieldSetter.setField(
        FieldSetter.setField(
                appInfo,
                appInfo,
                ApplicationInfo.class.getDeclaredField("createTimestamp"),
                ApplicationInfo.class.getDeclaredField("createTimestamp"),
                createTimestamp);
                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));


        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
        assertEquals(USER_ID_1, UserHandle.getUserId(uid));
        assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -638,6 +638,122 @@ public final class BackgroundInstallControlServiceTest {
        assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
        assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
    }
    }


    @Test
    public void testHandleUsageEvent_fallsBackToAppInfoTimeWhenHistoricalSessionsNotFound()
            throws NoSuchFieldException, PackageManager.NameNotFoundException {
        assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
        InstallSourceInfo installSourceInfo =
                new InstallSourceInfo(
                        /* initiatingPackageName= */ INSTALLER_NAME_1,
                        /* initiatingPackageSigningInfo= */ null,
                        /* originatingPackageName= */ null,
                        /* installingPackageName= */ INSTALLER_NAME_1);
        assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1);
        when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
        ApplicationInfo appInfo = mock(ApplicationInfo.class);

        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
                .thenReturn(appInfo);

        FieldSetter.setField(
                appInfo,
                ApplicationInfo.class.getDeclaredField("createTimestamp"),
                // create timestamp is after generated foreground events (hence not considered
                // foreground install)
                convertToTestAdjustTimestamp(USAGE_EVENT_TIMESTAMP_2 + 1));

        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
        assertEquals(USER_ID_1, UserHandle.getUserId(uid));

        createPackageManagerHistoricalSessions(List.of(), USER_ID_1);

        // The 2 relevants usage events are before the timeframe, the app is not considered
        // foreground installed.
        doReturn(PERMISSION_GRANTED)
                .when(mPermissionManager)
                .checkPermission(anyString(), anyString(), anyString(), anyInt());
        generateUsageEvent(
                UsageEvents.Event.ACTIVITY_RESUMED,
                USER_ID_1,
                INSTALLER_NAME_1,
                USAGE_EVENT_TIMESTAMP_1);
        generateUsageEvent(
                Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2);

        mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid);
        mTestLooper.dispatchAll();

        var packages = mBackgroundInstallControlService.getBackgroundInstalledPackages();
        assertNotNull(packages);
        assertEquals(1, packages.size());
        assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1));
    }

    @Test
    public void testHandleUsageEvent_usesHistoricalSessionCreateTimeWhenHistoricalSessionsFound()
            throws NoSuchFieldException, PackageManager.NameNotFoundException {
        assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
        InstallSourceInfo installSourceInfo =
                new InstallSourceInfo(
                        /* initiatingPackageName= */ INSTALLER_NAME_1,
                        /* initiatingPackageSigningInfo= */ null,
                        /* originatingPackageName= */ null,
                        /* installingPackageName= */ INSTALLER_NAME_1);
        assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1);
        when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
        ApplicationInfo appInfo = mock(ApplicationInfo.class);

        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
                .thenReturn(appInfo);

        FieldSetter.setField(
                appInfo,
                ApplicationInfo.class.getDeclaredField("createTimestamp"),
                //create timestamp is out of window of (after) the interact events
                convertToTestAdjustTimestamp(USAGE_EVENT_TIMESTAMP_2 + 1));

        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
        assertEquals(USER_ID_1, UserHandle.getUserId(uid));

        PackageInstaller.SessionInfo installSession1 = mock(PackageInstaller.SessionInfo.class);
        PackageInstaller.SessionInfo installSession2 = mock(PackageInstaller.SessionInfo.class);
        doReturn(convertToTestAdjustTimestamp(0L)).when(installSession1).getCreatedMillis();
        doReturn(convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1)).when(installSession2)
                .getCreatedMillis();
        doReturn(PACKAGE_NAME_1).when(installSession1).getAppPackageName();
        doReturn(PACKAGE_NAME_1).when(installSession2).getAppPackageName();
        createPackageManagerHistoricalSessions(List.of(installSession1, installSession2),
                USER_ID_1);

        // The following 2 generated usage events occur after historical session create times hence,
        // considered foreground install. The appInfo createTimestamp occurs after events, so the
        // app would be considered background install if it falls back to it as reference create
        // timestamp.
        doReturn(PERMISSION_GRANTED)
                .when(mPermissionManager)
                .checkPermission(anyString(), anyString(), anyString(), anyInt());
        generateUsageEvent(
                UsageEvents.Event.ACTIVITY_RESUMED,
                USER_ID_1,
                INSTALLER_NAME_1,
                USAGE_EVENT_TIMESTAMP_1);
        generateUsageEvent(
                Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2);

        mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid);
        mTestLooper.dispatchAll();

        assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
    }

    private void createPackageManagerHistoricalSessions(
            List<PackageInstaller.SessionInfo> sessions, int userId) {
        ParceledListSlice<PackageInstaller.SessionInfo> mockParcelList =
                mock(ParceledListSlice.class);
        when(mockParcelList.getList()).thenReturn(sessions);
        when(mPackageManagerInternal.getHistoricalSessions(userId)).thenReturn(mockParcelList);
    }

    @Test
    @Test
    public void testHandleUsageEvent_packageAddedOutsideTimeFrame1()
    public void testHandleUsageEvent_packageAddedOutsideTimeFrame1()
            throws NoSuchFieldException, PackageManager.NameNotFoundException {
            throws NoSuchFieldException, PackageManager.NameNotFoundException {
@@ -655,12 +771,10 @@ public final class BackgroundInstallControlServiceTest {
        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
                .thenReturn(appInfo);
                .thenReturn(appInfo);


        long createTimestamp =
                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
        FieldSetter.setField(
        FieldSetter.setField(
                appInfo,
                appInfo,
                ApplicationInfo.class.getDeclaredField("createTimestamp"),
                ApplicationInfo.class.getDeclaredField("createTimestamp"),
                createTimestamp);
                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));


        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
        assertEquals(USER_ID_1, UserHandle.getUserId(uid));
        assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -708,12 +822,10 @@ public final class BackgroundInstallControlServiceTest {
        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
                .thenReturn(appInfo);
                .thenReturn(appInfo);


        long createTimestamp =
                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
        FieldSetter.setField(
        FieldSetter.setField(
                appInfo,
                appInfo,
                ApplicationInfo.class.getDeclaredField("createTimestamp"),
                ApplicationInfo.class.getDeclaredField("createTimestamp"),
                createTimestamp);
                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));


        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
        assertEquals(USER_ID_1, UserHandle.getUserId(uid));
        assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -765,12 +877,10 @@ public final class BackgroundInstallControlServiceTest {
        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
                .thenReturn(appInfo);
                .thenReturn(appInfo);


        long createTimestamp =
                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
        FieldSetter.setField(
        FieldSetter.setField(
                appInfo,
                appInfo,
                ApplicationInfo.class.getDeclaredField("createTimestamp"),
                ApplicationInfo.class.getDeclaredField("createTimestamp"),
                createTimestamp);
                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));


        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
        assertEquals(USER_ID_1, UserHandle.getUserId(uid));
        assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -818,12 +928,10 @@ public final class BackgroundInstallControlServiceTest {
        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
                .thenReturn(appInfo);
                .thenReturn(appInfo);


        long createTimestamp =
                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
        FieldSetter.setField(
        FieldSetter.setField(
                appInfo,
                appInfo,
                ApplicationInfo.class.getDeclaredField("createTimestamp"),
                ApplicationInfo.class.getDeclaredField("createTimestamp"),
                createTimestamp);
                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));


        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
        assertEquals(USER_ID_1, UserHandle.getUserId(uid));
        assertEquals(USER_ID_1, UserHandle.getUserId(uid));