Revert "Revert "Overlay, actor, and target app visibility handling""

This reverts commit 637138dd94.

Exempt-From-Owner-Approval: Revert of revert, all necessary review will be in follow up

Reason for revert: Revert of revert for re-merge, follow up will fix test conflict

Change-Id: I6c5c3209d1fdb62cb6f733961ee8737dea4ca89b
This commit is contained in:
Winson Chiu
2019-12-13 20:01:15 +00:00
committed by Winson
parent b03866ee72
commit 2fdaf81c7a
16 changed files with 1025 additions and 118 deletions

View File

@@ -229,6 +229,11 @@ public interface AndroidPackage extends Parcelable {
String getOverlayTargetName();
/**
* Map of overlayable name to actor name.
*/
Map<String, String> getOverlayables();
// TODO(b/135203078): Does this and getAppInfoPackageName have to be separate methods?
// The refactor makes them the same value with no known consequences, so should be redundant.
String getPackageName();

View File

@@ -52,6 +52,7 @@ import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
import android.content.pm.split.SplitAssetLoader;
import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -92,6 +93,7 @@ import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** @hide */
@@ -287,8 +289,23 @@ public class ApkParseUtils {
+ result.getErrorMessage());
}
return result.getResultAndNull()
.setVolumeUuid(volumeUuid)
ParsingPackage pkg = result.getResultAndNull();
ApkAssets apkAssets = assets.getApkAssets()[0];
if (apkAssets.definesOverlayable()) {
SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
int size = packageNames.size();
for (int index = 0; index < size; index++) {
String packageName = packageNames.get(index);
Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
for (String overlayable : overlayableToActor.keySet()) {
pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
}
}
}
}
return pkg.setVolumeUuid(volumeUuid)
.setApplicationVolumeUuid(volumeUuid)
.setSigningDetails(SigningDetails.UNKNOWN);
} catch (PackageParserException e) {

View File

@@ -18,6 +18,8 @@ package android.content.pm.parsing;
import static android.os.Build.VERSION_CODES.DONUT;
import static java.util.Collections.emptyMap;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
@@ -55,11 +57,13 @@ import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.server.SystemConfig;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -126,6 +130,7 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
private String overlayCategory;
private int overlayPriority;
private boolean overlayIsStatic;
private Map<String, String> overlayables = emptyMap();
private String staticSharedLibName;
private long staticSharedLibVersion;
@@ -475,7 +480,7 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
@Override
public Map<String, ArraySet<PublicKey>> getKeySetMapping() {
return keySetMapping == null ? Collections.emptyMap() : keySetMapping;
return keySetMapping == null ? emptyMap() : keySetMapping;
}
@Override
@@ -772,6 +777,13 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
return this;
}
@Override
public ParsingPackage addOverlayable(String overlayableName, String actorName) {
this.overlayables = CollectionUtils.add(this.overlayables,
TextUtils.safeIntern(overlayableName), TextUtils.safeIntern(actorName));
return this;
}
@Override
public PackageImpl addAdoptPermission(String adoptPermission) {
this.adoptPermissions = ArrayUtils.add(this.adoptPermissions, adoptPermission);
@@ -2124,6 +2136,11 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
return overlayTargetName;
}
@Override
public Map<String, String> getOverlayables() {
return overlayables;
}
@Override
public boolean isOverlayIsStatic() {
return overlayIsStatic;
@@ -2291,7 +2308,7 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
appInfo.metaData = appMetaData;
appInfo.minAspectRatio = minAspectRatio;
appInfo.minSdkVersion = minSdkVersion;
appInfo.name = name;
appInfo.name = className;
if (appInfo.name != null) {
appInfo.name = appInfo.name.trim();
}
@@ -2957,6 +2974,7 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
dest.writeString(this.overlayCategory);
dest.writeInt(this.overlayPriority);
dest.writeBoolean(this.overlayIsStatic);
dest.writeMap(this.overlayables);
dest.writeString(this.staticSharedLibName);
dest.writeLong(this.staticSharedLibVersion);
dest.writeStringList(this.libraryNames);
@@ -3100,6 +3118,8 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
this.overlayCategory = in.readString();
this.overlayPriority = in.readInt();
this.overlayIsStatic = in.readBoolean();
this.overlayables = new HashMap<>();
in.readMap(overlayables, boot);
this.staticSharedLibName = TextUtils.safeIntern(in.readString());
this.staticSharedLibVersion = in.readLong();
this.libraryNames = in.createStringArrayList();

View File

@@ -62,6 +62,8 @@ public interface ParsingPackage extends AndroidPackage {
ParsingPackage addOriginalPackage(String originalPackage);
ParsingPackage addOverlayable(String overlayableName, String actorName);
ParsingPackage addPermission(ParsedPermission permission);
ParsingPackage addPermissionGroup(ParsedPermissionGroup permissionGroup);

View File

@@ -307,6 +307,17 @@ public class CollectionUtils {
return cur;
}
/**
* @see #add(List, Object)
*/
public static @NonNull <K, V> Map<K, V> add(@Nullable Map<K, V> map, K key, V value) {
if (map == null || map == Collections.emptyMap()) {
map = new ArrayMap<>();
}
map.put(key, value);
return map;
}
/**
* Similar to {@link List#remove}, but with support for list values of {@code null} and
* {@link Collections#emptyList}

View File

@@ -226,7 +226,7 @@ public class SystemConfig {
* Map of system pre-defined, uniquely named actors; keys are namespace,
* value maps actor name to package name.
*/
private ArrayMap<String, ArrayMap<String, String>> mNamedActors = null;
private Map<String, Map<String, String>> mNamedActors = null;
public static SystemConfig getInstance() {
if (!isSystemProcess()) {
@@ -406,7 +406,7 @@ public class SystemConfig {
}
@NonNull
public Map<String, ? extends Map<String, String>> getNamedActors() {
public Map<String, Map<String, String>> getNamedActors() {
return mNamedActors != null ? mNamedActors : Collections.emptyMap();
}
@@ -1063,7 +1063,7 @@ public class SystemConfig {
mNamedActors = new ArrayMap<>();
}
ArrayMap<String, String> nameToPkgMap = mNamedActors.get(namespace);
Map<String, String> nameToPkgMap = mNamedActors.get(namespace);
if (nameToPkgMap == null) {
nameToPkgMap = new ArrayMap<>();
mNamedActors.put(namespace, nameToPkgMap);

View File

@@ -26,6 +26,7 @@ import android.net.Uri;
import android.os.Process;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Pair;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -44,6 +45,38 @@ public class OverlayActorEnforcer {
private final VerifyCallback mVerifyCallback;
/**
* @return nullable actor result with {@link ActorState} failure status
*/
static Pair<String, ActorState> getPackageNameForActor(String actorUriString,
Map<String, Map<String, String>> namedActors) {
if (namedActors.isEmpty()) {
return Pair.create(null, ActorState.NO_NAMED_ACTORS);
}
Uri actorUri = Uri.parse(actorUriString);
String actorScheme = actorUri.getScheme();
List<String> actorPathSegments = actorUri.getPathSegments();
if (!"overlay".equals(actorScheme) || CollectionUtils.size(actorPathSegments) != 1) {
return Pair.create(null, ActorState.INVALID_OVERLAYABLE_ACTOR_NAME);
}
String actorNamespace = actorUri.getAuthority();
Map<String, String> namespace = namedActors.get(actorNamespace);
if (namespace == null) {
return Pair.create(null, ActorState.MISSING_NAMESPACE);
}
String actorName = actorPathSegments.get(0);
String packageName = namespace.get(actorName);
if (TextUtils.isEmpty(packageName)) {
return Pair.create(null, ActorState.MISSING_ACTOR_NAME);
}
return Pair.create(packageName, ActorState.ALLOWED);
}
public OverlayActorEnforcer(@NonNull VerifyCallback verifyCallback) {
mVerifyCallback = verifyCallback;
}
@@ -141,31 +174,14 @@ public class OverlayActorEnforcer {
}
}
Map<String, ? extends Map<String, String>> namedActors = mVerifyCallback.getNamedActors();
if (namedActors.isEmpty()) {
return ActorState.NO_NAMED_ACTORS;
}
Uri actorUri = Uri.parse(actor);
String actorScheme = actorUri.getScheme();
List<String> actorPathSegments = actorUri.getPathSegments();
if (!"overlay".equals(actorScheme) || CollectionUtils.size(actorPathSegments) != 1) {
return ActorState.INVALID_OVERLAYABLE_ACTOR_NAME;
}
String actorNamespace = actorUri.getAuthority();
Map<String, String> namespace = namedActors.get(actorNamespace);
if (namespace == null) {
return ActorState.MISSING_NAMESPACE;
}
String actorName = actorPathSegments.get(0);
String packageName = namespace.get(actorName);
if (TextUtils.isEmpty(packageName)) {
return ActorState.MISSING_ACTOR_NAME;
Map<String, Map<String, String>> namedActors = mVerifyCallback.getNamedActors();
Pair<String, ActorState> actorUriPair = getPackageNameForActor(actor, namedActors);
ActorState actorUriState = actorUriPair.second;
if (actorUriState != ActorState.ALLOWED) {
return actorUriState;
}
String packageName = actorUriPair.first;
PackageInfo packageInfo = mVerifyCallback.getPackageInfo(packageName, userId);
if (packageInfo == null) {
return ActorState.MISSING_APP_INFO;
@@ -192,7 +208,7 @@ public class OverlayActorEnforcer {
* For easier logging/debugging, a set of all possible failure/success states when running
* enforcement.
*/
private enum ActorState {
enum ActorState {
ALLOWED,
INVALID_ACTOR,
MISSING_NAMESPACE,
@@ -244,7 +260,7 @@ public class OverlayActorEnforcer {
* value maps actor name to package name
*/
@NonNull
Map<String, ? extends Map<String, String>> getNamedActors();
Map<String, Map<String, String>> getNamedActors();
/**
* @return true if the target package has declared an overlayable

View File

@@ -1073,8 +1073,6 @@ public final class OverlayManagerService extends SystemService {
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
}
// TODO(b/143096091): Remove PackageInfo cache so that PackageManager is always queried
// to enforce visibility/other permission checks
public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId,
final boolean useCache) {
if (useCache) {
@@ -1097,18 +1095,12 @@ public final class OverlayManagerService extends SystemService {
@Override
public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId) {
// TODO(b/143096091): Remove clearing calling ID
long callingIdentity = Binder.clearCallingIdentity();
try {
return getPackageInfo(packageName, userId, true);
} finally {
Binder.restoreCallingIdentity(callingIdentity);
}
return getPackageInfo(packageName, userId, true);
}
@NonNull
@Override
public Map<String, ? extends Map<String, String>> getNamedActors() {
public Map<String, Map<String, String>> getNamedActors() {
return SystemConfig.getInstance().getNamedActors();
}
@@ -1136,57 +1128,45 @@ public final class OverlayManagerService extends SystemService {
public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
@Nullable String targetOverlayableName, int userId)
throws IOException {
// TODO(b/143096091): Remove clearing calling ID
long callingIdentity = Binder.clearCallingIdentity();
PackageInfo packageInfo = getPackageInfo(packageName, userId);
if (packageInfo == null) {
throw new IOException("Unable to get target package");
}
String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
ApkAssets apkAssets = null;
try {
PackageInfo packageInfo = getPackageInfo(packageName, userId);
if (packageInfo == null) {
throw new IOException("Unable to get target package");
}
String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
ApkAssets apkAssets = null;
try {
apkAssets = ApkAssets.loadFromPath(baseCodePath);
return apkAssets.getOverlayableInfo(targetOverlayableName);
} finally {
if (apkAssets != null) {
try {
apkAssets.close();
} catch (Throwable ignored) {
}
apkAssets = ApkAssets.loadFromPath(baseCodePath);
return apkAssets.getOverlayableInfo(targetOverlayableName);
} finally {
if (apkAssets != null) {
try {
apkAssets.close();
} catch (Throwable ignored) {
}
}
} finally {
Binder.restoreCallingIdentity(callingIdentity);
}
}
@Override
public boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
throws RemoteException, IOException {
// TODO(b/143096091): Remove clearing calling ID
long callingIdentity = Binder.clearCallingIdentity();
try {
PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, 0,
userId);
String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, 0,
userId);
String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
ApkAssets apkAssets = null;
try {
apkAssets = ApkAssets.loadFromPath(baseCodePath);
return apkAssets.definesOverlayable();
} finally {
if (apkAssets != null) {
try {
apkAssets.close();
} catch (Throwable ignored) {
}
ApkAssets apkAssets = null;
try {
apkAssets = ApkAssets.loadFromPath(baseCodePath);
return apkAssets.definesOverlayable();
} finally {
if (apkAssets != null) {
try {
apkAssets.close();
} catch (Throwable ignored) {
}
}
} finally {
Binder.restoreCallingIdentity(callingIdentity);
}
}
@@ -1229,16 +1209,10 @@ public final class OverlayManagerService extends SystemService {
@Nullable
@Override
public String[] getPackagesForUid(int uid) {
// TODO(b/143096091): Remove clearing calling ID
long callingIdentity = Binder.clearCallingIdentity();
try {
try {
return mPackageManager.getPackagesForUid(uid);
} catch (RemoteException ignored) {
return null;
}
} finally {
Binder.restoreCallingIdentity(callingIdentity);
return mPackageManager.getPackagesForUid(uid);
} catch (RemoteException ignored) {
return null;
}
}

View File

@@ -0,0 +1,375 @@
/*
* 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.
*/
package com.android.server.om;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.parsing.AndroidPackage;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.CollectionUtils;
import com.android.server.SystemConfig;
import com.android.server.pm.PackageSetting;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Track visibility of a targets and overlays to actors.
*
* 4 cases to handle:
* <ol>
* <li>Target adds/changes an overlayable to add a reference to an actor
* <ul>
* <li>Must expose target to actor</li>
* <li>Must expose any overlays that pointed to that overlayable name to the actor</li>
* </ul>
* </li>
* <li>Target removes/changes an overlayable to remove a reference to an actor
* <ul>
* <li>If this target has no other overlayables referencing the actor, hide the
* target</li>
* <li>For all overlays targeting this overlayable, if the overlay is only visible to
* the actor through this overlayable, hide the overlay</li>
* </ul>
* </li>
* <li>Overlay adds/changes an overlay tag to add a reference to an overlayable name
* <ul>
* <li>Expose this overlay to the actor defined by the target overlayable</li>
* </ul>
* </li>
* <li>Overlay removes/changes an overlay tag to remove a reference to an overlayable name
* <ul>
* <li>If this overlay is only visible to an actor through this overlayable name's
* target's actor</li>
* </ul>
* </li>
* </ol>
*
* In this class, the names "actor", "target", and "overlay" all refer to the ID representations.
* All other use cases are named appropriate. "actor" is actor name, "target" is target package
* name, and "overlay" is overlay package name.
*/
public class OverlayReferenceMapper {
private final Object mLock = new Object();
/**
* Keys are actors, values are maps which map target to a set of overlays targeting it.
* The presence of a target in the value map means the actor and targets are connected, even
* if the corresponding target's set is empty.
* See class comment for specific types.
*/
@GuardedBy("mLock")
private final Map<String, Map<String, Set<String>>> mActorToTargetToOverlays = new HashMap<>();
/**
* Keys are actor package names, values are generic package names the actor should be able
* to see.
*/
@GuardedBy("mLock")
private final Map<String, Set<String>> mActorPkgToPkgs = new HashMap<>();
@GuardedBy("mLock")
private boolean mDeferRebuild;
@NonNull
private final Provider mProvider;
/**
* @param deferRebuild whether or not to defer rebuild calls on add/remove until first get call;
* useful during boot when multiple packages are added in rapid succession
* and queries in-between are not expected
*/
public OverlayReferenceMapper(boolean deferRebuild, @Nullable Provider provider) {
this.mDeferRebuild = deferRebuild;
this.mProvider = provider != null ? provider : new Provider() {
@Nullable
@Override
public String getActorPkg(String actor) {
Map<String, Map<String, String>> namedActors = SystemConfig.getInstance()
.getNamedActors();
Pair<String, OverlayActorEnforcer.ActorState> actorPair =
OverlayActorEnforcer.getPackageNameForActor(actor, namedActors);
return actorPair.first;
}
@NonNull
@Override
public Map<String, Set<String>> getTargetToOverlayables(@NonNull AndroidPackage pkg) {
String target = pkg.getOverlayTarget();
if (TextUtils.isEmpty(target)) {
return Collections.emptyMap();
}
String overlayable = pkg.getOverlayTargetName();
Map<String, Set<String>> targetToOverlayables = new HashMap<>();
Set<String> overlayables = new HashSet<>();
overlayables.add(overlayable);
targetToOverlayables.put(target, overlayables);
return targetToOverlayables;
}
};
}
/**
* @return mapping of actor package to a set of packages it can view
*/
@NonNull
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public Map<String, Set<String>> getActorPkgToPkgs() {
return mActorPkgToPkgs;
}
public boolean isValidActor(@NonNull String targetName, @NonNull String actorPackageName) {
synchronized (mLock) {
assertMapBuilt();
Set<String> validSet = mActorPkgToPkgs.get(actorPackageName);
return validSet != null && validSet.contains(targetName);
}
}
/**
* Add a package to be considered for visibility. Currently supports adding as a target and/or
* an overlay. Adding an actor is not supported. Those are configured as part of
* {@link SystemConfig#getNamedActors()}.
*
* @param pkg the package to add
* @param otherPkgs map of other packages to consider, excluding {@param pkg}
*/
public void addPkg(AndroidPackage pkg, Map<String, AndroidPackage> otherPkgs) {
synchronized (mLock) {
if (!pkg.getOverlayables().isEmpty()) {
addTarget(pkg, otherPkgs);
}
// TODO(b/135203078): Replace with isOverlay boolean flag check; fix test mocks
if (!mProvider.getTargetToOverlayables(pkg).isEmpty()) {
addOverlay(pkg, otherPkgs);
}
if (!mDeferRebuild) {
rebuild();
}
}
}
/**
* Removes a package to be considered for visibility. Currently supports removing as a target
* and/or an overlay. Removing an actor is not supported. Those are staticly configured as part
* of {@link SystemConfig#getNamedActors()}.
*
* @param pkgName name to remove, as was added through {@link #addPkg(AndroidPackage, Map)}
*/
public void removePkg(String pkgName) {
synchronized (mLock) {
removeTarget(pkgName);
removeOverlay(pkgName);
if (!mDeferRebuild) {
rebuild();
}
}
}
private void removeTarget(String target) {
synchronized (mLock) {
Iterator<Map<String, Set<String>>> iterator =
mActorToTargetToOverlays.values().iterator();
while (iterator.hasNext()) {
Map<String, Set<String>> next = iterator.next();
next.remove(target);
if (next.isEmpty()) {
iterator.remove();
}
}
}
}
/**
* Associate an actor with an association of a new target to overlays for that target.
*
* If a target overlays itself, it will not be associated with itself, as only one half of the
* relationship needs to exist for visibility purposes.
*/
private void addTarget(AndroidPackage targetPkg, Map<String, AndroidPackage> otherPkgs) {
synchronized (mLock) {
String target = targetPkg.getPackageName();
removeTarget(target);
Map<String, String> overlayablesToActors = targetPkg.getOverlayables();
for (String overlayable : overlayablesToActors.keySet()) {
String actor = overlayablesToActors.get(overlayable);
addTargetToMap(actor, target);
for (AndroidPackage overlayPkg : otherPkgs.values()) {
Map<String, Set<String>> targetToOverlayables =
mProvider.getTargetToOverlayables(overlayPkg);
Set<String> overlayables = targetToOverlayables.get(target);
if (CollectionUtils.isEmpty(overlayables)) {
continue;
}
if (overlayables.contains(overlayable)) {
addOverlayToMap(actor, target, overlayPkg.getPackageName());
}
}
}
}
}
private void removeOverlay(String overlay) {
synchronized (mLock) {
for (Map<String, Set<String>> targetToOverlays : mActorToTargetToOverlays.values()) {
for (Set<String> overlays : targetToOverlays.values()) {
overlays.remove(overlay);
}
}
}
}
/**
* Associate an actor with an association of targets to overlays for a new overlay.
*
* If an overlay targets itself, it will not be associated with itself, as only one half of the
* relationship needs to exist for visibility purposes.
*/
private void addOverlay(AndroidPackage overlayPkg, Map<String, AndroidPackage> otherPkgs) {
synchronized (mLock) {
String overlay = overlayPkg.getPackageName();
removeOverlay(overlay);
Map<String, Set<String>> targetToOverlayables =
mProvider.getTargetToOverlayables(overlayPkg);
for (Map.Entry<String, Set<String>> entry : targetToOverlayables.entrySet()) {
String target = entry.getKey();
Set<String> overlayables = entry.getValue();
AndroidPackage targetPkg = otherPkgs.get(target);
if (targetPkg == null) {
continue;
}
String targetPkgName = targetPkg.getPackageName();
Map<String, String> overlayableToActor = targetPkg.getOverlayables();
for (String overlayable : overlayables) {
String actor = overlayableToActor.get(overlayable);
if (TextUtils.isEmpty(actor)) {
continue;
}
addOverlayToMap(actor, targetPkgName, overlay);
}
}
}
}
public void rebuildIfDeferred() {
synchronized (mLock) {
if (mDeferRebuild) {
rebuild();
mDeferRebuild = false;
}
}
}
private void assertMapBuilt() {
if (mDeferRebuild) {
throw new IllegalStateException("The actor map must be built by calling "
+ "rebuildIfDeferred before it is queried");
}
}
private void rebuild() {
synchronized (mLock) {
mActorPkgToPkgs.clear();
for (String actor : mActorToTargetToOverlays.keySet()) {
String actorPkg = mProvider.getActorPkg(actor);
if (TextUtils.isEmpty(actorPkg)) {
continue;
}
Map<String, Set<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
Set<String> pkgs = new HashSet<>();
for (String target : targetToOverlays.keySet()) {
Set<String> overlays = targetToOverlays.get(target);
pkgs.add(target);
pkgs.addAll(overlays);
}
mActorPkgToPkgs.put(actorPkg, pkgs);
}
}
}
private void addTargetToMap(String actor, String target) {
Map<String, Set<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
if (targetToOverlays == null) {
targetToOverlays = new HashMap<>();
mActorToTargetToOverlays.put(actor, targetToOverlays);
}
Set<String> overlays = targetToOverlays.get(target);
if (overlays == null) {
overlays = new HashSet<>();
targetToOverlays.put(target, overlays);
}
}
private void addOverlayToMap(String actor, String target, String overlay) {
synchronized (mLock) {
Map<String, Set<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
if (targetToOverlays == null) {
targetToOverlays = new HashMap<>();
mActorToTargetToOverlays.put(actor, targetToOverlays);
}
Set<String> overlays = targetToOverlays.get(target);
if (overlays == null) {
overlays = new HashSet<>();
targetToOverlays.put(target, overlays);
}
overlays.add(overlay);
}
}
public interface Provider {
/**
* Given the actor string from an overlayable definition, return the actor's package name.
*/
@Nullable
String getActorPkg(String actor);
/**
* Mock response of multiple overlay tags.
*
* TODO(b/119899133): Replace with actual implementation; fix OverlayReferenceMapperTests
*/
@NonNull
Map<String, Set<String>> getTargetToOverlayables(@NonNull AndroidPackage pkg);
}
}

View File

@@ -16,8 +16,6 @@
package com.android.server.pm;
import static android.content.pm.PackageParser.Component;
import static android.content.pm.PackageParser.IntentInfo;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
@@ -26,7 +24,6 @@ import android.annotation.Nullable;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.parsing.AndroidPackage;
import android.content.pm.parsing.ComponentParseUtils;
import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
@@ -50,9 +47,9 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.FgThread;
import com.android.server.om.OverlayReferenceMapper;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -109,11 +106,16 @@ public class AppsFilter {
private final FeatureConfig mFeatureConfig;
private final OverlayReferenceMapper mOverlayReferenceMapper;
AppsFilter(FeatureConfig featureConfig, String[] forceQueryableWhitelist,
boolean systemAppsQueryable) {
boolean systemAppsQueryable,
@Nullable OverlayReferenceMapper.Provider overlayProvider) {
mFeatureConfig = featureConfig;
mForceQueryableByDevicePackageNames = forceQueryableWhitelist;
mSystemAppsQueryable = systemAppsQueryable;
mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/,
overlayProvider);
}
public interface FeatureConfig {
@@ -193,7 +195,7 @@ public class AppsFilter {
}
}
return new AppsFilter(featureConfig, forcedQueryablePackageNames,
forceSystemAppsQueryable);
forceSystemAppsQueryable, null);
}
/** Returns true if the querying package may query for the potential target package */
@@ -282,6 +284,7 @@ public class AppsFilter {
public void onSystemReady() {
mFeatureConfig.onSystemReady();
mOverlayReferenceMapper.rebuildIfDeferred();
}
/**
@@ -338,6 +341,16 @@ public class AppsFilter {
}
}
}
int existingSize = existingSettings.size();
ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize);
for (int index = 0; index < existingSize; index++) {
PackageSetting pkgSetting = existingSettings.valueAt(index);
if (pkgSetting.pkg != null) {
existingPkgs.put(pkgSetting.name, pkgSetting.pkg);
}
}
mOverlayReferenceMapper.addPkg(newPkgSetting.pkg, existingPkgs);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -381,6 +394,8 @@ public class AppsFilter {
addPackage(setting.sharedUser.packages.valueAt(i), existingSettings);
}
}
mOverlayReferenceMapper.removePkg(setting.name);
}
/**
@@ -397,8 +412,7 @@ public class AppsFilter {
PackageSetting targetPkgSetting, int userId) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplication");
try {
if (!shouldFilterApplicationInternal(callingUid, callingSetting,
targetPkgSetting,
if (!shouldFilterApplicationInternal(callingUid, callingSetting, targetPkgSetting,
userId)) {
return false;
}
@@ -412,8 +426,8 @@ public class AppsFilter {
}
}
private boolean shouldFilterApplicationInternal(int callingUid,
SettingBase callingSetting, PackageSetting targetPkgSetting, int userId) {
private boolean shouldFilterApplicationInternal(int callingUid, SettingBase callingSetting,
PackageSetting targetPkgSetting, int userId) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplicationInternal");
try {
final boolean featureEnabled = mFeatureConfig.isGloballyEnabled();
@@ -530,6 +544,29 @@ public class AppsFilter {
}
}
}
if (callingSharedPkgSettings != null) {
int size = callingSharedPkgSettings.size();
for (int index = 0; index < size; index++) {
PackageSetting pkgSetting = callingSharedPkgSettings.valueAt(index);
if (mOverlayReferenceMapper.isValidActor(targetName, pkgSetting.name)) {
if (DEBUG_LOGGING) {
log(callingPkgSetting, targetPkgSetting,
"matches shared user of package that acts on target of "
+ "overlay");
}
return false;
}
}
} else {
if (mOverlayReferenceMapper.isValidActor(targetName, callingPkgSetting.name)) {
if (DEBUG_LOGGING) {
log(callingPkgSetting, targetPkgSetting, "acts on target of overlay");
}
return false;
}
}
return true;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

View File

@@ -0,0 +1,208 @@
/*
* 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.
*/
package com.android.server.om
import android.content.pm.parsing.AndroidPackage
import android.net.Uri
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.testng.Assert.assertThrows
import test.util.mockThrowOnUnmocked
import test.util.whenever
@RunWith(Parameterized::class)
class OverlayReferenceMapperTests {
companion object {
private const val TARGET_PACKAGE_NAME = "com.test.target"
private const val OVERLAY_PACKAGE_NAME = "com.test.overlay"
private const val ACTOR_PACKAGE_NAME = "com.test.actor"
private const val ACTOR_NAME = "overlay://test/actorName"
@JvmStatic
@Parameterized.Parameters(name = "deferRebuild {0}")
fun parameters() = arrayOf(true, false)
}
private lateinit var mapper: OverlayReferenceMapper
@JvmField
@Parameterized.Parameter(0)
var deferRebuild = false
@Before
fun initMapper() {
mapper = mapper()
}
@Test
fun targetWithOverlay() {
val target = mockTarget()
val overlay = mockOverlay()
val existing = mapper.addInOrder(overlay)
assertEmpty()
mapper.addInOrder(target, existing = existing)
assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay))
mapper.remove(target)
assertEmpty()
}
@Test
fun targetWithMultipleOverlays() {
val target = mockTarget()
val overlay0 = mockOverlay(0)
val overlay1 = mockOverlay(1)
mapper = mapper(
overlayToTargetToOverlayables = mapOf(
overlay0.packageName to mapOf(
target.packageName to target.overlayables.keys
),
overlay1.packageName to mapOf(
target.packageName to target.overlayables.keys
)
)
)
val existing = mapper.addInOrder(overlay0, overlay1)
assertEmpty()
mapper.addInOrder(target, existing = existing)
assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay0, overlay1))
mapper.remove(overlay0)
assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay1))
mapper.remove(target)
assertEmpty()
}
@Test
fun targetWithoutOverlay() {
val target = mockTarget()
mapper.addInOrder(target)
assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
mapper.remove(target)
assertEmpty()
}
@Test
fun overlayWithTarget() {
val target = mockTarget()
val overlay = mockOverlay()
val existing = mapper.addInOrder(target)
assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
mapper.addInOrder(overlay, existing = existing)
assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay))
mapper.remove(overlay)
assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
}
@Test
fun overlayWithMultipleTargets() {
val target0 = mockTarget(0)
val target1 = mockTarget(1)
val overlay = mockOverlay()
mapper = mapper(
overlayToTargetToOverlayables = mapOf(
overlay.packageName to mapOf(
target0.packageName to target0.overlayables.keys,
target1.packageName to target1.overlayables.keys
)
)
)
mapper.addInOrder(target0, target1, overlay)
assertMapping(ACTOR_PACKAGE_NAME to setOf(target0, target1, overlay))
mapper.remove(target0)
assertMapping(ACTOR_PACKAGE_NAME to setOf(target1, overlay))
mapper.remove(target1)
assertEmpty()
}
@Test
fun overlayWithoutTarget() {
val overlay = mockOverlay()
mapper.addInOrder(overlay)
// An overlay can only have visibility exposed through its target
assertEmpty()
mapper.remove(overlay)
assertEmpty()
}
private fun OverlayReferenceMapper.addInOrder(
vararg pkgs: AndroidPackage,
existing: MutableMap<String, AndroidPackage> = mutableMapOf()
) = pkgs.fold(existing) { map, pkg ->
addPkg(pkg, map)
map[pkg.packageName] = pkg
return@fold map
}
private fun OverlayReferenceMapper.remove(pkg: AndroidPackage) = removePkg(pkg.packageName)
private fun assertMapping(vararg pairs: Pair<String, Set<AndroidPackage>>) {
val expected = pairs.associate { it }
.mapValues { pair -> pair.value.map { it.packageName }.toSet() }
// This validates the API exposed for querying the relationships
expected.forEach { (actorPkgName, expectedPkgNames) ->
expectedPkgNames.forEach { expectedPkgName ->
if (deferRebuild) {
assertThrows(IllegalStateException::class.java) {
mapper.isValidActor(expectedPkgName, actorPkgName)
}
mapper.rebuildIfDeferred()
deferRebuild = false
}
assertThat(mapper.isValidActor(expectedPkgName, actorPkgName)).isTrue()
}
}
// This asserts no other relationships are defined besides those tested above
assertThat(mapper.actorPkgToPkgs).containsExactlyEntriesIn(expected)
}
private fun assertEmpty() = assertMapping()
private fun mapper(
namedActors: Map<String, Map<String, String>> = Uri.parse(ACTOR_NAME).run {
mapOf(authority!! to mapOf(pathSegments.first() to ACTOR_PACKAGE_NAME))
},
overlayToTargetToOverlayables: Map<String, Map<String, Set<String>>> = mapOf(
mockOverlay().packageName to mapOf(
mockTarget().run { packageName to overlayables.keys }
)
)
) = OverlayReferenceMapper(deferRebuild, object : OverlayReferenceMapper.Provider {
override fun getActorPkg(actor: String?) =
OverlayActorEnforcer.getPackageNameForActor(actor, namedActors).first
override fun getTargetToOverlayables(pkg: AndroidPackage) =
overlayToTargetToOverlayables[pkg.packageName] ?: emptyMap()
})
private fun mockTarget(increment: Int = 0) = mockThrowOnUnmocked<AndroidPackage> {
whenever(packageName) { "$TARGET_PACKAGE_NAME$increment" }
whenever(overlayables) { mapOf("overlayableName$increment" to ACTOR_NAME) }
whenever(toString()) { "Package{$packageName}" }
}
private fun mockOverlay(increment: Int = 0) = mockThrowOnUnmocked<AndroidPackage> {
whenever(packageName) { "$OVERLAY_PACKAGE_NAME$increment" }
whenever(overlayables) { emptyMap<String, String>() }
whenever(toString()) { "Package{$packageName}" }
}
}

View File

@@ -35,6 +35,11 @@ import android.content.pm.parsing.ParsingPackage;
import android.os.Build;
import android.os.Process;
import android.util.ArrayMap;
import android.util.ArraySet;
import androidx.annotation.NonNull;
import com.android.server.om.OverlayReferenceMapper;
import org.junit.Before;
import org.junit.Test;
@@ -43,11 +48,18 @@ import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
@RunWith(JUnit4.class)
public class AppsFilterTest {
private static final int DUMMY_CALLING_UID = 10345;
private static final int DUMMY_TARGET_UID = 10556;
private static final int DUMMY_ACTOR_UID = 10656;
private static final int DUMMY_OVERLAY_UID = 10756;
private static final int DUMMY_ACTOR_TWO_UID = 10856;
@Mock
AppsFilter.FeatureConfig mFeatureConfigMock;
@@ -117,7 +129,7 @@ public class AppsFilterTest {
@Test
public void testSystemReadyPropogates() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mFeatureConfigMock, new String[]{}, false);
new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
verify(mFeatureConfigMock).onSystemReady();
}
@@ -125,7 +137,8 @@ public class AppsFilterTest {
@Test
public void testQueriesAction_FilterMatches() {
final AppsFilter appsFilter =
new AppsFilter(mFeatureConfigMock, new String[]{}, false);
new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package", new IntentFilter("TEST_ACTION")), DUMMY_TARGET_UID);
@@ -138,7 +151,8 @@ public class AppsFilterTest {
@Test
public void testQueriesAction_NoMatchingAction_Filters() {
final AppsFilter appsFilter =
new AppsFilter(mFeatureConfigMock, new String[]{}, false);
new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -151,7 +165,8 @@ public class AppsFilterTest {
@Test
public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() {
final AppsFilter appsFilter =
new AppsFilter(mFeatureConfigMock, new String[]{}, false);
new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -169,7 +184,8 @@ public class AppsFilterTest {
@Test
public void testNoQueries_Filters() {
final AppsFilter appsFilter =
new AppsFilter(mFeatureConfigMock, new String[]{}, false);
new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -182,7 +198,8 @@ public class AppsFilterTest {
@Test
public void testForceQueryable_DoesntFilter() {
final AppsFilter appsFilter =
new AppsFilter(mFeatureConfigMock, new String[]{}, false);
new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_UID);
@@ -195,7 +212,8 @@ public class AppsFilterTest {
@Test
public void testForceQueryableByDevice_SystemCaller_DoesntFilter() {
final AppsFilter appsFilter =
new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false);
new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false, null);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID,
@@ -209,7 +227,8 @@ public class AppsFilterTest {
@Test
public void testForceQueryableByDevice_NonSystemCaller_Filters() {
final AppsFilter appsFilter =
new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false);
new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false, null);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -224,7 +243,8 @@ public class AppsFilterTest {
public void testSystemQueryable_DoesntFilter() {
final AppsFilter appsFilter =
new AppsFilter(mFeatureConfigMock, new String[]{},
true /* system force queryable */);
true /* system force queryable */, null);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID,
@@ -238,7 +258,8 @@ public class AppsFilterTest {
@Test
public void testQueriesPackage_DoesntFilter() {
final AppsFilter appsFilter =
new AppsFilter(mFeatureConfigMock, new String[]{}, false);
new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -253,7 +274,8 @@ public class AppsFilterTest {
when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class)))
.thenReturn(false);
final AppsFilter appsFilter =
new AppsFilter(mFeatureConfigMock, new String[]{}, false);
new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(
appsFilter, pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -266,20 +288,22 @@ public class AppsFilterTest {
@Test
public void testSystemUid_DoesntFilter() {
final AppsFilter appsFilter =
new AppsFilter(mFeatureConfigMock, new String[]{}, false);
new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID);
assertFalse(appsFilter.shouldFilterApplication(0, null, target, 0));
assertFalse(appsFilter.shouldFilterApplication(
Process.FIRST_APPLICATION_UID - 1, null, target, 0));
assertFalse(appsFilter.shouldFilterApplication(Process.FIRST_APPLICATION_UID - 1,
null, target, 0));
}
@Test
public void testNonSystemUid_NoCallingSetting_Filters() {
final AppsFilter appsFilter =
new AppsFilter(mFeatureConfigMock, new String[]{}, false);
new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -290,7 +314,8 @@ public class AppsFilterTest {
@Test
public void testNoTargetPackage_filters() {
final AppsFilter appsFilter =
new AppsFilter(mFeatureConfigMock, new String[]{}, false);
new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
PackageSetting target = new PackageSettingBuilder()
.setName("com.some.package")
@@ -304,6 +329,127 @@ public class AppsFilterTest {
assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testActsOnTargetOfOverlay() {
final String actorName = "overlay://test/actorName";
ParsingPackage target = pkg("com.some.package.target")
.addOverlayable("overlayableName", actorName);
ParsingPackage overlay = pkg("com.some.package.overlay")
.setIsOverlay(true)
.setOverlayTarget(target.getPackageName())
.setOverlayTargetName("overlayableName");
ParsingPackage actor = pkg("com.some.package.actor");
final AppsFilter appsFilter = new AppsFilter(mFeatureConfigMock, new String[]{}, false,
new OverlayReferenceMapper.Provider() {
@Nullable
@Override
public String getActorPkg(String actorString) {
if (actorName.equals(actorString)) {
return actor.getPackageName();
}
return null;
}
@NonNull
@Override
public Map<String, Set<String>> getTargetToOverlayables(
@NonNull AndroidPackage pkg) {
if (overlay.getPackageName().equals(pkg.getPackageName())) {
Map<String, Set<String>> map = new ArrayMap<>();
Set<String> set = new ArraySet<>();
set.add(overlay.getOverlayTargetName());
map.put(overlay.getOverlayTarget(), set);
return map;
}
return Collections.emptyMap();
}
});
appsFilter.onSystemReady();
PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_UID);
PackageSetting overlaySetting = simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_UID);
PackageSetting actorSetting = simulateAddPackage(appsFilter, actor, DUMMY_ACTOR_UID);
// Actor can see both target and overlay
assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_UID, actorSetting,
targetSetting, 0));
assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_UID, actorSetting,
overlaySetting, 0));
// But target/overlay can't see each other
assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, targetSetting,
overlaySetting, 0));
assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_UID, overlaySetting,
targetSetting, 0));
// And can't see the actor
assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, targetSetting,
actorSetting, 0));
assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_UID, overlaySetting,
actorSetting, 0));
}
@Test
public void testActsOnTargetOfOverlayThroughSharedUser() {
final String actorName = "overlay://test/actorName";
ParsingPackage target = pkg("com.some.package.target")
.addOverlayable("overlayableName", actorName);
ParsingPackage overlay = pkg("com.some.package.overlay")
.setIsOverlay(true)
.setOverlayTarget(target.getPackageName())
.setOverlayTargetName("overlayableName");
ParsingPackage actorOne = pkg("com.some.package.actor.one");
ParsingPackage actorTwo = pkg("com.some.package.actor.two");
final AppsFilter appsFilter = new AppsFilter(mFeatureConfigMock, new String[]{}, false,
new OverlayReferenceMapper.Provider() {
@Nullable
@Override
public String getActorPkg(String actorString) {
// Only actorOne is mapped as a valid actor
if (actorName.equals(actorString)) {
return actorOne.getPackageName();
}
return null;
}
@NonNull
@Override
public Map<String, Set<String>> getTargetToOverlayables(
@NonNull AndroidPackage pkg) {
if (overlay.getPackageName().equals(pkg.getPackageName())) {
Map<String, Set<String>> map = new ArrayMap<>();
Set<String> set = new ArraySet<>();
set.add(overlay.getOverlayTargetName());
map.put(overlay.getOverlayTarget(), set);
return map;
}
return Collections.emptyMap();
}
});
appsFilter.onSystemReady();
PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_UID);
PackageSetting overlaySetting = simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_UID);
PackageSetting actorOneSetting = simulateAddPackage(appsFilter, actorOne, DUMMY_ACTOR_UID);
PackageSetting actorTwoSetting = simulateAddPackage(appsFilter, actorTwo,
DUMMY_ACTOR_TWO_UID);
SharedUserSetting actorSharedSetting = new SharedUserSetting("actorSharedUser",
actorOneSetting.pkgFlags, actorOneSetting.pkgPrivateFlags);
actorSharedSetting.addPackage(actorOneSetting);
actorSharedSetting.addPackage(actorTwoSetting);
// actorTwo can see both target and overlay
assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_TWO_UID, actorSharedSetting,
targetSetting, 0));
assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_TWO_UID, actorSharedSetting,
overlaySetting, 0));
}
private interface WithSettingBuilder {
PackageSettingBuilder withBuilder(PackageSettingBuilder builder);
}

