Improve installer attribution in App Info.

When an app is installed by the Package Installer app on behalf of
another app (eg. a browser, file manager or app store that opens an APK
via an activity start), it is preferable to attribute the install to the
originating app rather than the 'Package Installer' itself.

Since Android R, package manager keeps track of the necessary install
source information which enables this more precise attribution. If an
originating package is recorded and was set by a system app, we use this
as the user-visible 'installer'.

Bug: 182365285
Test: make RunSettingsRoboTests
Change-Id: Ibb329d6fe8f0fa2ad51d3530a219b2d8b8d6c17b
This commit is contained in:
Edward Cunningham
2021-03-14 13:47:00 +00:00
parent e98a5a9932
commit faf44124ba
4 changed files with 144 additions and 32 deletions

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class AppStoreUtilTest {
private static final String PACKAGE_NAME = "com.android.app";
private static final String INSTALLING_PACKAGE_NAME = "com.android.installing";
private static final String INITIATING_PACKAGE_NAME = "com.android.initiating";
private static final String ORIGINATING_PACKAGE_NAME = "com.android.originating";
@Mock
private PackageManager mPackageManager;
@Mock
private ApplicationInfo mInitiatingAppInfo;
@Mock
private InstallSourceInfo mInstallSourceInfo;
private Context mContext;
@Before
public void setUp() throws PackageManager.NameNotFoundException {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo);
when(mInstallSourceInfo.getInstallingPackageName()).thenReturn(INSTALLING_PACKAGE_NAME);
when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(INITIATING_PACKAGE_NAME);
when(mInstallSourceInfo.getOriginatingPackageName()).thenReturn(ORIGINATING_PACKAGE_NAME);
when(mPackageManager.getApplicationInfo(eq(INITIATING_PACKAGE_NAME), anyInt()))
.thenReturn(mInitiatingAppInfo);
}
@Test
public void getInstallerPackageName_hasOriginatingByNonSystem_shouldReturnInstalling() {
assertThat(AppStoreUtil.getInstallerPackageName(mContext, PACKAGE_NAME))
.isEqualTo(INSTALLING_PACKAGE_NAME);
}
@Test
public void getInstallerPackageName_hasOriginatingBySystem_shouldReturnOriginating() {
mInitiatingAppInfo.flags = ApplicationInfo.FLAG_SYSTEM;
assertThat(AppStoreUtil.getInstallerPackageName(mContext, PACKAGE_NAME))
.isEqualTo(ORIGINATING_PACKAGE_NAME);
}
@Test
public void getInstallerPackageName_noInitiating_shouldReturnInstalling() {
when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(null);
assertThat(AppStoreUtil.getInstallerPackageName(mContext, PACKAGE_NAME))
.isEqualTo(INSTALLING_PACKAGE_NAME);
}
@Test
public void getInstallerPackageName_noOriginating_shouldReturnInstalling() {
when(mInstallSourceInfo.getOriginatingPackageName()).thenReturn(null);
assertThat(AppStoreUtil.getInstallerPackageName(mContext, PACKAGE_NAME))
.isEqualTo(INSTALLING_PACKAGE_NAME);
}
}

View File

