Skip to content
Snippets Groups Projects
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
No related branches found
No related tags found
No related merge requests found
......@@ -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
......
......@@ -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));
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment