Merge changes from topic "revert-10403399-pkg-override-label-icon-DCRMJNYAKW" into rvc-dev

* changes:
  Revert "Add test constructor to PackageManagerService"
  Revert "Allow overriding the label and icon of a MainComponent"
This commit is contained in:
Steven Thomas
2020-04-01 18:13:13 +00:00
committed by Android (Google) Code Review
24 changed files with 27 additions and 998 deletions

View File

@@ -305,35 +305,6 @@ interface IPackageManager {
void setHomeActivity(in ComponentName className, int userId);
/**
* Overrides the label and icon of the component specified by the component name. The component
* must belong to the calling app.
*
* These changes will be reset on the next boot and whenever the package is updated.
*
* Only the app defined as com.android.internal.R.config_overrideComponentUiPackage is allowed
* to call this.
*
* @param componentName The component name to override the label/icon of.
* @param nonLocalizedLabel The label to be displayed.
* @param icon The icon to be displayed.
* @param userId The user id.
*/
void overrideLabelAndIcon(in ComponentName componentName, String nonLocalizedLabel,
int icon, int userId);
/**
* Restores the label and icon of the activity specified by the component name if either has
* been overridden. The component must belong to the calling app.
*
* Only the app defined as com.android.internal.R.config_overrideComponentUiPackage is allowed
* to call this.
*
* @param componentName The component name.
* @param userId The user id.
*/
void restoreLabelAndIcon(in ComponentName componentName, int userId);
/**
* As per {@link android.content.pm.PackageManager#setComponentEnabledSetting}.
*/

View File

@@ -27,24 +27,18 @@ import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.os.BaseBundle;
import android.os.Debug;
import android.os.PersistableBundle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -90,9 +84,6 @@ public class PackageUserState {
private ArrayMap<String, String[]> sharedLibraryOverlayPaths; // Lib name to overlay paths
private String[] cachedOverlayPaths;
@Nullable
private ArrayMap<ComponentName, Pair<String, Integer>> componentLabelIconOverrideMap;
@UnsupportedAppUsage
public PackageUserState() {
installed = true;
@@ -132,9 +123,6 @@ public class PackageUserState {
sharedLibraryOverlayPaths = new ArrayMap<>(o.sharedLibraryOverlayPaths);
}
harmfulAppWarning = o.harmfulAppWarning;
if (o.componentLabelIconOverrideMap != null) {
this.componentLabelIconOverrideMap = new ArrayMap<>(o.componentLabelIconOverrideMap);
}
}
public String[] getOverlayPaths() {
@@ -158,65 +146,6 @@ public class PackageUserState {
cachedOverlayPaths = null;
}
/**
* Overrides the non-localized label and icon of a component.
*
* @return true if the label or icon was changed.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public boolean overrideLabelAndIcon(@NonNull ComponentName component,
@Nullable String nonLocalizedLabel, @Nullable Integer icon) {
String existingLabel = null;
Integer existingIcon = null;
if (componentLabelIconOverrideMap != null) {
Pair<String, Integer> pair = componentLabelIconOverrideMap.get(component);
if (pair != null) {
existingLabel = pair.first;
existingIcon = pair.second;
}
}
boolean changed = !TextUtils.equals(existingLabel, nonLocalizedLabel)
|| !Objects.equals(existingIcon, icon);
if (changed) {
if (nonLocalizedLabel == null && icon == null) {
componentLabelIconOverrideMap.remove(component);
if (componentLabelIconOverrideMap.isEmpty()) {
componentLabelIconOverrideMap = null;
}
} else {
if (componentLabelIconOverrideMap == null) {
componentLabelIconOverrideMap = new ArrayMap<>(1);
}
componentLabelIconOverrideMap.put(component, Pair.create(nonLocalizedLabel, icon));
}
}
return changed;
}
/**
* Clears all values previously set by {@link #overrideLabelAndIcon(ComponentName,
* String, Integer)}.
*
* This is done when the package is updated as the components and resource IDs may have changed.
*/
public void resetOverrideComponentLabelIcon() {
componentLabelIconOverrideMap = null;
}
@Nullable
public Pair<String, Integer> getOverrideLabelIconForComponent(ComponentName componentName) {
if (ArrayUtils.isEmpty(componentLabelIconOverrideMap)) {
return null;
}
return componentLabelIconOverrideMap.get(componentName);
}
/**
* Test if this package is installed.
*/

View File

@@ -1910,9 +1910,6 @@
<!-- The name of the package that will hold the system gallery role. -->
<string name="config_systemGallery" translatable="false">com.android.gallery</string>
<!-- The name of the package that will be allowed to change its components' label/icon. -->
<string name="config_overrideComponentUiPackage" translatable="false"></string>
<!-- Enable/disable default bluetooth profiles:
HSP_AG, ObexObjectPush, Audio, NAP -->
<bool name="config_bluetooth_default_profiles">true</bool>

View File

@@ -3961,6 +3961,4 @@
<!-- Set to true to make assistant show in front of the dream/screensaver. -->
<java-symbol type="bool" name="config_assistantOnTopOfDream"/>
<java-symbol type="string" name="config_overrideComponentUiPackage" />
</resources>

View File

@@ -54,7 +54,6 @@ import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.IntentResolver;
import com.android.server.pm.parsing.PackageInfoUtils;
@@ -208,57 +207,33 @@ public class ComponentResolver {
}
/** Returns the given activity */
@Nullable
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public ParsedActivity getActivity(@NonNull ComponentName component) {
ParsedActivity getActivity(ComponentName component) {
synchronized (mLock) {
return mActivities.mActivities.get(component);
}
}
/** Returns the given provider */
@Nullable
ParsedProvider getProvider(@NonNull ComponentName component) {
ParsedProvider getProvider(ComponentName component) {
synchronized (mLock) {
return mProviders.mProviders.get(component);
}
}
/** Returns the given receiver */
@Nullable
ParsedActivity getReceiver(@NonNull ComponentName component) {
ParsedActivity getReceiver(ComponentName component) {
synchronized (mLock) {
return mReceivers.mActivities.get(component);
}
}
/** Returns the given service */
@Nullable
ParsedService getService(@NonNull ComponentName component) {
ParsedService getService(ComponentName component) {
synchronized (mLock) {
return mServices.mServices.get(component);
}
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public boolean componentExists(@NonNull ComponentName componentName) {
synchronized (mLock) {
ParsedMainComponent component = mActivities.mActivities.get(componentName);
if (component != null) {
return true;
}
component = mReceivers.mActivities.get(componentName);
if (component != null) {
return true;
}
component = mServices.mServices.get(componentName);
if (component != null) {
return true;
}
return mProviders.mProviders.get(componentName) != null;
}
}
@Nullable
List<ResolveInfo> queryActivities(Intent intent, String resolvedType, int flags,
@PrivateResolveFlags int privateResolveFlags, int userId) {

View File

@@ -674,7 +674,7 @@ public class PackageManagerService extends IPackageManager.Stub
final ServiceThread mHandlerThread;
final Handler mHandler;
final PackageHandler mHandler;
private final ProcessLoggingHandler mProcessLoggingHandler;
@@ -1033,61 +1033,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class TestParams {
public ApexManager apexManager;
public @Nullable String appPredictionServicePackage;
public ArtManagerService artManagerService;
public @Nullable String configuratorPackage;
public int defParseFlags;
public DexManager dexManager;
public List<ScanPartition> dirsToScanAsSystem;
public @Nullable String documenterPackage;
public boolean factoryTest;
public ArrayMap<String, FeatureInfo> availableFeatures;
public Handler handler;
public ServiceThread handlerThread;
public @Nullable String incidentReportApproverPackage;
public IncrementalManager incrementalManager;
public PackageInstallerService installerService;
public InstantAppRegistry instantAppRegistry;
public InstantAppResolverConnection instantAppResolverConnection;
public ComponentName instantAppResolverSettingsComponent;
public @Nullable IntentFilterVerifier<ParsedIntentInfo> intentFilterVerifier;
public @Nullable ComponentName intentFilterVerifierComponent;
public boolean isPreNmr1Upgrade;
public boolean isPreNupgrade;
public boolean isPreQupgrade;
public boolean isUpgrade;
public DisplayMetrics Metrics;
public ModuleInfoProvider moduleInfoProvider;
public MoveCallbacks moveCallbacks;
public boolean onlyCore;
public OverlayConfig overlayConfig;
public PackageDexOptimizer packageDexOptimizer;
public IPermissionManager permissionManagerService;
public PendingPackageBroadcasts pendingPackageBroadcasts;
public PackageManagerInternal pmInternal;
public ProcessLoggingHandler processLoggingHandler;
public ProtectedPackages protectedPackages;
public @NonNull String requiredInstallerPackage;
public @NonNull String requiredPermissionControllerPackage;
public @NonNull String requiredUninstallerPackage;
public @Nullable String requiredVerifierPackage;
public String[] separateProcesses;
public @NonNull String servicesExtensionPackageName;
public @Nullable String setupWizardPackage;
public @NonNull String sharedSystemSharedLibraryPackageName;
public @Nullable String storageManagerPackage;
public @Nullable String defaultTextClassifierPackage;
public @Nullable String systemTextClassifierPackage;
public ViewCompiler viewCompiler;
public @Nullable String wellbeingPackage;
public @Nullable String retailDemoPackage;
public ComponentName resolveComponentName;
public ArrayMap<String, AndroidPackage> packages;
}
private final AppsFilter mAppsFilter;
final PackageParser2.Callback mPackageParserCallback;
@@ -1451,8 +1396,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
// Set of pending broadcasts for aggregating enable/disable of components.
@VisibleForTesting(visibility = Visibility.PACKAGE)
public static class PendingPackageBroadcasts {
static class PendingPackageBroadcasts {
// for each user id, a map of <package name -> components within that package>
final SparseArray<ArrayMap<String, ArrayList<String>>> mUidMap;
@@ -1515,7 +1459,7 @@ public class PackageManagerService extends IPackageManager.Stub
return map;
}
}
final PendingPackageBroadcasts mPendingBroadcasts;
final PendingPackageBroadcasts mPendingBroadcasts = new PendingPackageBroadcasts();
static final int SEND_PENDING_BROADCAST = 1;
static final int INIT_COPY = 5;
@@ -2732,81 +2676,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
/**
* A extremely minimal constructor designed to start up a PackageManagerService instance for
* testing.
*
* It is assumed that all methods under test will mock the internal fields and thus
* none of the initialization is needed.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public PackageManagerService(@NonNull Injector injector, @NonNull TestParams testParams) {
mInjector = injector;
mInjector.bootstrap(this);
mAppsFilter = injector.getAppsFilter();
mComponentResolver = injector.getComponentResolver();
mContext = injector.getContext();
mInstaller = injector.getInstaller();
mInstallLock = injector.getInstallLock();
mLock = injector.getLock();
mPermissionManager = injector.getPermissionManagerServiceInternal();
mSettings = injector.getSettings();
mUserManager = injector.getUserManagerService();
mApexManager = testParams.apexManager;
mArtManagerService = testParams.artManagerService;
mAvailableFeatures = testParams.availableFeatures;
mDefParseFlags = testParams.defParseFlags;
mDexManager = testParams.dexManager;
mDirsToScanAsSystem = testParams.dirsToScanAsSystem;
mFactoryTest = testParams.factoryTest;
mHandler = testParams.handler;
mHandlerThread = testParams.handlerThread;
mIncrementalManager = testParams.incrementalManager;
mInstallerService = testParams.installerService;
mInstantAppRegistry = testParams.instantAppRegistry;
mInstantAppResolverConnection = testParams.instantAppResolverConnection;
mInstantAppResolverSettingsComponent = testParams.instantAppResolverSettingsComponent;
mIntentFilterVerifier = testParams.intentFilterVerifier;
mIntentFilterVerifierComponent = testParams.intentFilterVerifierComponent;
mIsPreNMR1Upgrade = testParams.isPreNmr1Upgrade;
mIsPreNUpgrade = testParams.isPreNupgrade;
mIsPreQUpgrade = testParams.isPreQupgrade;
mIsUpgrade = testParams.isUpgrade;
mMetrics = testParams.Metrics;
mModuleInfoProvider = testParams.moduleInfoProvider;
mMoveCallbacks = testParams.moveCallbacks;
mOnlyCore = testParams.onlyCore;
mOverlayConfig = testParams.overlayConfig;
mPackageDexOptimizer = testParams.packageDexOptimizer;
mPendingBroadcasts = testParams.pendingPackageBroadcasts;
mPermissionManagerService = testParams.permissionManagerService;
mPmInternal = testParams.pmInternal;
mProcessLoggingHandler = testParams.processLoggingHandler;
mProtectedPackages = testParams.protectedPackages;
mSeparateProcesses = testParams.separateProcesses;
mViewCompiler = testParams.viewCompiler;
mRequiredVerifierPackage = testParams.requiredVerifierPackage;
mRequiredInstallerPackage = testParams.requiredInstallerPackage;
mRequiredUninstallerPackage = testParams.requiredUninstallerPackage;
mRequiredPermissionControllerPackage = testParams.requiredPermissionControllerPackage;
mSetupWizardPackage = testParams.setupWizardPackage;
mStorageManagerPackage = testParams.storageManagerPackage;
mDefaultTextClassifierPackage = testParams.defaultTextClassifierPackage;
mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage;
mWellbeingPackage = testParams.wellbeingPackage;
mRetailDemoPackage = testParams.retailDemoPackage;
mDocumenterPackage = testParams.documenterPackage;
mConfiguratorPackage = testParams.configuratorPackage;
mAppPredictionServicePackage = testParams.appPredictionServicePackage;
mIncidentReportApproverPackage = testParams.incidentReportApproverPackage;
mServicesExtensionPackageName = testParams.servicesExtensionPackageName;
mSharedSystemSharedLibraryPackageName = testParams.sharedSystemSharedLibraryPackageName;
mResolveComponentName = testParams.resolveComponentName;
mPackages.putAll(testParams.packages);
}
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
PackageManager.invalidatePackageInfoCache();
PackageManager.disableApplicationInfoCache();
@@ -2814,8 +2683,6 @@ public class PackageManagerService extends IPackageManager.Stub
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
Trace.TRACE_TAG_PACKAGE_MANAGER);
mPendingBroadcasts = new PendingPackageBroadcasts();
mInjector = injector;
mInjector.bootstrap(this);
mLock = injector.getLock();
@@ -5301,7 +5168,7 @@ public class PackageManagerService extends IPackageManager.Stub
AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
if (pkg != null && mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) {
PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
if (shouldFilterApplicationLocked(
ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
@@ -15742,12 +15609,6 @@ public class PackageManagerService extends IPackageManager.Stub
// these install state changes will be persisted in the
// upcoming call to mSettings.writeLPr().
}
if (allUsers != null) {
for (int currentUserId : allUsers) {
ps.resetOverrideComponentLabelIcon(currentUserId);
}
}
}
// Retrieve the overlays for shared libraries of the package.
@@ -20315,86 +20176,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
@Override
public void overrideLabelAndIcon(@NonNull ComponentName componentName,
@NonNull String nonLocalizedLabel, int icon, int userId) {
if (TextUtils.isEmpty(nonLocalizedLabel)) {
throw new IllegalArgumentException("Override label should be a valid String");
}
updateComponentLabelIcon(componentName, nonLocalizedLabel, icon, userId);
}
@Override
public void restoreLabelAndIcon(@NonNull ComponentName componentName, int userId) {
updateComponentLabelIcon(componentName, null, null, userId);
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
public void updateComponentLabelIcon(/*@NonNull*/ ComponentName componentName,
@Nullable String nonLocalizedLabel, @Nullable Integer icon, int userId) {
if (componentName == null) {
throw new IllegalArgumentException("Must specify a component");
}
boolean componentExists = mComponentResolver.componentExists(componentName);
if (!componentExists) {
throw new IllegalArgumentException("Component " + componentName + " not found");
}
int callingUid = Binder.getCallingUid();
String componentPkgName = componentName.getPackageName();
int componentUid = getPackageUid(componentPkgName, 0, userId);
if (!UserHandle.isSameApp(callingUid, componentUid)) {
throw new SecurityException("The calling UID (" + callingUid + ")"
+ " does not match the target UID");
}
String allowedCallerPkg = mContext.getString(R.string.config_overrideComponentUiPackage);
if (TextUtils.isEmpty(allowedCallerPkg)) {
throw new SecurityException(
"There is no package defined as allowed to change a component's label or icon");
}
int allowedCallerUid = getPackageUid(allowedCallerPkg, PackageManager.MATCH_SYSTEM_ONLY,
userId);
if (allowedCallerUid == -1 || !UserHandle.isSameApp(callingUid, allowedCallerUid)) {
throw new SecurityException("The calling UID (" + callingUid + ")"
+ " is not allowed to change a component's label or icon");
}
synchronized (mLock) {
AndroidPackage pkg = mPackages.get(componentPkgName);
PackageSetting pkgSetting = getPackageSetting(componentPkgName);
if (pkg == null || pkgSetting == null
|| (!pkg.isSystem() && !pkgSetting.getPkgState().isUpdatedSystemApp())) {
throw new SecurityException(
"Changing the label is not allowed for " + componentName);
}
if (!pkgSetting.overrideNonLocalizedLabelAndIcon(componentName, nonLocalizedLabel,
icon, userId)) {
// Nothing changed
return;
}
}
ArrayList<String> components = mPendingBroadcasts.get(userId, componentPkgName);
if (components == null) {
components = new ArrayList<>();
mPendingBroadcasts.put(userId, componentPkgName, components);
}
String className = componentName.getClassName();
if (!components.contains(className)) {
components.add(className);
}
if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, BROADCAST_DELAY);
}
}
@Override
public void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags, int userId) {

View File

@@ -26,7 +26,6 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.PermissionsState;
import com.android.server.pm.pkg.PackageStateUnserialized;
@@ -69,8 +68,7 @@ public class PackageSetting extends PackageSettingBase {
@NonNull
private PackageStateUnserialized pkgState = new PackageStateUnserialized();
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public PackageSetting(String name, String realName, File codePath, File resourcePath,
PackageSetting(String name, String realName, File codePath, File resourcePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString,
long pVersionCode, int pkgFlags, int privateFlags,

View File

@@ -21,9 +21,6 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
@@ -700,26 +697,6 @@ public abstract class PackageSettingBase extends SettingBase {
return userState.harmfulAppWarning;
}
/**
* @see PackageUserState#overrideLabelAndIcon(ComponentName, String, Integer)
*
* @param userId the specific user to change the label/icon for
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public boolean overrideNonLocalizedLabelAndIcon(@NonNull ComponentName component,
@Nullable String label, @Nullable Integer icon, @UserIdInt int userId) {
return modifyUserState(userId).overrideLabelAndIcon(component, label, icon);
}
/**
* @see PackageUserState#resetOverrideComponentLabelIcon()
*
* @param userId the specific user to reset
*/
public void resetOverrideComponentLabelIcon(@UserIdInt int userId) {
modifyUserState(userId).resetOverrideComponentLabelIcon();
}
protected PackageSettingBase updateFrom(PackageSettingBase other) {
super.copyFrom(other);
this.codePath = other.codePath;

View File

@@ -18,11 +18,9 @@ package com.android.server.pm;
import android.content.pm.ApplicationInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.permission.PermissionsState;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public abstract class SettingBase {
abstract class SettingBase {
int pkgFlags;
int pkgPrivateFlags;

View File

@@ -91,7 +91,6 @@ import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -419,21 +418,6 @@ public final class Settings {
/** Settings and other information about permissions */
final PermissionSettings mPermissions;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public Settings(Map<String, PackageSetting> pkgSettings) {
mLock = new Object();
mPackages.putAll(pkgSettings);
mSystemDir = null;
mPermissions = null;
mRuntimePermissionsPersistence = null;
mSettingsFilename = null;
mBackupSettingsFilename = null;
mPackageListFilename = null;
mStoppedPackagesFilename = null;
mBackupStoppedPackagesFilename = null;
mKernelMappingFilename = null;
}
Settings(File dataDir, PermissionSettings permission,
Object lock) {
mLock = lock;
@@ -4344,9 +4328,8 @@ public final class Settings {
return userState.isMatch(componentInfo, flags);
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public boolean isEnabledAndMatchLPr(AndroidPackage pkg, ParsedMainComponent component,
int flags, int userId) {
boolean isEnabledAndMatchLPr(AndroidPackage pkg, ParsedMainComponent component, int flags,
int userId) {
final PackageSetting ps = mPackages.get(component.getPackageName());
if (ps == null) return false;

View File

@@ -48,7 +48,6 @@ import android.content.pm.parsing.component.ParsedService;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.util.ArrayUtils;
@@ -272,7 +271,7 @@ public class PackageInfoUtils {
ActivityInfo info =
PackageInfoWithoutStateUtils.generateActivityInfoUnchecked(a, applicationInfo);
assignSharedFieldsForComponentInfo(info, a, pkgSetting, userId);
assignSharedFieldsForComponentInfo(info, a, pkgSetting);
return info;
}
@@ -307,7 +306,7 @@ public class PackageInfoUtils {
ServiceInfo info =
PackageInfoWithoutStateUtils.generateServiceInfoUnchecked(s, applicationInfo);
assignSharedFieldsForComponentInfo(info, s, pkgSetting, userId);
assignSharedFieldsForComponentInfo(info, s, pkgSetting);
return info;
}
@@ -334,7 +333,7 @@ public class PackageInfoUtils {
}
ProviderInfo info = PackageInfoWithoutStateUtils.generateProviderInfoUnchecked(p, flags,
applicationInfo);
assignSharedFieldsForComponentInfo(info, p, pkgSetting, userId);
assignSharedFieldsForComponentInfo(info, p, pkgSetting);
return info;
}
@@ -359,7 +358,7 @@ public class PackageInfoUtils {
info.nativeLibraryDir = pkg.getNativeLibraryDir();
info.secondaryNativeLibraryDir = pkg.getSecondaryNativeLibraryDir();
assignStateFieldsForPackageItemInfo(info, i, pkgSetting, userId);
assignStateFieldsForPackageItemInfo(info, i, pkgSetting);
return info;
}
@@ -427,9 +426,8 @@ public class PackageInfoUtils {
}
private static void assignSharedFieldsForComponentInfo(@NonNull ComponentInfo componentInfo,
@NonNull ParsedMainComponent mainComponent, @Nullable PackageSetting pkgSetting,
int userId) {
assignStateFieldsForPackageItemInfo(componentInfo, mainComponent, pkgSetting, userId);
@NonNull ParsedMainComponent mainComponent, @Nullable PackageSetting pkgSetting) {
assignStateFieldsForPackageItemInfo(componentInfo, mainComponent, pkgSetting);
componentInfo.descriptionRes = mainComponent.getDescriptionRes();
componentInfo.directBootAware = mainComponent.isDirectBootAware();
componentInfo.enabled = mainComponent.isEnabled();
@@ -438,12 +436,8 @@ public class PackageInfoUtils {
private static void assignStateFieldsForPackageItemInfo(
@NonNull PackageItemInfo packageItemInfo, @NonNull ParsedComponent component,
@Nullable PackageSetting pkgSetting, int userId) {
Pair<CharSequence, Integer> labelAndIcon =
ParsedComponentStateUtils.getNonLocalizedLabelAndIcon(component, pkgSetting,
userId);
packageItemInfo.nonLocalizedLabel = labelAndIcon.first;
packageItemInfo.icon = labelAndIcon.second;
@Nullable PackageSetting pkgSetting) {
// TODO(b/135203078): Add setting related state
}
@CheckResult

View File

@@ -1,58 +0,0 @@
/*
* Copyright (C) 2020 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.server.pm.parsing;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.parsing.component.ParsedComponent;
import android.util.Pair;
import com.android.server.pm.PackageSetting;
/**
* For exposing internal fields to the rest of the server, enforcing that any overridden state from
* a {@link com.android.server.pm.PackageSetting} is applied.
*
* TODO(chiuwinson): The fields on ParsedComponent are not actually hidden. Will need to find a
* way to enforce the mechanism now that they exist in core instead of server. Can't rely on
* package-private.
*
* @hide
*/
public class ParsedComponentStateUtils {
@NonNull
public static Pair<CharSequence, Integer> getNonLocalizedLabelAndIcon(ParsedComponent component,
@Nullable PackageSetting pkgSetting, int userId) {
CharSequence label = component.getNonLocalizedLabel();
int icon = component.getIcon();
Pair<String, Integer> overrideLabelIcon = pkgSetting == null ? null :
pkgSetting.readUserState(userId)
.getOverrideLabelIconForComponent(component.getComponentName());
if (overrideLabelIcon != null) {
if (overrideLabelIcon.first != null) {
label = overrideLabelIcon.first;
}
if (overrideLabelIcon.second != null) {
icon = overrideLabelIcon.second;
}
}
return Pair.create(label, icon);
}
}

View File

@@ -1,40 +0,0 @@
//
// Copyright (C) 2019 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.
//
// NOTE: This test is separate from service tests since it relies on same vs different calling UID,
// and this is more representative of a real caller. It also uses Mockito extended, and this
// prevents converting the entire services test module.
android_test {
name: "PackageManagerComponentOverrideTests",
srcs: [
"src/**/*.kt"
],
static_libs: [
"androidx.test.runner",
"mockito-target-extended-minus-junit4",
"services.core",
"servicestests-utils-mockito-extended",
"testng", // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows
"truth-prebuilt",
],
jni_libs: [
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
],
test_suites: ["device-tests"],
platform_apis: true,
}

View File

@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2020 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.server.pm.test.override">
<uses-sdk
android:minSdkVersion="1"
android:targetSdkVersion="28" />
<application android:debuggable="true">
<uses-library android:name="android.test.mock" android:required="true" />
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:label="PackageManagerComponentOverrideTests"
android:targetPackage="com.android.server.pm.test.override" />
</manifest>

View File

@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2020 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.
-->
<configuration description="Test module config for PackageManagerComponentOverrideTests">
<option name="test-tag" value="PackageManagerComponentOverrideTests" />
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="PackageManagerComponentOverrideTests.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.server.pm.test.override" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
</test>
</configuration>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 B

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2020 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.
-->
<resources>
<public name="black16x16" type="drawable" id="0x7f080001"/>
<public name="white16x16" type="drawable" id="0x7f080002"/>
</resources>

View File

@@ -1,358 +0,0 @@
/*
* Copyright (C) 2020 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.server.pm.test.override
import android.content.ComponentName
import android.content.Context
import android.content.pm.parsing.component.ParsedActivity
import android.os.Binder
import android.os.UserHandle
import android.util.ArrayMap
import com.android.server.pm.AppsFilter
import com.android.server.pm.ComponentResolver
import com.android.server.pm.PackageManagerService
import com.android.server.pm.PackageSetting
import com.android.server.pm.Settings
import com.android.server.pm.UserManagerService
import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.pm.parsing.pkg.PackageImpl
import com.android.server.pm.parsing.pkg.ParsedPackage
import com.android.server.pm.permission.PermissionManagerServiceInternal
import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType
import com.android.server.pm.test.override.R
import com.android.server.testutils.TestHandler
import com.android.server.testutils.mock
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.spy
import com.android.server.testutils.whenever
import com.android.server.wm.ActivityTaskManagerInternal
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.mockito.Mockito.any
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.anyString
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.intThat
import org.mockito.Mockito.never
import org.mockito.Mockito.same
import org.mockito.Mockito.verify
import org.testng.Assert.assertThrows
import java.io.File
@RunWith(Parameterized::class)
class PackageManagerComponentLabelIconOverrideTest {
companion object {
private const val VALID_PKG = "com.android.server.pm.test.override"
private const val SHARED_PKG = "com.android.server.pm.test.override.shared"
private const val INVALID_PKG = "com.android.server.pm.test.override.invalid"
private const val SEND_PENDING_BROADCAST = 1 // PackageManagerService.SEND_PENDING_BROADCAST
private const val DEFAULT_LABEL = "DefaultLabel"
private const val TEST_LABEL = "TestLabel"
private const val DEFAULT_ICON = R.drawable.black16x16
private const val TEST_ICON = R.drawable.white16x16
private const val COMPONENT_CLASS_NAME = ".TestComponent"
sealed class Result {
// Component label/icon changed, message sent to send broadcast
object Changed : Result()
// Component label/icon changed, message was pending, not re-sent
object ChangedWithoutNotify : Result()
// Component label/icon did not changed, was already equivalent
object NotChanged : Result()
// Updating label/icon encountered a specific exception
data class Exception(val type: Class<out java.lang.Exception>) : Result()
}
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun parameters() = arrayOf(
// Start with an array of the simplest known inputs and expected outputs
Params(VALID_PKG, AppType.SYSTEM_APP, Result.Changed),
Params(SHARED_PKG, AppType.SYSTEM_APP, Result.Changed),
Params(INVALID_PKG, AppType.SYSTEM_APP, SecurityException::class.java)
)
.flatMap { param ->
mutableListOf(param).apply {
if (param.result is Result.Changed) {
// For each param that would've succeeded, also verify that if a change
// happened, but a message was pending, another is not re-queued/reset
this += param.copy(result = Result.ChangedWithoutNotify)
// Also verify that when the component is already configured, no change
// is propagated
this += param.copy(result = Result.NotChanged)
}
// For all params, verify that an invalid component will cause an
// IllegalArgumentException, instead of result initially specified
this += param.copy(componentName = null,
result = Result.Exception(IllegalArgumentException::class.java))
// Also verify an updated system app variant, which should have the same
// result as a vanilla system app
this += param.copy(appType = AppType.UPDATED_SYSTEM_APP)
// Also verify a non-system app will cause a failure, since normal apps
// are not allowed to edit their label/icon
this += param.copy(appType = AppType.NORMAL_APP,
result = Result.Exception(SecurityException::class.java))
}
}
data class Params(
val pkgName: String,
private val appType: AppType,
val result: Result,
val componentName: ComponentName? = ComponentName(pkgName, COMPONENT_CLASS_NAME)
) {
constructor(pkgName: String, appType: AppType, exception: Class<out Exception>)
: this(pkgName, appType, Result.Exception(exception))
val expectedLabel = when (result) {
Result.Changed, Result.ChangedWithoutNotify, Result.NotChanged -> TEST_LABEL
is Result.Exception -> DEFAULT_LABEL
}
val expectedIcon = when (result) {
Result.Changed, Result.ChangedWithoutNotify, Result.NotChanged -> TEST_ICON
is Result.Exception -> DEFAULT_ICON
}
val isUpdatedSystemApp = appType == AppType.UPDATED_SYSTEM_APP
val isSystem = appType == AppType.SYSTEM_APP || isUpdatedSystemApp
override fun toString(): String {
val resultString = when (result) {
Result.Changed -> "Changed"
Result.ChangedWithoutNotify -> "ChangedWithoutNotify"
Result.NotChanged -> "NotChanged"
is Result.Exception -> result.type.simpleName
}
// Nicer formatting for the test method suffix
return "pkg=$pkgName, type=$appType, component=$componentName, result=$resultString"
}
enum class AppType { SYSTEM_APP, UPDATED_SYSTEM_APP, NORMAL_APP }
}
}
@Parameterized.Parameter(0)
lateinit var params: Params
private lateinit var testHandler: TestHandler
private lateinit var mockPendingBroadcasts: PackageManagerService.PendingPackageBroadcasts
private lateinit var mockPkg: AndroidPackage
private lateinit var mockPkgSetting: PackageSetting
private lateinit var service: PackageManagerService
private val userId = UserHandle.getCallingUserId()
private val userIdDifferent = userId + 1
@Before
fun setUpMocks() {
makeTestData()
testHandler = TestHandler(null)
if (params.result is Result.ChangedWithoutNotify) {
// Case where the handler already has a message and so another should not be sent.
// This case will verify that only 1 message exists, which is the one added here.
testHandler.sendEmptyMessage(SEND_PENDING_BROADCAST)
}
mockPendingBroadcasts = PackageManagerService.PendingPackageBroadcasts()
service = mockService()
}
@Test
fun updateComponentLabelIcon() {
fun runUpdate() {
service.updateComponentLabelIcon(params.componentName, TEST_LABEL, TEST_ICON, userId)
}
when (val result = params.result) {
Result.Changed, Result.ChangedWithoutNotify, Result.NotChanged -> {
runUpdate()
verify(mockPkgSetting).overrideNonLocalizedLabelAndIcon(params.componentName!!,
TEST_LABEL, TEST_ICON, userId)
}
is Result.Exception -> {
assertThrows(result.type) { runUpdate() }
verify(mockPkgSetting, never()).overrideNonLocalizedLabelAndIcon(
any<ComponentName>(), any(), anyInt(), anyInt())
}
}
}
@After
fun verifyExpectedResult() {
if (params.componentName != null) {
val activityInfo = service.getActivityInfo(params.componentName, 0, userId)
assertThat(activityInfo.nonLocalizedLabel).isEqualTo(params.expectedLabel)
assertThat(activityInfo.icon).isEqualTo(params.expectedIcon)
}
}
@After
fun verifyDifferentUserUnchanged() {
when (params.result) {
Result.Changed, Result.ChangedWithoutNotify -> {
val activityInfo = service.getActivityInfo(params.componentName, 0, userIdDifferent)
assertThat(activityInfo.nonLocalizedLabel).isEqualTo(DEFAULT_LABEL)
assertThat(activityInfo.icon).isEqualTo(DEFAULT_ICON)
}
Result.NotChanged, is Result.Exception -> {}
}.run { /*exhaust*/ }
}
@After
fun verifyHandlerHasMessage() {
when (params.result) {
is Result.Changed, is Result.ChangedWithoutNotify -> {
assertThat(testHandler.pendingMessages).hasSize(1)
assertThat(testHandler.pendingMessages.first().message.what)
.isEqualTo(SEND_PENDING_BROADCAST)
}
is Result.NotChanged, is Result.Exception -> {
assertThat(testHandler.pendingMessages).hasSize(0)
}
}.run { /*exhaust*/ }
}
@After
fun verifyPendingBroadcast() {
when (params.result) {
is Result.Changed, Result.ChangedWithoutNotify -> {
assertThat(mockPendingBroadcasts.get(userId, params.pkgName))
.containsExactly(params.componentName!!.className)
.inOrder()
}
is Result.NotChanged, is Result.Exception -> {
assertThat(mockPendingBroadcasts.get(userId, params.pkgName)).isNull()
}
}.run { /*exhaust*/ }
}
private fun makePkg(pkgName: String, block: ParsedPackage.() -> Unit = {}) =
PackageImpl.forTesting(pkgName)
.setEnabled(true)
.let { it.hideAsParsed() as ParsedPackage }
.setSystem(params.isSystem)
.apply(block)
.hideAsFinal()
private fun makePkgSetting(pkgName: String) = spy(PackageSetting(pkgName, null, File("/test"),
File("/test"), null, null, null, null, 0, 0, 0, 0, null, null, null)) {
this.pkgState.isUpdatedSystemApp = params.isUpdatedSystemApp
}
private fun makeTestData() {
mockPkg = makePkg(params.pkgName)
mockPkgSetting = makePkgSetting(params.pkgName)
if (params.result is Result.NotChanged) {
// If verifying no-op behavior, set the current setting to the test values
mockPkgSetting.overrideNonLocalizedLabelAndIcon(params.componentName!!, TEST_LABEL,
TEST_ICON, userId)
// Then clear the mock because the line above just incremented it
clearInvocations(mockPkgSetting)
}
}
private fun mockService(): PackageManagerService {
val mockedPkgs = mapOf(
// Must use the test app's UID so that PMS can match them when querying, since
// the static Binder.getCallingUid can't mocked as it's marked final
VALID_PKG to makePkg(VALID_PKG) { uid = Binder.getCallingUid() },
SHARED_PKG to makePkg(SHARED_PKG) { uid = Binder.getCallingUid() },
INVALID_PKG to makePkg(INVALID_PKG) { uid = Binder.getCallingUid() + 1 }
)
val mockedPkgSettings = mapOf(
VALID_PKG to makePkgSetting(VALID_PKG),
SHARED_PKG to makePkgSetting(SHARED_PKG),
INVALID_PKG to makePkgSetting(INVALID_PKG)
)
// Add pkgSetting under test so its attributes override the defaults added above
.plus(params.pkgName to mockPkgSetting)
val mockActivity: ParsedActivity = mock {
whenever(this.packageName) { params.pkgName }
whenever(this.nonLocalizedLabel) { DEFAULT_LABEL }
whenever(this.icon) { DEFAULT_ICON }
whenever(this.componentName) { params.componentName }
whenever(this.name) { params.componentName?.className }
whenever(this.isEnabled) { true }
whenever(this.isDirectBootAware) { params.isSystem }
}
val mockSettings = Settings(mockedPkgSettings)
val mockComponentResolver: ComponentResolver = mockThrowOnUnmocked {
params.componentName?.let {
whenever(this.componentExists(same(it))) { true }
whenever(this.getActivity(same(it))) { mockActivity }
}
}
val mockUserManagerService: UserManagerService = mockThrowOnUnmocked {
val matcher: (Int) -> Boolean = { it == userId || it == userIdDifferent }
whenever(this.exists(intThat(matcher))) { true }
whenever(this.isUserUnlockingOrUnlocked(intThat(matcher))) { true }
}
val mockPermissionManagerService: PermissionManagerServiceInternal = mockThrowOnUnmocked {
whenever(this.enforceCrossUserPermission(anyInt(), anyInt(), anyBoolean(), anyBoolean(),
anyString())) { }
}
val mockActivityTaskManager: ActivityTaskManagerInternal = mockThrowOnUnmocked {
whenever(this.isCallerRecents(anyInt())) { false }
}
val mockAppsFilter: AppsFilter = mockThrowOnUnmocked {
whenever(this.shouldFilterApplication(anyInt(), any<PackageSetting>(),
any<PackageSetting>(), anyInt())) { false }
}
val mockContext: Context = mockThrowOnUnmocked {
whenever(this.getString(
com.android.internal.R.string.config_overrideComponentUiPackage)) { VALID_PKG }
}
val mockInjector: PackageManagerService.Injector = mock {
whenever(this.lock) { Object() }
whenever(this.componentResolver) { mockComponentResolver }
whenever(this.userManagerService) { mockUserManagerService }
whenever(this.permissionManagerServiceInternal) { mockPermissionManagerService }
whenever(this.settings) { mockSettings }
whenever(this.activityTaskManagerInternal) { mockActivityTaskManager }
whenever(this.appsFilter) { mockAppsFilter }
whenever(this.context) { mockContext }
}
val testParams = PackageManagerService.TestParams().apply {
this.handler = testHandler
this.pendingPackageBroadcasts = mockPendingBroadcasts
this.resolveComponentName = ComponentName("android", ".Test")
this.packages = ArrayMap<String, AndroidPackage>().apply { putAll(mockedPkgs) }
}
return PackageManagerService(mockInjector, testParams)
}
}

View File

@@ -107,8 +107,6 @@ java_library {
name: "servicestests-utils",
srcs: [
"utils/**/*.java",
"utils/**/*.kt",
"utils-mockito/**/*.kt",
],
static_libs: [
"junit",
@@ -119,22 +117,6 @@ java_library {
],
}
java_library {
name: "servicestests-utils-mockito-extended",
srcs: [
"utils/**/*.java",
"utils/**/*.kt",
"utils-mockito/**/*.kt",
],
static_libs: [
"junit",
"mockito-target-extended-minus-junit4",
],
libs: [
"android.test.runner",
],
}
filegroup {
name: "servicestests-SuspendTestApp-files",
srcs: [

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 The Android Open Source Project
* Copyright (C) 2019 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.server.testutils
package com.android.server.om
import org.mockito.Answers
import org.mockito.Mockito
@@ -31,13 +31,6 @@ object MockitoUtils {
else -> {
val arguments = it.arguments
?.takeUnless { it.isEmpty() }
?.mapIndexed { index, arg ->
try {
arg?.toString()
} catch (e: Exception) {
"toString[$index] threw ${e.message}"
}
}
?.joinToString()
?.let {
"with $it"
@@ -53,8 +46,6 @@ object MockitoUtils {
inline fun <reified T> mock(block: T.() -> Unit = {}) = Mockito.mock(T::class.java).apply(block)
fun <T> spy(value: T, block: T.() -> Unit = {}) = Mockito.spy(value).apply(block)
fun <Type> Stubber.whenever(mock: Type) = Mockito.`when`(mock)
fun <Type : Any?> whenever(mock: Type) = Mockito.`when`(mock)
@@ -64,7 +55,7 @@ fun <Type : Any?> whenever(mock: Type, block: InvocationOnMock.() -> Any?) =
fun whenever(mock: Unit) = Mockito.`when`(mock).thenAnswer { }
inline fun <reified T> spyThrowOnUnmocked(value: T?, block: T.() -> Unit): T {
inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit): T {
val swappingAnswer = object : Answer<Any?> {
var delegate: Answer<*> = Answers.RETURNS_DEFAULTS
@@ -73,12 +64,9 @@ inline fun <reified T> spyThrowOnUnmocked(value: T?, block: T.() -> Unit): T {
}
}
return Mockito.mock(T::class.java, Mockito.withSettings().spiedInstance(value)
.defaultAnswer(swappingAnswer)).apply(block)
return Mockito.mock(T::class.java, swappingAnswer).apply(block)
.also {
// To allow when() usage inside block, only swap to throwing afterwards
swappingAnswer.delegate = MockitoUtils.ANSWER_THROWS
}
}
inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit) = spyThrowOnUnmocked<T>(null, block)

View File

@@ -18,8 +18,6 @@ package com.android.server.om
import android.net.Uri
import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test

View File

@@ -31,12 +31,12 @@ import android.os.Debug
import android.os.Environment
import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.om.mockThrowOnUnmocked
import com.android.server.om.whenever
import com.android.server.pm.PackageManagerService
import com.android.server.pm.PackageSetting
import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.pm.pkg.PackageStateUnserialized
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.whenever
import org.junit.BeforeClass
import org.mockito.Mockito
import org.mockito.Mockito.anyInt

View File

@@ -139,7 +139,7 @@ public class TestHandler extends Handler {
}
}
public class MsgInfo implements Comparable<MsgInfo> {
private class MsgInfo implements Comparable<MsgInfo> {
public final Message message;
public final long sendTime;
public final RuntimeException postPoint;