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

Commit 36c28755 authored by George Chan's avatar George Chan Committed by Android (Google) Code Review
Browse files

Merge "Updated BackgroundInstallControlService to use historical sessions to...

Merge "Updated BackgroundInstallControlService to use historical sessions to retrieve install start time." into main
parents 999de02f abd727d7
Loading
Loading
Loading
Loading
+55 −17
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IBackgroundInstallControlService;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
@@ -46,6 +47,7 @@ import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArrayMap;
import android.util.SparseSetArray;
@@ -63,8 +65,10 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.ListIterator;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;

@@ -103,6 +107,24 @@ public class BackgroundInstallControlService extends SystemService {
    private final SparseArrayMap<String, TreeSet<ForegroundTimeFrame>>
            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) {
        this(new InjectorImpl(context));
    }
@@ -258,6 +280,7 @@ public class BackgroundInstallControlService extends SystemService {

        String installerPackageName;
        String initiatingPackageName;

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

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

        if (installedByAdb(initiatingPackageName)
                || wasForegroundInstallation(installerPackageName, userId, installTimestamp)) {
@@ -293,6 +317,35 @@ public class BackgroundInstallControlService extends SystemService {
        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
    // addressed with b/265203007
    private boolean installedByAdb(String initiatingPackageName) {
@@ -496,22 +549,7 @@ public class BackgroundInstallControlService extends SystemService {
            publishBinderService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE, mBinderService);
        }

        mPackageManagerInternal.getPackageList(
                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();
                    }
                });
        mPackageManagerInternal.getPackageList(mPackageObserver);
    }

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

    @Captor
    private ArgumentCaptor<PackageManagerInternal.PackageListObserver> mPackageListObserverCaptor;

    @Captor
    private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor;

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

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

        mPackageListObserver = mBackgroundInstallControlService.mPackageObserver;
    }

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

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

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

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

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

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

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

        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
        assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -638,6 +638,122 @@ public final class BackgroundInstallControlServiceTest {
        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
    public void testHandleUsageEvent_packageAddedOutsideTimeFrame1()
            throws NoSuchFieldException, PackageManager.NameNotFoundException {
@@ -655,12 +771,10 @@ public final class BackgroundInstallControlServiceTest {
        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
                .thenReturn(appInfo);

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

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

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

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

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

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

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

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