Merge changes from topic "revert-10921255-revert-10403399-pkg-override-label-icon-DCRMJNYAKW-YCXMFEDIAG" into rvc-dev am: bd24ca3daf am: 0905856ed1
Change-Id: I2ee26cc3f22872e0e3777590876d8dd02f3e4723
This commit is contained in:
@@ -305,6 +305,35 @@ interface IPackageManager {
|
|||||||
|
|
||||||
void setHomeActivity(in ComponentName className, int userId);
|
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}.
|
* As per {@link android.content.pm.PackageManager#setComponentEnabledSetting}.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -27,18 +27,24 @@ 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_DISABLED_UNTIL_USED_COMPONENTS;
|
||||||
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
|
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.compat.annotation.UnsupportedAppUsage;
|
import android.compat.annotation.UnsupportedAppUsage;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.pm.parsing.component.ParsedMainComponent;
|
import android.content.pm.parsing.component.ParsedMainComponent;
|
||||||
import android.os.BaseBundle;
|
import android.os.BaseBundle;
|
||||||
import android.os.Debug;
|
import android.os.Debug;
|
||||||
import android.os.PersistableBundle;
|
import android.os.PersistableBundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.DebugUtils;
|
import android.util.DebugUtils;
|
||||||
|
import android.util.Pair;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
|
import com.android.internal.util.CollectionUtils;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
@@ -84,6 +90,9 @@ public class PackageUserState {
|
|||||||
private ArrayMap<String, String[]> sharedLibraryOverlayPaths; // Lib name to overlay paths
|
private ArrayMap<String, String[]> sharedLibraryOverlayPaths; // Lib name to overlay paths
|
||||||
private String[] cachedOverlayPaths;
|
private String[] cachedOverlayPaths;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private ArrayMap<ComponentName, Pair<String, Integer>> componentLabelIconOverrideMap;
|
||||||
|
|
||||||
@UnsupportedAppUsage
|
@UnsupportedAppUsage
|
||||||
public PackageUserState() {
|
public PackageUserState() {
|
||||||
installed = true;
|
installed = true;
|
||||||
@@ -123,6 +132,9 @@ public class PackageUserState {
|
|||||||
sharedLibraryOverlayPaths = new ArrayMap<>(o.sharedLibraryOverlayPaths);
|
sharedLibraryOverlayPaths = new ArrayMap<>(o.sharedLibraryOverlayPaths);
|
||||||
}
|
}
|
||||||
harmfulAppWarning = o.harmfulAppWarning;
|
harmfulAppWarning = o.harmfulAppWarning;
|
||||||
|
if (o.componentLabelIconOverrideMap != null) {
|
||||||
|
this.componentLabelIconOverrideMap = new ArrayMap<>(o.componentLabelIconOverrideMap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[] getOverlayPaths() {
|
public String[] getOverlayPaths() {
|
||||||
@@ -146,6 +158,65 @@ public class PackageUserState {
|
|||||||
cachedOverlayPaths = null;
|
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.
|
* Test if this package is installed.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1910,6 +1910,9 @@
|
|||||||
<!-- The name of the package that will hold the system gallery role. -->
|
<!-- The name of the package that will hold the system gallery role. -->
|
||||||
<string name="config_systemGallery" translatable="false">com.android.gallery</string>
|
<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:
|
<!-- Enable/disable default bluetooth profiles:
|
||||||
HSP_AG, ObexObjectPush, Audio, NAP -->
|
HSP_AG, ObexObjectPush, Audio, NAP -->
|
||||||
<bool name="config_bluetooth_default_profiles">true</bool>
|
<bool name="config_bluetooth_default_profiles">true</bool>
|
||||||
|
|||||||
@@ -3961,4 +3961,6 @@
|
|||||||
|
|
||||||
<!-- Set to true to make assistant show in front of the dream/screensaver. -->
|
<!-- Set to true to make assistant show in front of the dream/screensaver. -->
|
||||||
<java-symbol type="bool" name="config_assistantOnTopOfDream"/>
|
<java-symbol type="bool" name="config_assistantOnTopOfDream"/>
|
||||||
|
|
||||||
|
<java-symbol type="string" name="config_overrideComponentUiPackage" />
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ import android.util.Pair;
|
|||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
import com.android.server.IntentResolver;
|
import com.android.server.IntentResolver;
|
||||||
import com.android.server.pm.parsing.PackageInfoUtils;
|
import com.android.server.pm.parsing.PackageInfoUtils;
|
||||||
@@ -207,33 +208,57 @@ public class ComponentResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the given activity */
|
/** Returns the given activity */
|
||||||
ParsedActivity getActivity(ComponentName component) {
|
@Nullable
|
||||||
|
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||||
|
public ParsedActivity getActivity(@NonNull ComponentName component) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
return mActivities.mActivities.get(component);
|
return mActivities.mActivities.get(component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the given provider */
|
/** Returns the given provider */
|
||||||
ParsedProvider getProvider(ComponentName component) {
|
@Nullable
|
||||||
|
ParsedProvider getProvider(@NonNull ComponentName component) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
return mProviders.mProviders.get(component);
|
return mProviders.mProviders.get(component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the given receiver */
|
/** Returns the given receiver */
|
||||||
ParsedActivity getReceiver(ComponentName component) {
|
@Nullable
|
||||||
|
ParsedActivity getReceiver(@NonNull ComponentName component) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
return mReceivers.mActivities.get(component);
|
return mReceivers.mActivities.get(component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the given service */
|
/** Returns the given service */
|
||||||
ParsedService getService(ComponentName component) {
|
@Nullable
|
||||||
|
ParsedService getService(@NonNull ComponentName component) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
return mServices.mServices.get(component);
|
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
|
@Nullable
|
||||||
List<ResolveInfo> queryActivities(Intent intent, String resolvedType, int flags,
|
List<ResolveInfo> queryActivities(Intent intent, String resolvedType, int flags,
|
||||||
@PrivateResolveFlags int privateResolveFlags, int userId) {
|
@PrivateResolveFlags int privateResolveFlags, int userId) {
|
||||||
|
|||||||
@@ -674,7 +674,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
|
|
||||||
final ServiceThread mHandlerThread;
|
final ServiceThread mHandlerThread;
|
||||||
|
|
||||||
final PackageHandler mHandler;
|
final Handler mHandler;
|
||||||
|
|
||||||
private final ProcessLoggingHandler mProcessLoggingHandler;
|
private final ProcessLoggingHandler mProcessLoggingHandler;
|
||||||
|
|
||||||
@@ -1033,6 +1033,62 @@ 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 PackageParser2.Callback packageParserCallback;
|
||||||
|
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;
|
private final AppsFilter mAppsFilter;
|
||||||
|
|
||||||
final PackageParser2.Callback mPackageParserCallback;
|
final PackageParser2.Callback mPackageParserCallback;
|
||||||
@@ -1396,7 +1452,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set of pending broadcasts for aggregating enable/disable of components.
|
// Set of pending broadcasts for aggregating enable/disable of components.
|
||||||
static class PendingPackageBroadcasts {
|
@VisibleForTesting(visibility = Visibility.PACKAGE)
|
||||||
|
public static class PendingPackageBroadcasts {
|
||||||
// for each user id, a map of <package name -> components within that package>
|
// for each user id, a map of <package name -> components within that package>
|
||||||
final SparseArray<ArrayMap<String, ArrayList<String>>> mUidMap;
|
final SparseArray<ArrayMap<String, ArrayList<String>>> mUidMap;
|
||||||
|
|
||||||
@@ -1459,7 +1516,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final PendingPackageBroadcasts mPendingBroadcasts = new PendingPackageBroadcasts();
|
final PendingPackageBroadcasts mPendingBroadcasts;
|
||||||
|
|
||||||
static final int SEND_PENDING_BROADCAST = 1;
|
static final int SEND_PENDING_BROADCAST = 1;
|
||||||
static final int INIT_COPY = 5;
|
static final int INIT_COPY = 5;
|
||||||
@@ -2676,6 +2733,82 @@ 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;
|
||||||
|
mPackageParserCallback = testParams.packageParserCallback;
|
||||||
|
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) {
|
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
|
||||||
PackageManager.invalidatePackageInfoCache();
|
PackageManager.invalidatePackageInfoCache();
|
||||||
PackageManager.disableApplicationInfoCache();
|
PackageManager.disableApplicationInfoCache();
|
||||||
@@ -2683,6 +2816,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
|
|
||||||
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
|
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
|
||||||
Trace.TRACE_TAG_PACKAGE_MANAGER);
|
Trace.TRACE_TAG_PACKAGE_MANAGER);
|
||||||
|
mPendingBroadcasts = new PendingPackageBroadcasts();
|
||||||
|
|
||||||
mInjector = injector;
|
mInjector = injector;
|
||||||
mInjector.bootstrap(this);
|
mInjector.bootstrap(this);
|
||||||
mLock = injector.getLock();
|
mLock = injector.getLock();
|
||||||
@@ -5168,7 +5303,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
|
|
||||||
AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
|
AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
|
||||||
if (pkg != null && mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) {
|
if (pkg != null && mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) {
|
||||||
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
|
PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
|
||||||
if (ps == null) return null;
|
if (ps == null) return null;
|
||||||
if (shouldFilterApplicationLocked(
|
if (shouldFilterApplicationLocked(
|
||||||
ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
|
ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
|
||||||
@@ -15609,6 +15744,12 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
// these install state changes will be persisted in the
|
// these install state changes will be persisted in the
|
||||||
// upcoming call to mSettings.writeLPr().
|
// upcoming call to mSettings.writeLPr().
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (allUsers != null) {
|
||||||
|
for (int currentUserId : allUsers) {
|
||||||
|
ps.resetOverrideComponentLabelIcon(currentUserId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the overlays for shared libraries of the package.
|
// Retrieve the overlays for shared libraries of the package.
|
||||||
@@ -20176,6 +20317,86 @@ 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
|
@Override
|
||||||
public void setComponentEnabledSetting(ComponentName componentName,
|
public void setComponentEnabledSetting(ComponentName componentName,
|
||||||
int newState, int flags, int userId) {
|
int newState, int flags, int userId) {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import android.util.ArrayMap;
|
|||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.proto.ProtoOutputStream;
|
import android.util.proto.ProtoOutputStream;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||||
import com.android.server.pm.permission.PermissionsState;
|
import com.android.server.pm.permission.PermissionsState;
|
||||||
import com.android.server.pm.pkg.PackageStateUnserialized;
|
import com.android.server.pm.pkg.PackageStateUnserialized;
|
||||||
@@ -68,7 +69,8 @@ public class PackageSetting extends PackageSettingBase {
|
|||||||
@NonNull
|
@NonNull
|
||||||
private PackageStateUnserialized pkgState = new PackageStateUnserialized();
|
private PackageStateUnserialized pkgState = new PackageStateUnserialized();
|
||||||
|
|
||||||
PackageSetting(String name, String realName, File codePath, File resourcePath,
|
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||||
|
public PackageSetting(String name, String realName, File codePath, File resourcePath,
|
||||||
String legacyNativeLibraryPathString, String primaryCpuAbiString,
|
String legacyNativeLibraryPathString, String primaryCpuAbiString,
|
||||||
String secondaryCpuAbiString, String cpuAbiOverrideString,
|
String secondaryCpuAbiString, String cpuAbiOverrideString,
|
||||||
long pVersionCode, int pkgFlags, int privateFlags,
|
long pVersionCode, int pkgFlags, int privateFlags,
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|
|||||||
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
|
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
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.ApplicationInfo;
|
||||||
import android.content.pm.IntentFilterVerificationInfo;
|
import android.content.pm.IntentFilterVerificationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
@@ -697,6 +700,26 @@ public abstract class PackageSettingBase extends SettingBase {
|
|||||||
return userState.harmfulAppWarning;
|
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) {
|
protected PackageSettingBase updateFrom(PackageSettingBase other) {
|
||||||
super.copyFrom(other);
|
super.copyFrom(other);
|
||||||
this.codePath = other.codePath;
|
this.codePath = other.codePath;
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ package com.android.server.pm;
|
|||||||
|
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.server.pm.permission.PermissionsState;
|
import com.android.server.pm.permission.PermissionsState;
|
||||||
|
|
||||||
abstract class SettingBase {
|
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||||
|
public abstract class SettingBase {
|
||||||
int pkgFlags;
|
int pkgFlags;
|
||||||
int pkgPrivateFlags;
|
int pkgPrivateFlags;
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ import android.util.Xml;
|
|||||||
import android.util.proto.ProtoOutputStream;
|
import android.util.proto.ProtoOutputStream;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.os.BackgroundThread;
|
import com.android.internal.os.BackgroundThread;
|
||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
import com.android.internal.util.CollectionUtils;
|
import com.android.internal.util.CollectionUtils;
|
||||||
@@ -418,6 +419,21 @@ public final class Settings {
|
|||||||
/** Settings and other information about permissions */
|
/** Settings and other information about permissions */
|
||||||
final PermissionSettings mPermissions;
|
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,
|
Settings(File dataDir, PermissionSettings permission,
|
||||||
Object lock) {
|
Object lock) {
|
||||||
mLock = lock;
|
mLock = lock;
|
||||||
@@ -4328,8 +4344,9 @@ public final class Settings {
|
|||||||
return userState.isMatch(componentInfo, flags);
|
return userState.isMatch(componentInfo, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isEnabledAndMatchLPr(AndroidPackage pkg, ParsedMainComponent component, int flags,
|
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||||
int userId) {
|
public boolean isEnabledAndMatchLPr(AndroidPackage pkg, ParsedMainComponent component,
|
||||||
|
int flags, int userId) {
|
||||||
final PackageSetting ps = mPackages.get(component.getPackageName());
|
final PackageSetting ps = mPackages.get(component.getPackageName());
|
||||||
if (ps == null) return false;
|
if (ps == null) return false;
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import android.content.pm.parsing.component.ParsedService;
|
|||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
|
import android.util.Pair;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
@@ -271,7 +272,7 @@ public class PackageInfoUtils {
|
|||||||
|
|
||||||
ActivityInfo info =
|
ActivityInfo info =
|
||||||
PackageInfoWithoutStateUtils.generateActivityInfoUnchecked(a, applicationInfo);
|
PackageInfoWithoutStateUtils.generateActivityInfoUnchecked(a, applicationInfo);
|
||||||
assignSharedFieldsForComponentInfo(info, a, pkgSetting);
|
assignSharedFieldsForComponentInfo(info, a, pkgSetting, userId);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +307,7 @@ public class PackageInfoUtils {
|
|||||||
|
|
||||||
ServiceInfo info =
|
ServiceInfo info =
|
||||||
PackageInfoWithoutStateUtils.generateServiceInfoUnchecked(s, applicationInfo);
|
PackageInfoWithoutStateUtils.generateServiceInfoUnchecked(s, applicationInfo);
|
||||||
assignSharedFieldsForComponentInfo(info, s, pkgSetting);
|
assignSharedFieldsForComponentInfo(info, s, pkgSetting, userId);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,7 +334,7 @@ public class PackageInfoUtils {
|
|||||||
}
|
}
|
||||||
ProviderInfo info = PackageInfoWithoutStateUtils.generateProviderInfoUnchecked(p, flags,
|
ProviderInfo info = PackageInfoWithoutStateUtils.generateProviderInfoUnchecked(p, flags,
|
||||||
applicationInfo);
|
applicationInfo);
|
||||||
assignSharedFieldsForComponentInfo(info, p, pkgSetting);
|
assignSharedFieldsForComponentInfo(info, p, pkgSetting, userId);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +359,7 @@ public class PackageInfoUtils {
|
|||||||
info.nativeLibraryDir = pkg.getNativeLibraryDir();
|
info.nativeLibraryDir = pkg.getNativeLibraryDir();
|
||||||
info.secondaryNativeLibraryDir = pkg.getSecondaryNativeLibraryDir();
|
info.secondaryNativeLibraryDir = pkg.getSecondaryNativeLibraryDir();
|
||||||
|
|
||||||
assignStateFieldsForPackageItemInfo(info, i, pkgSetting);
|
assignStateFieldsForPackageItemInfo(info, i, pkgSetting, userId);
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
@@ -426,8 +427,9 @@ public class PackageInfoUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void assignSharedFieldsForComponentInfo(@NonNull ComponentInfo componentInfo,
|
private static void assignSharedFieldsForComponentInfo(@NonNull ComponentInfo componentInfo,
|
||||||
@NonNull ParsedMainComponent mainComponent, @Nullable PackageSetting pkgSetting) {
|
@NonNull ParsedMainComponent mainComponent, @Nullable PackageSetting pkgSetting,
|
||||||
assignStateFieldsForPackageItemInfo(componentInfo, mainComponent, pkgSetting);
|
int userId) {
|
||||||
|
assignStateFieldsForPackageItemInfo(componentInfo, mainComponent, pkgSetting, userId);
|
||||||
componentInfo.descriptionRes = mainComponent.getDescriptionRes();
|
componentInfo.descriptionRes = mainComponent.getDescriptionRes();
|
||||||
componentInfo.directBootAware = mainComponent.isDirectBootAware();
|
componentInfo.directBootAware = mainComponent.isDirectBootAware();
|
||||||
componentInfo.enabled = mainComponent.isEnabled();
|
componentInfo.enabled = mainComponent.isEnabled();
|
||||||
@@ -436,8 +438,12 @@ public class PackageInfoUtils {
|
|||||||
|
|
||||||
private static void assignStateFieldsForPackageItemInfo(
|
private static void assignStateFieldsForPackageItemInfo(
|
||||||
@NonNull PackageItemInfo packageItemInfo, @NonNull ParsedComponent component,
|
@NonNull PackageItemInfo packageItemInfo, @NonNull ParsedComponent component,
|
||||||
@Nullable PackageSetting pkgSetting) {
|
@Nullable PackageSetting pkgSetting, int userId) {
|
||||||
// TODO(b/135203078): Add setting related state
|
Pair<CharSequence, Integer> labelAndIcon =
|
||||||
|
ParsedComponentStateUtils.getNonLocalizedLabelAndIcon(component, pkgSetting,
|
||||||
|
userId);
|
||||||
|
packageItemInfo.nonLocalizedLabel = labelAndIcon.first;
|
||||||
|
packageItemInfo.icon = labelAndIcon.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
@CheckResult
|
@CheckResult
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// 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,
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?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>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?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.
|
After Width: | Height: | Size: 260 B |
Binary file not shown.
|
After Width: | Height: | Size: 262 B |
@@ -0,0 +1,21 @@
|
|||||||
|
<?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>
|
||||||
@@ -0,0 +1,358 @@
|
|||||||
|
/*
|
||||||
|
* 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,6 +107,8 @@ java_library {
|
|||||||
name: "servicestests-utils",
|
name: "servicestests-utils",
|
||||||
srcs: [
|
srcs: [
|
||||||
"utils/**/*.java",
|
"utils/**/*.java",
|
||||||
|
"utils/**/*.kt",
|
||||||
|
"utils-mockito/**/*.kt",
|
||||||
],
|
],
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"junit",
|
"junit",
|
||||||
@@ -117,6 +119,22 @@ 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 {
|
filegroup {
|
||||||
name: "servicestests-SuspendTestApp-files",
|
name: "servicestests-SuspendTestApp-files",
|
||||||
srcs: [
|
srcs: [
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ package com.android.server.om
|
|||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.android.server.pm.parsing.pkg.AndroidPackage
|
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 com.google.common.truth.Truth.assertThat
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ import android.os.Debug
|
|||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.util.SparseArray
|
import android.util.SparseArray
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
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.PackageManagerService
|
||||||
import com.android.server.pm.PackageSetting
|
import com.android.server.pm.PackageSetting
|
||||||
import com.android.server.pm.parsing.pkg.AndroidPackage
|
import com.android.server.pm.parsing.pkg.AndroidPackage
|
||||||
import com.android.server.pm.pkg.PackageStateUnserialized
|
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.junit.BeforeClass
|
||||||
import org.mockito.Mockito
|
import org.mockito.Mockito
|
||||||
import org.mockito.Mockito.anyInt
|
import org.mockito.Mockito.anyInt
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2019 The Android Open Source Project
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.server.om
|
package com.android.server.testutils
|
||||||
|
|
||||||
import org.mockito.Answers
|
import org.mockito.Answers
|
||||||
import org.mockito.Mockito
|
import org.mockito.Mockito
|
||||||
@@ -31,6 +31,13 @@ object MockitoUtils {
|
|||||||
else -> {
|
else -> {
|
||||||
val arguments = it.arguments
|
val arguments = it.arguments
|
||||||
?.takeUnless { it.isEmpty() }
|
?.takeUnless { it.isEmpty() }
|
||||||
|
?.mapIndexed { index, arg ->
|
||||||
|
try {
|
||||||
|
arg?.toString()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
"toString[$index] threw ${e.message}"
|
||||||
|
}
|
||||||
|
}
|
||||||
?.joinToString()
|
?.joinToString()
|
||||||
?.let {
|
?.let {
|
||||||
"with $it"
|
"with $it"
|
||||||
@@ -46,6 +53,8 @@ object MockitoUtils {
|
|||||||
|
|
||||||
inline fun <reified T> mock(block: T.() -> Unit = {}) = Mockito.mock(T::class.java).apply(block)
|
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> Stubber.whenever(mock: Type) = Mockito.`when`(mock)
|
||||||
fun <Type : Any?> whenever(mock: Type) = Mockito.`when`(mock)
|
fun <Type : Any?> whenever(mock: Type) = Mockito.`when`(mock)
|
||||||
|
|
||||||
@@ -55,7 +64,7 @@ fun <Type : Any?> whenever(mock: Type, block: InvocationOnMock.() -> Any?) =
|
|||||||
|
|
||||||
fun whenever(mock: Unit) = Mockito.`when`(mock).thenAnswer { }
|
fun whenever(mock: Unit) = Mockito.`when`(mock).thenAnswer { }
|
||||||
|
|
||||||
inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit): T {
|
inline fun <reified T> spyThrowOnUnmocked(value: T?, block: T.() -> Unit): T {
|
||||||
val swappingAnswer = object : Answer<Any?> {
|
val swappingAnswer = object : Answer<Any?> {
|
||||||
var delegate: Answer<*> = Answers.RETURNS_DEFAULTS
|
var delegate: Answer<*> = Answers.RETURNS_DEFAULTS
|
||||||
|
|
||||||
@@ -64,9 +73,12 @@ inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit): T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Mockito.mock(T::class.java, swappingAnswer).apply(block)
|
return Mockito.mock(T::class.java, Mockito.withSettings().spiedInstance(value)
|
||||||
|
.defaultAnswer(swappingAnswer)).apply(block)
|
||||||
.also {
|
.also {
|
||||||
// To allow when() usage inside block, only swap to throwing afterwards
|
// To allow when() usage inside block, only swap to throwing afterwards
|
||||||
swappingAnswer.delegate = MockitoUtils.ANSWER_THROWS
|
swappingAnswer.delegate = MockitoUtils.ANSWER_THROWS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit) = spyThrowOnUnmocked<T>(null, block)
|
||||||
@@ -139,7 +139,7 @@ public class TestHandler extends Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MsgInfo implements Comparable<MsgInfo> {
|
public class MsgInfo implements Comparable<MsgInfo> {
|
||||||
public final Message message;
|
public final Message message;
|
||||||
public final long sendTime;
|
public final long sendTime;
|
||||||
public final RuntimeException postPoint;
|
public final RuntimeException postPoint;
|
||||||
|
|||||||
Reference in New Issue
Block a user