View File

@@ -22,7 +22,7 @@ import android.util.SparseArray;
import java.io.File;
class PackageSettingBuilder {
public class PackageSettingBuilder {
private String mName;
private String mRealName;
private String mCodePath;

View File

@@ -17,11 +17,16 @@
java_library {
name: "frameworks-base-testutils",
srcs: ["java/**/*.java"],
srcs: [
"java/**/*.java",
"java/**/*.kt",
],
static_libs: [
"junit",
"hamcrest-library",
"truth-prebuilt",
"mockito-target-minus-junit4",
],
libs: [

View File

@@ -0,0 +1,21 @@
/*
* 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.
*/
/**
* This package separated from android. because placing classes under android.'s .test/.util
* may be confused with tests for that actual android subpackage.
**/
package test;

View File

@@ -0,0 +1,70 @@
/*
* 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.
*/
package test.util
import org.mockito.Answers
import org.mockito.Mockito
import org.mockito.invocation.InvocationOnMock
import org.mockito.stubbing.Answer
import org.mockito.stubbing.Stubber
object MockitoUtils {
val ANSWER_THROWS = Answer<Any?> {
when (val name = it.method.name) {
"toString" -> return@Answer Answers.CALLS_REAL_METHODS.answer(it)
else -> {
val arguments = it.arguments
?.takeUnless { it.isEmpty() }
?.joinToString()
?.let {
"with $it"
}
.orEmpty()
throw UnsupportedOperationException("${it.mock::class.java.simpleName}#$name " +
"$arguments should not be called")
}
}
}
}
inline fun <reified T> mock(block: T.() -> Unit = {}) = Mockito.mock(T::class.java).apply(block)
fun <Type> Stubber.whenever(mock: Type) = Mockito.`when`(mock)
fun <Type : Any?> whenever(mock: Type) = Mockito.`when`(mock)
@Suppress("UNCHECKED_CAST")
fun <Type : Any?> whenever(mock: Type, block: InvocationOnMock.() -> Any?) =
Mockito.`when`(mock).thenAnswer { block(it) }
fun whenever(mock: Unit) = Mockito.`when`(mock).thenAnswer { }
inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit): T {
val swappingAnswer = object : Answer<Any?> {
var delegate: Answer<*> = Answers.RETURNS_DEFAULTS
override fun answer(invocation: InvocationOnMock?): Any? {
return delegate.answer(invocation)
}
}
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
}
}