@@ -32,6 +32,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -60,6 +61,8 @@ public class AppInstallerInfoPreferenceControllerTest {
@Mock
private ApplicationInfo mAppInfo;
@Mock
private InstallSourceInfo mInstallSourceInfo;
@Mock
private AppInfoDashboardFragment mFragment;
@Mock
private Preference mPreference;
@@ -74,7 +77,8 @@ public class AppInstallerInfoPreferenceControllerTest {
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
final String installerPackage = "Installer1";
when(mPackageManager.getInstallerPackageName(anyString())).thenReturn(installerPackage);
when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo);
when(mInstallSourceInfo.getInstallingPackageName()).thenReturn(installerPackage);
when(mPackageManager.getApplicationInfo(eq(installerPackage), anyInt()))
.thenReturn(mAppInfo);
mController = new AppInstallerInfoPreferenceController(mContext, "test_key");

View File

@@ -33,6 +33,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -87,7 +88,7 @@ public class InstantAppButtonsPreferenceControllerTest {
private InstantAppButtonsPreferenceController mController;
@Before
public void setUp() {
public void setUp() throws PackageManager.NameNotFoundException {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
@@ -107,6 +108,10 @@ public class InstantAppButtonsPreferenceControllerTest {
when(mPreference.getKey()).thenReturn("instant_app_buttons");
mScreen.addPreference(mPreference);
when(mPreference.findViewById(R.id.instant_app_button_container)).thenReturn(buttons);
final InstallSourceInfo installSourceInfo = mock(InstallSourceInfo.class);
when(mPackageManager.getInstallSourceInfo(TEST_AIA_PACKAGE_NAME))
.thenReturn(installSourceInfo);
when(installSourceInfo.getInstallingPackageName()).thenReturn(TEST_INSTALLER_PACKAGE_NAME);
}
@Test
@@ -165,12 +170,7 @@ public class InstantAppButtonsPreferenceControllerTest {
@Test
public void onPrepareOptionsMenu_hasAppStoreLink_shoulNotDisableInstallInstantAppMenu() {
ReflectionHelpers.setField(mController, "mLaunchUri", "www.test.launch");
final ResolveInfo resolveInfo = mock(ResolveInfo.class);
final ActivityInfo activityInfo = mock(ActivityInfo.class);
resolveInfo.activityInfo = activityInfo;
activityInfo.packageName = TEST_INSTALLER_PACKAGE_NAME;
activityInfo.name = TEST_INSTALLER_ACTIVITY_NAME;
when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo);
initAppStoreInfo();
final Menu menu = mock(Menu.class);
final MenuItem menuItem = mock(MenuItem.class);
when(menu.findItem(AppInfoDashboardFragment.INSTALL_INSTANT_APP_MENU)).thenReturn(menuItem);
@@ -191,12 +191,7 @@ public class InstantAppButtonsPreferenceControllerTest {
@Test
public void onOptionsItemSelected_shouldOpenAppStore() {
final ResolveInfo resolveInfo = mock(ResolveInfo.class);
final ActivityInfo activityInfo = mock(ActivityInfo.class);
resolveInfo.activityInfo = activityInfo;
activityInfo.packageName = TEST_INSTALLER_PACKAGE_NAME;
activityInfo.name = TEST_INSTALLER_ACTIVITY_NAME;
when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo);
initAppStoreInfo();
mController.displayPreference(mScreen);
final ComponentName componentName =
new ComponentName(TEST_INSTALLER_PACKAGE_NAME, TEST_INSTALLER_ACTIVITY_NAME);
@@ -235,12 +230,7 @@ public class InstantAppButtonsPreferenceControllerTest {
@Test
public void displayPreference_hasAppStoreLink_shoulSetClickListenerForInstallButton() {
final ResolveInfo resolveInfo = mock(ResolveInfo.class);
final ActivityInfo activityInfo = mock(ActivityInfo.class);
resolveInfo.activityInfo = activityInfo;
activityInfo.packageName = TEST_INSTALLER_PACKAGE_NAME;
activityInfo.name = TEST_INSTALLER_ACTIVITY_NAME;
when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo);
initAppStoreInfo();
mController.displayPreference(mScreen);
@@ -272,12 +262,7 @@ public class InstantAppButtonsPreferenceControllerTest {
@Test
public void clickInstallButton_shouldOpenAppStore() {
final ResolveInfo resolveInfo = mock(ResolveInfo.class);
final ActivityInfo activityInfo = mock(ActivityInfo.class);
resolveInfo.activityInfo = activityInfo;
activityInfo.packageName = TEST_INSTALLER_PACKAGE_NAME;
activityInfo.name = TEST_INSTALLER_ACTIVITY_NAME;
when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo);
initAppStoreInfo();
mController.displayPreference(mScreen);
final ComponentName componentName =
new ComponentName(TEST_INSTALLER_PACKAGE_NAME, TEST_INSTALLER_ACTIVITY_NAME);
@@ -302,4 +287,13 @@ public class InstantAppButtonsPreferenceControllerTest {
verify(fragmentTransaction).add(any(InstantAppButtonDialogFragment.class),
eq("instant_app_buttons"));
}
private void initAppStoreInfo() {
final ResolveInfo resolveInfo = mock(ResolveInfo.class);
final ActivityInfo activityInfo = mock(ActivityInfo.class);
resolveInfo.activityInfo = activityInfo;
activityInfo.packageName = TEST_INSTALLER_PACKAGE_NAME;
activityInfo.name = TEST_INSTALLER_ACTIVITY_NAME;
when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo);
}
}