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:
committed by
Android (Google) Code Review
commit
b69f2bc126
@@ -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}.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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 |
@@ -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>
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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: [
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user