diff options
| -rw-r--r-- | services/core/java/com/android/server/pm/BackgroundInstallControlService.java | 72 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java | 154 |
2 files changed, 186 insertions, 40 deletions
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java index 524bad58ce07..b6daed121057 100644 --- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java +++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java @@ -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 diff --git a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java index 1ae6e63c3ff1..0d826dfd2467 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java @@ -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; @@ -115,9 +117,6 @@ public final class BackgroundInstallControlServiceTest { private BackgroundInstallControlCallbackHelper mCallbackHelper; @Captor - private ArgumentCaptor<PackageManagerInternal.PackageListObserver> mPackageListObserverCaptor; - - @Captor private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor; @Before @@ -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)); @@ -639,6 +639,122 @@ public final class BackgroundInstallControlServiceTest { } @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 { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); @@ -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)); |