Skip to content
Snippets Groups Projects
Commit 5eb60ccf authored by Yanting Yang's avatar Yanting Yang Committed by Android (Google) Code Review
Browse files

Merge "Hide hidden apk-in-Apex of mainline modules from the app list" into main

parents 404ed228 6c552d6e
No related branches found
No related tags found
No related merge requests found
......@@ -17,10 +17,11 @@
package com.android.settingslib.spaprivileged.model.app
import android.content.Context
import android.content.pm.FeatureFlags
import android.content.pm.FeatureFlagsImpl
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.FeatureFlags
import android.content.pm.FeatureFlagsImpl
import android.content.pm.Flags
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ApplicationInfoFlags
import android.content.pm.ResolveInfo
......@@ -85,13 +86,7 @@ class AppListRepositoryImpl(
loadInstantApps: Boolean,
matchAnyUserForAdmin: Boolean,
): List<ApplicationInfo> = coroutineScope {
val hiddenSystemModulesDeferred = async {
packageManager.getInstalledModules(0)
.filter { it.isHidden }
.map { it.packageName }
.filterNotNull()
.toSet()
}
val hiddenSystemModulesDeferred = async { packageManager.getHiddenSystemModules() }
val hideWhenDisabledPackagesDeferred = async {
context.resources.getStringArray(R.array.config_hideWhenDisabled_packageNames)
}
......@@ -205,6 +200,15 @@ class AppListRepositoryImpl(
private fun isSystemApp(app: ApplicationInfo, homeOrLauncherPackages: Set<String>): Boolean =
app.isSystemApp && !app.isUpdatedSystemApp && app.packageName !in homeOrLauncherPackages
private fun PackageManager.getHiddenSystemModules(): Set<String> {
val moduleInfos = getInstalledModules(0).filter { it.isHidden }
val hiddenApps = moduleInfos.mapNotNull { it.packageName }.toMutableSet()
if (Flags.provideInfoOfApkInApex()) {
hiddenApps += moduleInfos.flatMap { it.apkInApexPackageNames }
}
return hiddenApps
}
companion object {
private fun ApplicationInfo.isInAppList(
showInstantApps: Boolean,
......
......@@ -35,5 +35,6 @@ android_test {
"androidx.test.ext.junit",
"androidx.test.runner",
"mockito-target-minus-junit4",
"flag-junit",
],
}
......@@ -21,6 +21,7 @@ import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.FakeFeatureFlagsImpl
import android.content.pm.Flags
import android.content.pm.ModuleInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ApplicationInfoFlags
import android.content.pm.PackageManager.ResolveInfoFlags
......@@ -28,6 +29,7 @@ import android.content.pm.ResolveInfo
import android.content.pm.UserInfo
import android.content.res.Resources
import android.os.UserManager
import android.platform.test.flag.junit.SetFlagsRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.internal.R
......@@ -35,6 +37,7 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
......@@ -50,6 +53,9 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class AppListRepositoryTest {
@get:Rule
val mSetFlagsRule = SetFlagsRule()
private val resources = mock<Resources> {
on { getStringArray(R.array.config_hideWhenDisabled_packageNames) } doReturn emptyArray()
}
......@@ -272,6 +278,38 @@ class AppListRepositoryTest {
)
}
@Test
fun loadApps_hasApkInApexInfo_shouldNotIncludeAllHiddenApps() = runTest {
mSetFlagsRule.enableFlags(Flags.FLAG_PROVIDE_INFO_OF_APK_IN_APEX)
packageManager.stub {
on { getInstalledModules(any()) } doReturn listOf(HIDDEN_MODULE)
}
mockInstalledApplications(
listOf(NORMAL_APP, HIDDEN_APEX_APP, HIDDEN_MODULE_APP),
ADMIN_USER_ID
)
val appList = repository.loadApps(userId = ADMIN_USER_ID)
assertThat(appList).containsExactly(NORMAL_APP)
}
@Test
fun loadApps_noApkInApexInfo_shouldNotIncludeHiddenSystemModule() = runTest {
mSetFlagsRule.disableFlags(Flags.FLAG_PROVIDE_INFO_OF_APK_IN_APEX)
packageManager.stub {
on { getInstalledModules(any()) } doReturn listOf(HIDDEN_MODULE)
}
mockInstalledApplications(
listOf(NORMAL_APP, HIDDEN_APEX_APP, HIDDEN_MODULE_APP),
ADMIN_USER_ID
)
val appList = repository.loadApps(userId = ADMIN_USER_ID)
assertThat(appList).containsExactly(NORMAL_APP, HIDDEN_APEX_APP)
}
@Test
fun showSystemPredicate_showSystem() = runTest {
val app = SYSTEM_APP
......@@ -402,6 +440,20 @@ class AppListRepositoryTest {
isArchived = true
}
val HIDDEN_APEX_APP = ApplicationInfo().apply {
packageName = "hidden.apex.package"
}
val HIDDEN_MODULE_APP = ApplicationInfo().apply {
packageName = "hidden.module.package"
}
val HIDDEN_MODULE = ModuleInfo().apply {
packageName = "hidden.module.package"
apkInApexPackageNames = listOf("hidden.apex.package")
isHidden = true
}
fun resolveInfoOf(packageName: String) = ResolveInfo().apply {
activityInfo = ActivityInfo().apply {
this.packageName = packageName
......
......@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.Flags;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
......@@ -164,12 +165,16 @@ public class AppUtils {
try {
final PackageInfo pkg = pm.getPackageInfo(packageName, 0 /* flags */);
// Check if the package is contained in an APEX. There is no public API to properly
// check whether a given APK package comes from an APEX registered as module.
// Therefore we conservatively assume that any package scanned from an /apex path is
// a system package.
return pkg.applicationInfo.sourceDir.startsWith(
Environment.getApexDirectory().getAbsolutePath());
if (Flags.provideInfoOfApkInApex()) {
return pkg.getApexPackageName() != null;
} else {
// Check if the package is contained in an APEX. There is no public API to properly
// check whether a given APK package comes from an APEX registered as module.
// Therefore we conservatively assume that any package scanned from an /apex path is
// a system package.
return pkg.applicationInfo.sourceDir.startsWith(
Environment.getApexDirectory().getAbsolutePath());
}
} catch (PackageManager.NameNotFoundException e) {
return false;
}
......
......@@ -27,6 +27,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.Flags;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.ModuleInfo;
......@@ -226,6 +227,11 @@ public class ApplicationsState {
final List<ModuleInfo> moduleInfos = mPm.getInstalledModules(0 /* flags */);
for (ModuleInfo info : moduleInfos) {
mSystemModules.put(info.getPackageName(), info.isHidden());
if (Flags.provideInfoOfApkInApex()) {
for (String apkInApexPackageName : info.getApkInApexPackageNames()) {
mSystemModules.put(apkInApexPackageName, info.isHidden());
}
}
}
/**
......
......@@ -16,6 +16,8 @@
package com.android.settingslib.applications;
import static android.content.pm.Flags.FLAG_PROVIDE_INFO_OF_APK_IN_APEX;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
......@@ -24,12 +26,16 @@ import static org.mockito.Mockito.mock;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.platform.test.flag.junit.SetFlagsRule;
import com.android.settingslib.Utils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
......@@ -39,6 +45,8 @@ import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowPackageManager;
import java.io.File;
import java.lang.reflect.Field;
......@@ -60,6 +68,10 @@ public class AppUtilsTest {
private ApplicationInfo mAppInfo;
private ApplicationsState.AppEntry mAppEntry;
private ArrayList<ApplicationsState.AppEntry> mAppEntries;
private ShadowPackageManager mShadowPackageManager;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void setUp() {
......@@ -70,6 +82,7 @@ public class AppUtilsTest {
mAppEntry = createAppEntry(mAppInfo, /* id= */ 1);
mAppEntries = new ArrayList<>(Arrays.asList(mAppEntry));
doReturn(mIcon).when(mIcon).mutate();
mShadowPackageManager = Shadow.extract(mContext.getPackageManager());
}
@After
......@@ -151,6 +164,32 @@ public class AppUtilsTest {
assertThat(AppUtils.isAppInstalled(appEntry)).isFalse();
}
@Test
public void isMainlineModule_hasApexPackageName_shouldCheckByPackageInfo() {
mSetFlagsRule.enableFlags(FLAG_PROVIDE_INFO_OF_APK_IN_APEX);
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = APP_PACKAGE_NAME;
packageInfo.setApexPackageName("com.test.apex.package");
mShadowPackageManager.installPackage(packageInfo);
assertThat(
AppUtils.isMainlineModule(mContext.getPackageManager(), APP_PACKAGE_NAME)).isTrue();
}
@Test
public void isMainlineModule_noApexPackageName_shouldCheckBySourceDirPath() {
mSetFlagsRule.disableFlags(FLAG_PROVIDE_INFO_OF_APK_IN_APEX);
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.sourceDir = Environment.getApexDirectory().getAbsolutePath();
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = APP_PACKAGE_NAME;
packageInfo.applicationInfo = applicationInfo;
mShadowPackageManager.installPackage(packageInfo);
assertThat(
AppUtils.isMainlineModule(mContext.getPackageManager(), APP_PACKAGE_NAME)).isTrue();
}
private ApplicationsState.AppEntry createAppEntry(ApplicationInfo appInfo, int id) {
ApplicationsState.AppEntry appEntry = new ApplicationsState.AppEntry(mContext, appInfo, id);
appEntry.label = "label";
......
......@@ -16,6 +16,7 @@
package com.android.settingslib.applications;
import static android.content.pm.Flags.FLAG_PROVIDE_INFO_OF_APK_IN_APEX;
import static android.os.UserHandle.MU_ENABLED;
import static android.os.UserHandle.USER_SYSTEM;
......@@ -58,6 +59,7 @@ import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.flag.junit.SetFlagsRule;
import android.text.TextUtils;
import android.util.IconDrawableFactory;
......@@ -70,6 +72,7 @@ import com.android.settingslib.testutils.shadow.ShadowUserManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
......@@ -89,6 +92,7 @@ import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
......@@ -137,6 +141,9 @@ public class ApplicationsStateRoboTest {
@Mock
private IPackageManager mPackageManagerService;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Implements(value = IconDrawableFactory.class)
public static class ShadowIconDrawableFactory {
......@@ -169,7 +176,9 @@ public class ApplicationsStateRoboTest {
public List<ModuleInfo> getInstalledModules(int flags) {
if (mInstalledModules.isEmpty()) {
for (String moduleName : mModuleNames) {
mInstalledModules.add(createModuleInfo(moduleName));
mInstalledModules.add(
createModuleInfo(moduleName,
TextUtils.concat(moduleName, ".apex").toString()));
}
}
return mInstalledModules;
......@@ -188,10 +197,11 @@ public class ApplicationsStateRoboTest {
return resolveInfos;
}
private ModuleInfo createModuleInfo(String packageName) {
private ModuleInfo createModuleInfo(String packageName, String apexPackageName) {
final ModuleInfo info = new ModuleInfo();
info.setName(packageName);
info.setPackageName(packageName);
info.setApkInApexPackageNames(Collections.singletonList(apexPackageName));
// will treat any app with package name that contains "hidden" as hidden module
info.setHidden(!TextUtils.isEmpty(packageName) && packageName.contains("hidden"));
return info;
......@@ -822,4 +832,32 @@ public class ApplicationsStateRoboTest {
assertThat(mApplicationsState.getEntry(PKG_1, /* userId= */ 0).info.packageName)
.isEqualTo(PKG_1);
}
@Test
public void isHiddenModule_hasApkInApexInfo_shouldSupportHiddenApexPackage() {
mSetFlagsRule.enableFlags(FLAG_PROVIDE_INFO_OF_APK_IN_APEX);
ApplicationsState.sInstance = null;
mApplicationsState = ApplicationsState.getInstance(mApplication, mPackageManagerService);
String normalModulePackage = "test.module.1";
String hiddenModulePackage = "test.hidden.module.2";
String hiddenApexPackage = "test.hidden.module.2.apex";
assertThat(mApplicationsState.isHiddenModule(normalModulePackage)).isFalse();
assertThat(mApplicationsState.isHiddenModule(hiddenModulePackage)).isTrue();
assertThat(mApplicationsState.isHiddenModule(hiddenApexPackage)).isTrue();
}
@Test
public void isHiddenModule_noApkInApexInfo_onlySupportHiddenModule() {
mSetFlagsRule.disableFlags(FLAG_PROVIDE_INFO_OF_APK_IN_APEX);
ApplicationsState.sInstance = null;
mApplicationsState = ApplicationsState.getInstance(mApplication, mPackageManagerService);
String normalModulePackage = "test.module.1";
String hiddenModulePackage = "test.hidden.module.2";
String hiddenApexPackage = "test.hidden.module.2.apex";
assertThat(mApplicationsState.isHiddenModule(normalModulePackage)).isFalse();
assertThat(mApplicationsState.isHiddenModule(hiddenModulePackage)).isTrue();
assertThat(mApplicationsState.isHiddenModule(hiddenApexPackage)).isFalse();
}
}
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