From 28d6bbd6b4c45c6196e2e3d9887771619c07e569 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 23 Sep 2024 01:05:13 -0700 Subject: [PATCH] Moving various application into related methods to a separate class > This avoids duplicate RPCs when trying to get multiple properties from AppInfo > This would be used in a followup cl when AppInfo is added to caching logic in IconCache Bug: 366237794 Test: atest ApplicationInfoWrapperTest Flag: EXEMPT refactor Change-Id: I55b964d4f8cfa1ff1770e310ac278719495e285d --- .../taskbar/TaskbarActivityContext.java | 5 +- .../src/com/android/quickstep/TaskUtils.java | 6 +- src/com/android/launcher3/LauncherModel.java | 5 +- .../launcher3/SecondaryDropTarget.java | 8 +- .../launcher3/dragndrop/AddItemActivity.java | 6 +- .../model/AddWorkspaceItemsTask.java | 5 +- .../android/launcher3/model/AllAppsList.java | 5 +- .../model/SdCardAvailableReceiver.java | 5 +- .../launcher3/model/ShortcutsChangedTask.java | 9 +- .../launcher3/model/WorkspaceItemProcessor.kt | 13 +- .../android/launcher3/model/data/AppInfo.java | 9 +- .../launcher3/pm/InstallSessionHelper.java | 10 +- .../launcher3/util/ApplicationInfoWrapper.kt | 115 +++++++++ .../launcher3/util/PackageManagerHelper.java | 125 --------- .../launcher3/views/ActivityContext.java | 4 +- .../WidgetRecommendationCategoryProvider.java | 18 +- .../model/WorkspaceItemProcessorTest.kt | 33 ++- .../util/ApplicationInfoWrapperTest.kt | 131 ++++++++++ .../util/PackageManagerHelperTest.java | 86 ------- ...getRecommendationCategoryProviderTest.java | 15 +- .../model/WorkspaceItemProcessorExtraTest.kt | 238 ++++++++---------- .../launcher3/pm/InstallSessionHelperTest.kt | 10 +- 22 files changed, 432 insertions(+), 429 deletions(-) create mode 100644 src/com/android/launcher3/util/ApplicationInfoWrapper.kt create mode 100644 tests/multivalentTests/src/com/android/launcher3/util/ApplicationInfoWrapperTest.kt delete mode 100644 tests/multivalentTests/src/com/android/launcher3/util/PackageManagerHelperTest.java diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 4f9310c6c4..13160cc5b9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -131,11 +131,11 @@ import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy; import com.android.launcher3.util.ActivityOptionsWrapper; import com.android.launcher3.util.ApiWrapper; +import com.android.launcher3.util.ApplicationInfoWrapper; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.Executors; import com.android.launcher3.util.NavigationMode; -import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.RunnableList; import com.android.launcher3.util.SettingsCache; import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource; @@ -1265,7 +1265,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { Intent intent = new Intent(info.getIntent()) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { - if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) { + if (mIsSafeModeEnabled + && !new ApplicationInfoWrapper(this, intent).isSystem()) { Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show(); } else if (info.isPromise()) { diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java index 63e536ae0a..49f7daf04a 100644 --- a/quickstep/src/com/android/quickstep/TaskUtils.java +++ b/quickstep/src/com/android/quickstep/TaskUtils.java @@ -31,8 +31,8 @@ import android.view.RemoteAnimationTarget; import androidx.annotation.Nullable; import com.android.launcher3.pm.UserCache; +import com.android.launcher3.util.ApplicationInfoWrapper; import com.android.launcher3.util.ComponentKey; -import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.TraceHelper; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -70,8 +70,8 @@ public final class TaskUtils { return ""; } UserHandle user = UserHandle.of(userId); - ApplicationInfo applicationInfo = PackageManagerHelper.INSTANCE.get(context) - .getApplicationInfo(packageName, user, 0); + ApplicationInfo applicationInfo = + new ApplicationInfoWrapper(context, packageName, user).getInfo(); if (applicationInfo == null) { Log.e(TAG, "Failed to get title for userId=" + userId + ", packageName=" + packageName); return ""; diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index ca1b2a91f7..7ad17d9f76 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -71,6 +71,7 @@ import com.android.launcher3.pm.InstallSessionTracker; import com.android.launcher3.pm.PackageInstallInfo; import com.android.launcher3.pm.UserCache; import com.android.launcher3.shortcuts.ShortcutRequest; +import com.android.launcher3.util.ApplicationInfoWrapper; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.PackageManagerHelper; @@ -446,8 +447,8 @@ public class LauncherModel implements InstallSessionTracker.Callback { IconCache iconCache = mApp.getIconCache(); final IntSet removedIds = new IntSet(); HashSet archivedWorkspaceItemsToCacheRefresh = new HashSet<>(); - boolean isAppArchived = PackageManagerHelper.INSTANCE.get(mApp.getContext()) - .isAppArchivedForUser(packageName, user); + boolean isAppArchived = + new ApplicationInfoWrapper(mApp.getContext(), packageName, user).isArchived(); synchronized (dataModel) { if (isAppArchived) { // Remove package icon cache entry for archived app in case of a session diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java index 8d1e61f9ba..b3cb948a3d 100644 --- a/src/com/android/launcher3/SecondaryDropTarget.java +++ b/src/com/android/launcher3/SecondaryDropTarget.java @@ -22,7 +22,6 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; -import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; @@ -44,7 +43,7 @@ import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.pm.UserCache; -import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.util.ApplicationInfoWrapper; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import java.net.URISyntaxException; @@ -342,9 +341,8 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList } public void onLauncherResume() { - // We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well. - if (PackageManagerHelper.INSTANCE.get(mContext).getApplicationInfo(mPackageName, - mDragObject.dragInfo.user, PackageManager.MATCH_UNINSTALLED_PACKAGES) == null) { + if (new ApplicationInfoWrapper(mContext, mPackageName, mDragObject.dragInfo.user) + .getInfo() == null) { mDragObject.dragSource = mOriginal; mOriginal.onDropCompleted(SecondaryDropTarget.this, mDragObject, true); mStatsLogManager.logger().withInstanceId(mDragObject.logInstanceId) diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java index 85eb39bab8..a3cfe5c3a0 100644 --- a/src/com/android/launcher3/dragndrop/AddItemActivity.java +++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java @@ -68,7 +68,7 @@ import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.pm.PinRequestHelper; import com.android.launcher3.util.ApiWrapper; -import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.util.ApplicationInfoWrapper; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.views.AbstractSlideInView; import com.android.launcher3.views.BaseDragLayer; @@ -164,8 +164,8 @@ public class AddItemActivity extends BaseActivity finish(); return; } - ApplicationInfo info = PackageManagerHelper.INSTANCE.get(this) - .getApplicationInfo(targetApp.packageName, targetApp.user, 0); + ApplicationInfo info = new ApplicationInfoWrapper( + this, targetApp.packageName, targetApp.user).getInfo(); if (info == null) { finish(); return; diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java index 427fb970be..55bcb7036a 100644 --- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java +++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java @@ -41,6 +41,7 @@ import com.android.launcher3.model.data.WorkspaceItemFactory; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.pm.PackageInstallInfo; +import com.android.launcher3.util.ApplicationInfoWrapper; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.PackageManagerHelper; @@ -103,8 +104,8 @@ public class AddWorkspaceItemsTask implements ModelUpdateTask { } // b/139663018 Short-circuit this logic if the icon is a system app - if (PackageManagerHelper.isSystemApp(context, - Objects.requireNonNull(item.getIntent()))) { + if (new ApplicationInfoWrapper(context, + Objects.requireNonNull(item.getIntent())).isSystem()) { continue; } diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java index 1f60f132df..7bc92733de 100644 --- a/src/com/android/launcher3/model/AllAppsList.java +++ b/src/com/android/launcher3/model/AllAppsList.java @@ -40,6 +40,7 @@ import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.pm.PackageInstallInfo; import com.android.launcher3.pm.UserCache; import com.android.launcher3.util.ApiWrapper; +import com.android.launcher3.util.ApplicationInfoWrapper; import com.android.launcher3.util.FlagOp; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.SafeCloseable; @@ -169,8 +170,8 @@ public class AllAppsList { public AppInfo addPromiseApp( Context context, PackageInstallInfo installInfo, boolean loadIcon) { // only if not yet installed - if (PackageManagerHelper.INSTANCE.get(context) - .isAppInstalled(installInfo.packageName, installInfo.user)) { + if (new ApplicationInfoWrapper(context, installInfo.packageName, installInfo.user) + .isInstalled()) { return null; } AppInfo promiseAppInfo = new AppInfo(installInfo); diff --git a/src/com/android/launcher3/model/SdCardAvailableReceiver.java b/src/com/android/launcher3/model/SdCardAvailableReceiver.java index 529331675b..9e3f0e1aa2 100644 --- a/src/com/android/launcher3/model/SdCardAvailableReceiver.java +++ b/src/com/android/launcher3/model/SdCardAvailableReceiver.java @@ -24,7 +24,7 @@ import android.os.UserHandle; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; -import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.util.ApplicationInfoWrapper; import com.android.launcher3.util.PackageUserKey; import java.util.ArrayList; @@ -52,7 +52,6 @@ public class SdCardAvailableReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { final LauncherApps launcherApps = context.getSystemService(LauncherApps.class); - final PackageManagerHelper pmHelper = PackageManagerHelper.INSTANCE.get(context); for (PackageUserKey puk : mPackages) { UserHandle user = puk.mUser; @@ -60,7 +59,7 @@ public class SdCardAvailableReceiver extends BroadcastReceiver { final ArrayList packagesUnavailable = new ArrayList<>(); if (!launcherApps.isPackageEnabled(puk.mPackageName, user)) { - if (pmHelper.isAppOnSdcard(puk.mPackageName, user)) { + if (new ApplicationInfoWrapper(context, puk.mPackageName, user).isOnSdCard()) { packagesUnavailable.add(puk.mPackageName); } else { packagesRemoved.add(puk.mPackageName); diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java index 1916d23b67..55c4d30656 100644 --- a/src/com/android/launcher3/model/ShortcutsChangedTask.java +++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java @@ -27,8 +27,8 @@ import com.android.launcher3.LauncherSettings; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.shortcuts.ShortcutRequest; +import com.android.launcher3.util.ApplicationInfoWrapper; import com.android.launcher3.util.ItemInfoMatcher; -import com.android.launcher3.util.PackageManagerHelper; import java.util.ArrayList; import java.util.HashSet; @@ -80,11 +80,10 @@ public class ShortcutsChangedTask implements ModelUpdateTask { if (!matchingWorkspaceItems.isEmpty()) { if (mShortcuts.isEmpty()) { - PackageManagerHelper packageManagerHelper = - PackageManagerHelper.INSTANCE.get(context); + ApplicationInfoWrapper infoWrapper = + new ApplicationInfoWrapper(context, mPackageName, mUser); // Verify that the app is indeed installed. - if (!packageManagerHelper.isAppInstalled(mPackageName, mUser) - && !packageManagerHelper.isAppArchivedForUser(mPackageName, mUser)) { + if (!infoWrapper.isInstalled() && !infoWrapper.isArchived()) { // App is not installed or archived, ignoring package events return; } diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt index 1f1e514ee3..18c7f955eb 100644 --- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt +++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt @@ -44,6 +44,7 @@ import com.android.launcher3.pm.PackageInstallInfo import com.android.launcher3.pm.UserCache import com.android.launcher3.shortcuts.ShortcutKey import com.android.launcher3.util.ApiWrapper +import com.android.launcher3.util.ApplicationInfoWrapper import com.android.launcher3.util.ComponentKey import com.android.launcher3.util.PackageManagerHelper import com.android.launcher3.util.PackageUserKey @@ -152,6 +153,7 @@ class WorkspaceItemProcessor( c.markDeleted("No target package for item id=${c.id}", RestoreError.MISSING_INFO) return } + val appInfoWrapper = ApplicationInfoWrapper(app.context, targetPkg, c.user) var validTarget = launcherApps.isPackageEnabled(targetPkg, c.user) // If it's a deep shortcut, we'll use pinned shortcuts to restore it @@ -218,7 +220,7 @@ class WorkspaceItemProcessor( } } } - pmHelper.isAppOnSdcard(targetPkg, c.user) -> { + appInfoWrapper.isOnSdCard() -> { // Package is present but not available. disabledState = disabledState or WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE @@ -277,7 +279,7 @@ class WorkspaceItemProcessor( // If the pinned deep shortcut is no longer published, // use the last saved icon instead of the default. iconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon) - if (pmHelper.isAppSuspended(pinnedShortcut.getPackage(), info.user)) { + if (appInfoWrapper.isSuspended()) { info.runtimeStatusFlags = info.runtimeStatusFlags or ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED } @@ -294,7 +296,7 @@ class WorkspaceItemProcessor( info = c.loadSimpleWorkspaceItem() // Shortcuts are only available on the primary profile - if (!TextUtils.isEmpty(targetPkg) && pmHelper.isAppSuspended(targetPkg, c.user)) { + if (appInfoWrapper.isSuspended()) { disabledState = disabledState or ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED } info.options = c.options @@ -325,7 +327,7 @@ class WorkspaceItemProcessor( info.spanX = 1 info.spanY = 1 info.runtimeStatusFlags = info.runtimeStatusFlags or disabledState - if (isSafeMode && !PackageManagerHelper.isSystemApp(app.context, intent)) { + if (isSafeMode && !appInfoWrapper.isSystem()) { info.runtimeStatusFlags = info.runtimeStatusFlags or ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE } @@ -486,7 +488,8 @@ class WorkspaceItemProcessor( (si == null) && (lapi == null) && !(Flags.enableSupportForArchiving() && - pmHelper.isAppArchived(component.packageName)) + ApplicationInfoWrapper(app.context, component.packageName, c.user) + .isArchived()) ) { // Restore never started c.markDeleted( diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java index a4281f8677..97b62b43c3 100644 --- a/src/com/android/launcher3/model/data/AppInfo.java +++ b/src/com/android/launcher3/model/data/AppInfo.java @@ -21,7 +21,6 @@ import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APP import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.ApplicationInfo; import android.content.pm.LauncherActivityInfo; import android.os.UserHandle; import android.os.UserManager; @@ -36,6 +35,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.pm.PackageInstallInfo; import com.android.launcher3.pm.UserCache; import com.android.launcher3.util.ApiWrapper; +import com.android.launcher3.util.ApplicationInfoWrapper; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.UserIconInfo; @@ -187,8 +187,8 @@ public class AppInfo extends ItemInfoWithIcon implements WorkspaceItemFactory { ApiWrapper apiWrapper, PackageManagerHelper pmHelper) { final int oldProgressLevel = info.getProgressLevel(); final int oldRuntimeStatusFlags = info.runtimeStatusFlags; - ApplicationInfo appInfo = lai.getApplicationInfo(); - if (PackageManagerHelper.isAppSuspended(appInfo)) { + ApplicationInfoWrapper appInfo = new ApplicationInfoWrapper(lai.getApplicationInfo()); + if (appInfo.isSuspended()) { info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED; } else { info.runtimeStatusFlags &= ~FLAG_DISABLED_SUSPENDED; @@ -200,8 +200,7 @@ public class AppInfo extends ItemInfoWithIcon implements WorkspaceItemFactory { info.runtimeStatusFlags &= ~FLAG_ARCHIVED; } } - info.runtimeStatusFlags |= (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 - ? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES; + info.runtimeStatusFlags |= appInfo.isSystem() ? FLAG_SYSTEM_YES : FLAG_SYSTEM_NO; if (Flags.privateSpaceRestrictAccessibilityDrag()) { if (userIconInfo.isPrivate()) { diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java index e66f496b9d..124907f181 100644 --- a/src/com/android/launcher3/pm/InstallSessionHelper.java +++ b/src/com/android/launcher3/pm/InstallSessionHelper.java @@ -17,7 +17,6 @@ package com.android.launcher3.pm; import android.content.Context; -import android.content.pm.ApplicationInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; @@ -34,10 +33,10 @@ import com.android.launcher3.LauncherPrefs; import com.android.launcher3.SessionCommitReceiver; import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.ItemInstallQueue; +import com.android.launcher3.util.ApplicationInfoWrapper; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.MainThreadInitializedObject; -import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.SafeCloseable; @@ -171,8 +170,7 @@ public class InstallSessionHelper implements SafeCloseable { synchronized (mSessionVerifiedMap) { if (!mSessionVerifiedMap.containsKey(pkg)) { boolean hasSystemFlag = DEBUG || mAppContext.getPackageName().equals(pkg) - || PackageManagerHelper.INSTANCE.get(mAppContext) - .getApplicationInfo(pkg, user, ApplicationInfo.FLAG_SYSTEM) != null; + || new ApplicationInfoWrapper(mAppContext, pkg, user).isSystem(); mSessionVerifiedMap.put(pkg, hasSystemFlag); } } @@ -245,8 +243,8 @@ public class InstallSessionHelper implements SafeCloseable { && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER && sessionInfo.getAppIcon() != null && !TextUtils.isEmpty(sessionInfo.getAppLabel()) - && !PackageManagerHelper.INSTANCE.get(mAppContext).isAppInstalled( - sessionInfo.getAppPackageName(), getUserHandle(sessionInfo)); + && !new ApplicationInfoWrapper(mAppContext, sessionInfo.getAppPackageName(), + getUserHandle(sessionInfo)).isInstalled(); } public InstallSessionTracker registerInstallTracker( diff --git a/src/com/android/launcher3/util/ApplicationInfoWrapper.kt b/src/com/android/launcher3/util/ApplicationInfoWrapper.kt new file mode 100644 index 0000000000..e75b3bce42 --- /dev/null +++ b/src/com/android/launcher3/util/ApplicationInfoWrapper.kt @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2024 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.launcher3.util + +import android.content.Context +import android.content.Intent +import android.content.pm.ApplicationInfo +import android.content.pm.ApplicationInfo.FLAG_EXTERNAL_STORAGE +import android.content.pm.ApplicationInfo.FLAG_INSTALLED +import android.content.pm.ApplicationInfo.FLAG_SUSPENDED +import android.content.pm.ApplicationInfo.FLAG_SYSTEM +import android.content.pm.LauncherApps +import android.content.pm.PackageManager +import android.content.pm.PackageManager.NameNotFoundException +import android.os.UserHandle +import com.android.launcher3.Flags.enableSupportForArchiving +import com.android.launcher3.Utilities.ATLEAST_V +import kotlin.LazyThreadSafetyMode.NONE + +/** + * A set of utility methods around ApplicationInfo with support for fetching the actual info lazily + */ +class ApplicationInfoWrapper private constructor(provider: () -> ApplicationInfo?) { + + constructor(appInfo: ApplicationInfo?) : this({ appInfo }) + + constructor( + ctx: Context, + pkg: String, + user: UserHandle, + ) : this({ + try { + ctx.getSystemService(LauncherApps::class.java) + ?.getApplicationInfo(pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES, user) + ?.let { ai -> + // its enabled and (either installed or archived) + if ( + ai.enabled && + (ai.flags.and(FLAG_INSTALLED) != 0 || + (ATLEAST_V && enableSupportForArchiving() && ai.isArchived)) + ) { + ai + } else { + null + } + } + } catch (e: NameNotFoundException) { + null + } + }) + + constructor( + ctx: Context, + intent: Intent, + ) : this( + provider@{ + try { + val pm = ctx.packageManager + val packageName: String = + intent.component?.packageName + ?: intent.getPackage() + ?: return@provider pm.resolveActivity( + intent, + PackageManager.MATCH_DEFAULT_ONLY, + ) + ?.activityInfo + ?.applicationInfo + pm.getApplicationInfo(packageName, 0) + } catch (e: NameNotFoundException) { + null + } + } + ) + + private val appInfo: ApplicationInfo? by lazy(NONE, provider) + + private fun hasFlag(flag: Int) = appInfo?.let { it.flags.and(flag) != 0 } ?: false + + /** + * Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't + * guarantee that the app is on SD card. + */ + fun isOnSdCard() = hasFlag(FLAG_EXTERNAL_STORAGE) + + /** Returns whether the target app is installed for a given user */ + fun isInstalled() = hasFlag(FLAG_INSTALLED) + + /** + * Returns whether the target app is suspended for a given user as per + * [android.app.admin.DevicePolicyManager.isPackageSuspended]. + */ + fun isSuspended() = hasFlag(FLAG_INSTALLED) && hasFlag(FLAG_SUSPENDED) + + /** Returns whether the target app is archived for a given user */ + fun isArchived() = ATLEAST_V && enableSupportForArchiving() && appInfo?.isArchived ?: false + + /** Returns whether the target app is a system app */ + fun isSystem() = hasFlag(FLAG_SYSTEM) + + fun getInfo(): ApplicationInfo? = appInfo +} diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java index b1913c0164..e51609ac6e 100644 --- a/src/com/android/launcher3/util/PackageManagerHelper.java +++ b/src/com/android/launcher3/util/PackageManagerHelper.java @@ -24,13 +24,10 @@ import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.ApplicationInfo; import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; import android.graphics.Rect; import android.os.Bundle; import android.os.Process; @@ -42,7 +39,6 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.launcher3.Flags; import com.android.launcher3.PendingAddItemInfo; import com.android.launcher3.R; import com.android.launcher3.Utilities; @@ -88,69 +84,6 @@ public class PackageManagerHelper implements SafeCloseable{ @Override public void close() { } - /** - * Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't - * guarantee that the app is on SD card. - */ - public boolean isAppOnSdcard(@NonNull final String packageName, - @NonNull final UserHandle user) { - final ApplicationInfo info = getApplicationInfo( - packageName, user, PackageManager.MATCH_UNINSTALLED_PACKAGES); - return info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; - } - - /** - * Returns whether the target app is suspended for a given user as per - * {@link android.app.admin.DevicePolicyManager#isPackageSuspended}. - */ - public boolean isAppSuspended(@NonNull final String packageName, - @NonNull final UserHandle user) { - final ApplicationInfo info = getApplicationInfo(packageName, user, 0); - return info != null && isAppSuspended(info); - } - - /** - * Returns whether the target app is installed for a given user - */ - public boolean isAppInstalled(@NonNull final String packageName, - @NonNull final UserHandle user) { - final ApplicationInfo info = getApplicationInfo(packageName, user, 0); - return info != null; - } - - /** - * Returns whether the target app is archived for a given user - */ - @SuppressWarnings("NewApi") - public boolean isAppArchivedForUser(@NonNull final String packageName, - @NonNull final UserHandle user) { - if (!Flags.enableSupportForArchiving()) { - return false; - } - final ApplicationInfo info = getApplicationInfo( - // LauncherApps does not support long flags currently. Since archived apps are - // subset of uninstalled apps, this filter also includes archived apps. - packageName, user, PackageManager.MATCH_UNINSTALLED_PACKAGES); - return info != null && info.isArchived; - } - - /** - * Returns whether the target app is in archived state - */ - @SuppressWarnings("NewApi") - public boolean isAppArchived(@NonNull final String packageName) { - final ApplicationInfo info; - try { - info = mPm.getPackageInfo(packageName, - PackageManager.PackageInfoFlags.of( - PackageManager.MATCH_ARCHIVED_PACKAGES)).applicationInfo; - return info.isArchived; - } catch (NameNotFoundException e) { - Log.e(TAG, "Failed to get applicationInfo for package: " + packageName, e); - return false; - } - } - /** * Returns the installing app package for the given package */ @@ -163,20 +96,6 @@ public class PackageManagerHelper implements SafeCloseable{ } } - /** - * Returns the application info for the provided package or null - */ - @Nullable - public ApplicationInfo getApplicationInfo(@NonNull final String packageName, - @NonNull final UserHandle user, final int flags) { - try { - ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user); - return !isPackageInstalledOrArchived(info) || !info.enabled ? null : info; - } catch (PackageManager.NameNotFoundException e) { - return null; - } - } - /** * Returns the preferred launch activity intent for a given package. */ @@ -196,14 +115,6 @@ public class PackageManagerHelper implements SafeCloseable{ return activities.isEmpty() ? null : activities.get(0); } - /** - * Returns whether an application is suspended as per - * {@link android.app.admin.DevicePolicyManager#isPackageSuspended}. - */ - public static boolean isAppSuspended(ApplicationInfo info) { - return (info.flags & ApplicationInfo.FLAG_SUSPENDED) != 0; - } - /** * Starts the details activity for {@code info} */ @@ -236,35 +147,6 @@ public class PackageManagerHelper implements SafeCloseable{ } } - public static boolean isSystemApp(@NonNull final Context context, - @NonNull final Intent intent) { - PackageManager pm = context.getPackageManager(); - ComponentName cn = intent.getComponent(); - String packageName = null; - if (cn == null) { - ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); - if ((info != null) && (info.activityInfo != null)) { - packageName = info.activityInfo.packageName; - } - } else { - packageName = cn.getPackageName(); - } - if (packageName == null) { - packageName = intent.getPackage(); - } - if (packageName != null) { - try { - PackageInfo info = pm.getPackageInfo(packageName, 0); - return (info != null) && (info.applicationInfo != null) && - ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); - } catch (NameNotFoundException e) { - return false; - } - } else { - return false; - } - } - /** * Returns true if the intent is a valid launch intent for a launcher activity of an app. * This is used to identify shortcuts which are different from the ones exposed by the @@ -306,13 +188,6 @@ public class PackageManagerHelper implements SafeCloseable{ return (int) (100 * info.getLoadingProgress()); } - /** Returns true in case app is installed on the device or in archived state. */ - @SuppressWarnings("NewApi") - private boolean isPackageInstalledOrArchived(ApplicationInfo info) { - return (info.flags & ApplicationInfo.FLAG_INSTALLED) != 0 || ( - Flags.enableSupportForArchiving() && info.isArchived); - } - /** * Returns whether the given component or its application has the multi-instance property set. */ diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java index d3160e0db3..8131a231c0 100644 --- a/src/com/android/launcher3/views/ActivityContext.java +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -76,7 +76,7 @@ import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.PopupDataProvider; import com.android.launcher3.util.ActivityOptionsWrapper; -import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.util.ApplicationInfoWrapper; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.RunnableList; import com.android.launcher3.util.SplitConfigurationOptions; @@ -391,7 +391,7 @@ public interface ActivityContext { View v, Intent intent, @Nullable ItemInfo item) { Preconditions.assertUIThread(); Context context = (Context) this; - if (isAppBlockedForSafeMode() && !PackageManagerHelper.isSystemApp(context, intent)) { + if (isAppBlockedForSafeMode() && !new ApplicationInfoWrapper(context, intent).isSystem()) { Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show(); return null; } diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java index 9253b374d6..f8dc6b0bb2 100644 --- a/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java +++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java @@ -24,7 +24,7 @@ import androidx.annotation.WorkerThread; import com.android.launcher3.R; import com.android.launcher3.model.WidgetItem; -import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.util.ApplicationInfoWrapper; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.ResourceBasedOverride; @@ -62,14 +62,14 @@ public class WidgetRecommendationCategoryProvider implements ResourceBasedOverri // via the overridden WidgetRecommendationCategoryProvider resource. Preconditions.assertWorkerThread(); - try (PackageManagerHelper pmHelper = new PackageManagerHelper(context)) { - if (item.widgetInfo != null && item.widgetInfo.getComponent() != null) { - ApplicationInfo applicationInfo = pmHelper.getApplicationInfo( - item.widgetInfo.getComponent().getPackageName(), item.widgetInfo.getUser(), - 0 /* flags */); - if (applicationInfo != null) { - return getCategoryFromApplicationCategory(applicationInfo.category); - } + if (item.widgetInfo != null && item.widgetInfo.getComponent() != null) { + ApplicationInfo applicationInfo = new ApplicationInfoWrapper( + context, + item.widgetInfo.getComponent().getPackageName(), + item.widgetInfo.getUser()) + .getInfo(); + if (applicationInfo != null) { + return getCategoryFromApplicationCategory(applicationInfo.category); } } return null; diff --git a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt index 1d9c161e93..7529ba9b12 100644 --- a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt @@ -118,11 +118,17 @@ class WorkspaceItemProcessorTest { `package` = "pkg" putExtra(ShortcutKey.EXTRA_SHORTCUT_ID, "") } + mockLauncherApps = + mock().apply { + whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true) + whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(true) + } mockContext = mock().apply { whenever(packageManager).thenReturn(mock()) whenever(packageManager.getUserBadgedLabel(any(), any())).thenReturn("") whenever(applicationContext).thenReturn(ApplicationProvider.getApplicationContext()) + whenever(getSystemService(LauncherApps::class.java)).thenReturn(mockLauncherApps) } mockAppState = mock().apply { @@ -135,11 +141,6 @@ class WorkspaceItemProcessorTest { whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle)) .thenReturn(intent) } - mockLauncherApps = - mock().apply { - whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true) - whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(true) - } mockCursor = mock(LoaderCursor::class.java, RETURNS_DEEP_STUBS).apply { user = mUserHandle @@ -193,7 +194,7 @@ class WorkspaceItemProcessorTest { pendingPackages: MutableSet = mPendingPackages, unlockedUsers: LongSparseArray = mUnlockedUsersArray, installingPkgs: HashMap = mInstallingPkgs, - allDeepShortcuts: MutableList = mAllDeepShortcuts + allDeepShortcuts: MutableList = mAllDeepShortcuts, ) = WorkspaceItemProcessor( c = cursor, @@ -212,7 +213,7 @@ class WorkspaceItemProcessorTest { isSdCardReady = isSdCardReady, shortcutKeyToPinnedShortcuts = shortcutKeyToPinnedShortcuts, installingPkgs = installingPkgs, - allDeepShortcuts = allDeepShortcuts + allDeepShortcuts = allDeepShortcuts, ) @Test @@ -351,7 +352,7 @@ class WorkspaceItemProcessorTest { " targetPkg=package," + " component=ComponentInfo{package/class}." + " Unable to create launch Intent.", - MISSING_INFO + MISSING_INFO, ) verify(mockCursor, times(0)).checkAndAddItem(any(), any(), anyOrNull()) } @@ -412,7 +413,7 @@ class WorkspaceItemProcessorTest { verify(mockCursor) .markDeleted( "Pinned shortcut not found from request. package=pkg, user=UserHandle{0}", - "shortcut_not_found" + "shortcut_not_found", ) } @@ -549,7 +550,7 @@ class WorkspaceItemProcessorTest { val inflationResult = WidgetInflater.InflationResult( type = WidgetInflater.TYPE_REAL, - widgetInfo = expectedWidgetProviderInfo + widgetInfo = expectedWidgetProviderInfo, ) mockWidgetInflater = mock().apply { @@ -607,7 +608,7 @@ class WorkspaceItemProcessorTest { val inflationResult = WidgetInflater.InflationResult( type = WidgetInflater.TYPE_PENDING, - widgetInfo = mockProviderInfo + widgetInfo = mockProviderInfo, ) mockWidgetInflater = mock().apply { @@ -662,7 +663,7 @@ class WorkspaceItemProcessorTest { verify(mockCursor) .markDeleted( "processWidget: Unrestored Pending widget removed: id=1, appWidgetId=0, component=$expectedComponentName, restoreFlag:=4", - LauncherRestoreEventLogger.RestoreError.APP_NOT_INSTALLED + LauncherRestoreEventLogger.RestoreError.APP_NOT_INSTALLED, ) } @@ -670,12 +671,6 @@ class WorkspaceItemProcessorTest { fun `When widget inflation result is TYPE_DELETE then mark deleted`() { // Given val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider" - val expectedComponentName = ComponentName.unflattenFromString(expectedProvider) - val expectedPackage = expectedComponentName!!.packageName - mockPmHelper = - mock().apply { - whenever(isAppArchived(expectedPackage)).thenReturn(true) - } mockCursor = mock().apply { itemType = ITEM_TYPE_APPWIDGET @@ -694,7 +689,7 @@ class WorkspaceItemProcessorTest { type = WidgetInflater.TYPE_DELETE, widgetInfo = null, reason = "test_delete_reason", - restoreErrorType = MISSING_WIDGET_PROVIDER + restoreErrorType = MISSING_WIDGET_PROVIDER, ) mockWidgetInflater = mock().apply { diff --git a/tests/multivalentTests/src/com/android/launcher3/util/ApplicationInfoWrapperTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/ApplicationInfoWrapperTest.kt new file mode 100644 index 0000000000..86c3fd8536 --- /dev/null +++ b/tests/multivalentTests/src/com/android/launcher3/util/ApplicationInfoWrapperTest.kt @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2024 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.launcher3.util + +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.ApplicationInfo.FLAG_EXTERNAL_STORAGE +import android.content.pm.ApplicationInfo.FLAG_INSTALLED +import android.content.pm.ApplicationInfo.FLAG_SUSPENDED +import android.content.pm.ApplicationInfo.FLAG_SYSTEM +import android.content.pm.LauncherApps +import android.os.UserHandle +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever + +/** Unit tests for {@link ApplicationInfoWrapper}. */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class ApplicationInfoWrapperTest { + + @get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT) + + private lateinit var context: Context + private lateinit var launcherApps: LauncherApps + + @Before + fun setup() { + context = Mockito.mock(Context::class.java) + launcherApps = Mockito.mock(LauncherApps::class.java) + whenever(context.getSystemService(eq(LauncherApps::class.java))).thenReturn(launcherApps) + } + + @Test + @EnableFlags(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING) + fun archivedApp_appInfoIsNotNull() { + val applicationInfo = ApplicationInfo() + applicationInfo.isArchived = true + whenever(launcherApps.getApplicationInfo(eq(TEST_PACKAGE), any(), eq(TEST_USER))) + .thenReturn(applicationInfo) + + val wrapper = ApplicationInfoWrapper(context, TEST_PACKAGE, TEST_USER) + assertNotNull(wrapper.getInfo()) + assertTrue(wrapper.isArchived()) + assertFalse(wrapper.isInstalled()) + } + + @Test + fun notInstalledApp_nullAppInfo() { + val applicationInfo = ApplicationInfo() + whenever(launcherApps.getApplicationInfo(eq(TEST_PACKAGE), any(), eq(TEST_USER))) + .thenReturn(applicationInfo) + + val wrapper = ApplicationInfoWrapper(context, TEST_PACKAGE, TEST_USER) + assertNull(wrapper.getInfo()) + assertFalse(wrapper.isInstalled()) + } + + @Test + fun appInfo_suspended() { + val wrapper = + ApplicationInfoWrapper( + ApplicationInfo().apply { flags = FLAG_INSTALLED.or(FLAG_SUSPENDED) } + ) + assertTrue(wrapper.isSuspended()) + } + + @Test + fun appInfo_notSuspended() { + val wrapper = ApplicationInfoWrapper(ApplicationInfo()) + assertFalse(wrapper.isSuspended()) + } + + @Test + fun appInfo_system() { + val wrapper = ApplicationInfoWrapper(ApplicationInfo().apply { flags = FLAG_SYSTEM }) + assertTrue(wrapper.isSystem()) + } + + @Test + fun appInfo_notSystem() { + val wrapper = ApplicationInfoWrapper(ApplicationInfo()) + assertFalse(wrapper.isSystem()) + } + + @Test + fun appInfo_onSDCard() { + val wrapper = + ApplicationInfoWrapper(ApplicationInfo().apply { flags = FLAG_EXTERNAL_STORAGE }) + assertTrue(wrapper.isOnSdCard()) + } + + @Test + fun appInfo_notOnSDCard() { + val wrapper = ApplicationInfoWrapper(ApplicationInfo()) + assertFalse(wrapper.isOnSdCard()) + } + + companion object { + const val TEST_PACKAGE = "com.android.test.package" + private val TEST_USER = UserHandle.of(3) + } +} diff --git a/tests/multivalentTests/src/com/android/launcher3/util/PackageManagerHelperTest.java b/tests/multivalentTests/src/com/android/launcher3/util/PackageManagerHelperTest.java deleted file mode 100644 index b5e797ed39..0000000000 --- a/tests/multivalentTests/src/com/android/launcher3/util/PackageManagerHelperTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2024 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.launcher3.util; - -import static com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.LauncherApps; -import android.content.pm.PackageManager; -import android.os.UserHandle; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; -import androidx.test.platform.app.InstrumentationRegistry; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; - -/** Unit tests for {@link PackageManagerHelper}. */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public final class PackageManagerHelperTest { - @Rule - public ExpectedException exception = ExpectedException.none(); - - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - - private static final String TEST_PACKAGE = "com.android.test.package"; - private static final int TEST_USER = 2; - - private Context mContext; - private LauncherApps mLauncherApps; - private PackageManagerHelper mPackageManagerHelper; - - @Before - public void setup() { - mContext = mock(Context.class); - mLauncherApps = mock(LauncherApps.class); - when(mContext.getSystemService(eq(LauncherApps.class))).thenReturn(mLauncherApps); - when(mContext.getResources()).thenReturn( - InstrumentationRegistry.getInstrumentation().getTargetContext().getResources()); - mPackageManagerHelper = new PackageManagerHelper(mContext); - } - - @Test - @RequiresFlagsEnabled(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING) - public void getApplicationInfo_archivedApp_appInfoIsNotNull() - throws PackageManager.NameNotFoundException { - ApplicationInfo applicationInfo = new ApplicationInfo(); - applicationInfo.isArchived = true; - when(mLauncherApps.getApplicationInfo(TEST_PACKAGE, 0 /* flags */, - UserHandle.of(TEST_USER))) - .thenReturn(applicationInfo); - - assertThat(mPackageManagerHelper.getApplicationInfo(TEST_PACKAGE, UserHandle.of(TEST_USER), - 0 /* flags */)) - .isNotNull(); - } -} diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java index 3024d26af7..8b6553ff2c 100644 --- a/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java +++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java @@ -30,14 +30,16 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; -import android.content.ContextWrapper; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherApps; import android.os.Process; @@ -102,13 +104,8 @@ public class WidgetRecommendationCategoryProviderTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mContext = new ContextWrapper(getInstrumentation().getTargetContext()) { - @Override - public Object getSystemService(String name) { - return LAUNCHER_APPS_SERVICE.equals(name) ? mLauncherApps : super.getSystemService( - name); - } - }; + mContext = spy(getInstrumentation().getTargetContext()); + doReturn(mLauncherApps).when(mContext).getSystemService(LauncherApps.class); mTestAppInfo.flags = FLAG_INSTALLED; mTestProfile = new InvariantDeviceProfile(); mTestProfile.numRows = 5; @@ -132,7 +129,7 @@ public class WidgetRecommendationCategoryProviderTest { mTestAppInfo.category = testCategory.getKey(); when(mLauncherApps.getApplicationInfo(/*packageName=*/ eq(TEST_PACKAGE), - /*flags=*/ eq(0), + /*flags=*/ anyInt(), /*user=*/ eq(Process.myUserHandle()))) .thenReturn(mTestAppInfo); diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt index b93c3053e2..f1b62719bf 100644 --- a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt +++ b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt @@ -20,6 +20,7 @@ import android.appwidget.AppWidgetProviderInfo import android.content.ComponentName import android.content.Context import android.content.Intent +import android.content.pm.ApplicationInfo import android.content.pm.LauncherApps import android.content.pm.PackageInstaller import android.content.pm.ShortcutInfo @@ -28,14 +29,12 @@ import android.platform.test.annotations.EnableFlags import android.util.LongSparseArray import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING import com.android.launcher3.LauncherAppState import com.android.launcher3.LauncherSettings.Favorites import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET -import com.android.launcher3.Utilities import com.android.launcher3.model.data.IconRequestInfo import com.android.launcher3.model.data.LauncherAppWidgetInfo import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_RESTORE_STARTED @@ -48,7 +47,6 @@ import com.android.launcher3.util.PackageManagerHelper import com.android.launcher3.util.PackageUserKey import com.android.launcher3.util.UserIconInfo import com.android.launcher3.widget.WidgetInflater -import com.android.launcher3.widget.WidgetSections import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test @@ -63,7 +61,6 @@ import org.mockito.kotlin.doAnswer import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.whenever -import org.mockito.quality.Strictness @RunWith(AndroidJUnit4::class) class WorkspaceItemProcessorExtraTest { @@ -108,11 +105,17 @@ class WorkspaceItemProcessorExtraTest { `package` = "pkg" putExtra(ShortcutKey.EXTRA_SHORTCUT_ID, "") } + mockLauncherApps = + mock().apply { + whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true) + whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(true) + } mockContext = mock().apply { whenever(packageManager).thenReturn(mock()) whenever(packageManager.getUserBadgedLabel(any(), any())).thenReturn("") whenever(applicationContext).thenReturn(ApplicationProvider.getApplicationContext()) + whenever(getSystemService(LauncherApps::class.java)).thenReturn(mockLauncherApps) } mockAppState = mock().apply { @@ -125,11 +128,6 @@ class WorkspaceItemProcessorExtraTest { whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle)) .thenReturn(intent) } - mockLauncherApps = - mock().apply { - whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true) - whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(true) - } mockCursor = Mockito.mock(LoaderCursor::class.java, RETURNS_DEEP_STUBS).apply { user = mUserHandle @@ -163,138 +161,116 @@ class WorkspaceItemProcessorExtraTest { @Test fun `When Pending App Widget has not started restore then update db and add item`() { - - val mockitoSession = - ExtendedMockito.mockitoSession() - .strictness(Strictness.LENIENT) - .mockStatic(WidgetSections::class.java) - .startMocking() - try { - // Given - val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider" - val expectedComponentName = - ComponentName.unflattenFromString(expectedProvider)!!.flattenToString() - val expectedRestoreStatus = FLAG_UI_NOT_READY or FLAG_RESTORE_STARTED - val expectedAppWidgetId = 0 - mockCursor.apply { - itemType = ITEM_TYPE_APPWIDGET - user = mUserHandle - restoreFlag = FLAG_UI_NOT_READY - container = CONTAINER_DESKTOP - whenever(isOnWorkspaceOrHotseat).thenCallRealMethod() - whenever(appWidgetProvider).thenReturn(expectedProvider) - whenever(appWidgetId).thenReturn(expectedAppWidgetId) - whenever(spanX).thenReturn(2) - whenever(spanY).thenReturn(1) - whenever(options).thenReturn(0) - whenever(appWidgetSource).thenReturn(20) - whenever(applyCommonProperties(any())).thenCallRealMethod() - whenever( - updater() - .put(Favorites.APPWIDGET_PROVIDER, expectedComponentName) - .put(Favorites.APPWIDGET_ID, expectedAppWidgetId) - .put(Favorites.RESTORED, expectedRestoreStatus) - .commit() - ) - .thenReturn(1) - } - val inflationResult = - WidgetInflater.InflationResult( - type = WidgetInflater.TYPE_PENDING, - widgetInfo = null - ) - mockWidgetInflater = - mock().apply { - whenever(inflateAppWidget(any())).thenReturn(inflationResult) - } - val packageUserKey = PackageUserKey("com.google.android.testApp", mUserHandle) - mInstallingPkgs[packageUserKey] = PackageInstaller.SessionInfo() - - // When - itemProcessorUnderTest = - createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap) - itemProcessorUnderTest.processItem() - - // Then - val expectedWidgetInfo = - LauncherAppWidgetInfo().apply { - appWidgetId = expectedAppWidgetId - providerName = ComponentName.unflattenFromString(expectedProvider) - restoreStatus = expectedRestoreStatus - } - verify( - mockCursor - .updater() - .put(Favorites.APPWIDGET_PROVIDER, expectedProvider) + // Given + val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider" + val expectedComponentName = + ComponentName.unflattenFromString(expectedProvider)!!.flattenToString() + val expectedRestoreStatus = FLAG_UI_NOT_READY or FLAG_RESTORE_STARTED + val expectedAppWidgetId = 0 + mockCursor.apply { + itemType = ITEM_TYPE_APPWIDGET + user = mUserHandle + restoreFlag = FLAG_UI_NOT_READY + container = CONTAINER_DESKTOP + whenever(isOnWorkspaceOrHotseat).thenCallRealMethod() + whenever(appWidgetProvider).thenReturn(expectedProvider) + whenever(appWidgetId).thenReturn(expectedAppWidgetId) + whenever(spanX).thenReturn(2) + whenever(spanY).thenReturn(1) + whenever(options).thenReturn(0) + whenever(appWidgetSource).thenReturn(20) + whenever(applyCommonProperties(any())).thenCallRealMethod() + whenever( + updater() + .put(Favorites.APPWIDGET_PROVIDER, expectedComponentName) .put(Favorites.APPWIDGET_ID, expectedAppWidgetId) .put(Favorites.RESTORED, expectedRestoreStatus) + .commit() ) - .commit() - val widgetInfoCaptor = ArgumentCaptor.forClass(LauncherAppWidgetInfo::class.java) - verify(mockCursor).checkAndAddItem(widgetInfoCaptor.capture(), eq(mockBgDataModel)) - val actualWidgetInfo = widgetInfoCaptor.value - with(actualWidgetInfo) { - assertThat(providerName).isEqualTo(expectedWidgetInfo.providerName) - assertThat(restoreStatus).isEqualTo(expectedWidgetInfo.restoreStatus) - assertThat(targetComponent).isEqualTo(expectedWidgetInfo.targetComponent) - assertThat(appWidgetId).isEqualTo(expectedWidgetInfo.appWidgetId) + .thenReturn(1) + } + val inflationResult = + WidgetInflater.InflationResult(type = WidgetInflater.TYPE_PENDING, widgetInfo = null) + mockWidgetInflater = + mock().apply { + whenever(inflateAppWidget(any())).thenReturn(inflationResult) } - } finally { - mockitoSession.finishMocking() + val packageUserKey = PackageUserKey("com.google.android.testApp", mUserHandle) + mInstallingPkgs[packageUserKey] = PackageInstaller.SessionInfo() + + // When + itemProcessorUnderTest = + createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap) + itemProcessorUnderTest.processItem() + + // Then + val expectedWidgetInfo = + LauncherAppWidgetInfo().apply { + appWidgetId = expectedAppWidgetId + providerName = ComponentName.unflattenFromString(expectedProvider) + restoreStatus = expectedRestoreStatus + } + verify( + mockCursor + .updater() + .put(Favorites.APPWIDGET_PROVIDER, expectedProvider) + .put(Favorites.APPWIDGET_ID, expectedAppWidgetId) + .put(Favorites.RESTORED, expectedRestoreStatus) + ) + .commit() + val widgetInfoCaptor = ArgumentCaptor.forClass(LauncherAppWidgetInfo::class.java) + verify(mockCursor).checkAndAddItem(widgetInfoCaptor.capture(), eq(mockBgDataModel)) + val actualWidgetInfo = widgetInfoCaptor.value + with(actualWidgetInfo) { + assertThat(providerName).isEqualTo(expectedWidgetInfo.providerName) + assertThat(restoreStatus).isEqualTo(expectedWidgetInfo.restoreStatus) + assertThat(targetComponent).isEqualTo(expectedWidgetInfo.targetComponent) + assertThat(appWidgetId).isEqualTo(expectedWidgetInfo.appWidgetId) } } @Test @EnableFlags(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING) fun `When Archived Pending App Widget then checkAndAddItem`() { - val mockitoSession = - ExtendedMockito.mockitoSession().mockStatic(Utilities::class.java).startMocking() - try { - // Given - val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider" - val expectedComponentName = ComponentName.unflattenFromString(expectedProvider) - val expectedPackage = expectedComponentName!!.packageName - mockPmHelper = - mock().apply { - whenever(isAppArchived(expectedPackage)).thenReturn(true) - } - mockCursor = - mock().apply { - itemType = ITEM_TYPE_APPWIDGET - id = 1 - user = UserHandle(1) - restoreFlag = FLAG_UI_NOT_READY - container = CONTAINER_DESKTOP - whenever(isOnWorkspaceOrHotseat).thenCallRealMethod() - whenever(appWidgetProvider).thenReturn(expectedProvider) - whenever(appWidgetId).thenReturn(0) - whenever(spanX).thenReturn(2) - whenever(spanY).thenReturn(1) - whenever(options).thenReturn(0) - whenever(appWidgetSource).thenReturn(20) - whenever(applyCommonProperties(any())).thenCallRealMethod() - } - mInstallingPkgs = hashMapOf() - val inflationResult = - WidgetInflater.InflationResult( - type = WidgetInflater.TYPE_PENDING, - widgetInfo = null - ) - mockWidgetInflater = - mock().apply { - whenever(inflateAppWidget(any())).thenReturn(inflationResult) - } - itemProcessorUnderTest = - createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap) + // Given + val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider" + val expectedComponentName = ComponentName.unflattenFromString(expectedProvider) + val expectedPackage = expectedComponentName!!.packageName + val expectedUser = UserHandle(1) - // When - itemProcessorUnderTest.processItem() + whenever(mockLauncherApps.getApplicationInfo(eq(expectedPackage), any(), eq(expectedUser))) + .thenReturn(ApplicationInfo().apply { isArchived = true }) + mockCursor = + mock().apply { + itemType = ITEM_TYPE_APPWIDGET + id = 1 + user = expectedUser + restoreFlag = FLAG_UI_NOT_READY + container = CONTAINER_DESKTOP + whenever(isOnWorkspaceOrHotseat).thenCallRealMethod() + whenever(appWidgetProvider).thenReturn(expectedProvider) + whenever(appWidgetId).thenReturn(0) + whenever(spanX).thenReturn(2) + whenever(spanY).thenReturn(1) + whenever(options).thenReturn(0) + whenever(appWidgetSource).thenReturn(20) + whenever(applyCommonProperties(any())).thenCallRealMethod() + } + mInstallingPkgs = hashMapOf() + val inflationResult = + WidgetInflater.InflationResult(type = WidgetInflater.TYPE_PENDING, widgetInfo = null) + mockWidgetInflater = + mock().apply { + whenever(inflateAppWidget(any())).thenReturn(inflationResult) + } + itemProcessorUnderTest = + createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap) - // Then - verify(mockCursor).checkAndAddItem(any(), any()) - } finally { - mockitoSession.finishMocking() - } + // When + itemProcessorUnderTest.processItem() + + // Then + verify(mockCursor).checkAndAddItem(any(), any()) } private fun createWorkspaceItemProcessorUnderTest( @@ -314,7 +290,7 @@ class WorkspaceItemProcessorExtraTest { pendingPackages: MutableSet = mPendingPackages, unlockedUsers: LongSparseArray = mUnlockedUsersArray, installingPkgs: HashMap = mInstallingPkgs, - allDeepShortcuts: MutableList = mAllDeepShortcuts + allDeepShortcuts: MutableList = mAllDeepShortcuts, ) = WorkspaceItemProcessor( c = cursor, @@ -333,6 +309,6 @@ class WorkspaceItemProcessorExtraTest { isSdCardReady = isSdCardReady, shortcutKeyToPinnedShortcuts = shortcutKeyToPinnedShortcuts, installingPkgs = installingPkgs, - allDeepShortcuts = allDeepShortcuts + allDeepShortcuts = allDeepShortcuts, ) } diff --git a/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt index 3dd8dbceec..ca2ef42124 100644 --- a/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt +++ b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt @@ -18,6 +18,7 @@ package com.android.launcher3.pm import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo.FLAG_INSTALLED +import android.content.pm.ApplicationInfo.FLAG_SYSTEM import android.content.pm.LauncherApps import android.content.pm.PackageInstaller import android.content.pm.PackageManager @@ -35,7 +36,9 @@ import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.whenever @@ -126,13 +129,10 @@ class InstallSessionHelperTest { fun `isTrustedPackage returns true if LauncherApps finds ApplicationInfo`() { // Given val expectedApplicationInfo = - ApplicationInfo().apply { - flags = flags or FLAG_INSTALLED - enabled = true - } + ApplicationInfo().apply { flags = FLAG_SYSTEM or FLAG_INSTALLED } doReturn(expectedApplicationInfo) .whenever(launcherApps) - .getApplicationInfo(expectedAppPackage, ApplicationInfo.FLAG_SYSTEM, UserHandle(0)) + .getApplicationInfo(eq(expectedAppPackage), any(), eq(UserHandle(0))) // When val actualResult = installSessionHelper.isTrustedPackage(expectedAppPackage, UserHandle(0)) // Then