summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Yanting Yang <yantingyang@google.com> 2024-01-29 14:13:28 +0000
committer Yanting Yang <yantingyang@google.com> 2024-02-01 12:07:52 +0000
commit6c552d6e5233c7db4745d747eb41e3a8235c908f (patch)
tree30cb31541793c589e4337fe35985b8333190ca86
parentf23d375e4f15262bd951cd059a9461310ba97ba9 (diff)
Hide hidden apk-in-Apex of mainline modules from the app list
- Update the legacy app list behavior - Update the SPA app list behavior - Refactor isMainlineModule() with new support interface Bug: 273913035 Test: manual & atest Change-Id: I880f7ce92cf88325f8bd30e2fb2e29e1033da42c
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt22
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/Android.bp1
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt52
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java17
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java6
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppUtilsTest.java39
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java42
7 files changed, 162 insertions, 17 deletions
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
index 0a98791e8a6a..988afd70aaed 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
@@ -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,
diff --git a/packages/SettingsLib/SpaPrivileged/tests/Android.bp b/packages/SettingsLib/SpaPrivileged/tests/Android.bp
index a28ebc6923bf..458fcc97aa9b 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/Android.bp
+++ b/packages/SettingsLib/SpaPrivileged/tests/Android.bp
@@ -35,5 +35,6 @@ android_test {
"androidx.test.ext.junit",
"androidx.test.runner",
"mockito-target-minus-junit4",
+ "flag-junit",
],
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
index f2922316dbde..efd53a4c9c23 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
@@ -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()
}
@@ -273,6 +279,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
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index 07de7fd6438e..c4829951d61a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -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;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 8e1067f28ffb..e3012cd3e6fd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -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());
+ }
+ }
}
/**
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppUtilsTest.java
index 994c1ee5013f..e32d88069238 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppUtilsTest.java
@@ -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";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index 34d8148f418f..827d8fa9e7c1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -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();
+ }
}