summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Kershaw <alexkershaw@google.com>2020-01-15 20:00:05 +0000
committerAlex Kershaw <alexkershaw@google.com>2020-01-22 13:20:24 +0000
commit4b0197d8ba2cacaed1a2b6a774c78774fdeeffdc (patch)
treee3933f538532e6aee103e81a1ef0b8171bd0aa9c
parenta263d01c447f4bf5d946dfb4361ad78a5ecce7b1 (diff)
Reset unset app-ops when the admin sets cross-profile packages.
When the admin unsets cross-profile packages, reset the INTERACT_ACROSS_PROFILES app-op back to the default if it is no longer configurable by the user. Other minor changes: - Remove the explicit app-op permission check from setInteractAcrossProfilesAppOp, since AppOpManager performs that check when required. - Fix the broadcasting logic in CrossProfileAppsServiceImpl to correctly set the component and flags. Bug: 136249261 Bug: 148010178 Test: atest CtsDevicePolicyManagerTestCases:com.android.cts.devicepolicy.ManagedProfileCrossProfileTest Change-Id: Ib8b5f331fb92fc475bc95ad039adf93fac04da37
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java4
-rw-r--r--core/java/android/content/pm/CrossProfileApps.java73
-rw-r--r--core/java/android/content/pm/ICrossProfileApps.aidl2
-rw-r--r--services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java84
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java9
-rw-r--r--services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java116
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java3
9 files changed, 257 insertions, 46 deletions
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9a63c528f488..aeeba4c54108 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11399,6 +11399,10 @@ public class DevicePolicyManager {
*
* <p>Previous calls are overridden by each subsequent call to this method.
*
+ * <p>When previously-set cross-profile packages are missing from {@code packageNames}, the
+ * app-op for {@code INTERACT_ACROSS_PROFILES} will be reset for those packages. This will not
+ * occur for packages that are whitelisted by the OEM.
+ *
* @param admin the {@link DeviceAdminReceiver} this request is associated with
* @param packageNames the new cross-profile package names
*/
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 48f473018127..de153d00b48b 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -34,8 +34,10 @@ import android.provider.Settings;
import com.android.internal.R;
import com.android.internal.util.UserIcons;
+import java.util.Collection;
import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* Class for handling cross profile operations. Apps can use this class to interact with its
@@ -298,19 +300,24 @@ public class CrossProfileApps {
* configurable by users in Settings. This configures it for the profile group of the calling
* package.
*
- * <p>Before calling, check {@link #canRequestInteractAcrossProfiles()} and do not call if it is
- * {@code false}. If presenting a user interface, do not allow the user to configure the app-op
- * in that case.
+ * <p>Before calling, check {@link #canConfigureInteractAcrossProfiles(String)} and do not call
+ * if it is {@code false}. If presenting a user interface, do not allow the user to configure
+ * the app-op in that case.
*
* <p>The underlying app-op {@link android.app.AppOpsManager#OP_INTERACT_ACROSS_PROFILES} should
* never be set directly. This method ensures that the app-op is kept in sync for the app across
* each user in the profile group and that those apps are sent a broadcast when their ability to
* interact across profiles changes.
*
- * <p>This method should be used whenever an app's ability to interact across profiles changes,
- * as defined by the return value of {@link #canInteractAcrossProfiles()}. This includes user
- * consent changes in Settings or during provisioning, plus changes to the admin or OEM consent
- * whitelists that make the current app-op value invalid.
+ * <p>This method should be used directly whenever a user's action results in a change in an
+ * app's ability to interact across profiles, as defined by the return value of {@link
+ * #canInteractAcrossProfiles()}. This includes user consent changes in Settings or during
+ * provisioning.
+ *
+ * <p>If other changes could have affected the app's ability to interact across profiles, as
+ * defined by the return value of {@link #canInteractAcrossProfiles()}, such as changes to the
+ * admin or OEM consent whitelists, then {@link
+ * #resetInteractAcrossProfilesAppOpsIfInvalid(List)} should be used.
*
* @hide
*/
@@ -325,6 +332,58 @@ public class CrossProfileApps {
}
}
+ /**
+ * Returns whether the given package can have its ability to interact across profiles configured
+ * by the user. This means that every other condition to interact across profiles has been set.
+ *
+ * <p>This differs from {@link #canRequestInteractAcrossProfiles()} since it will not return
+ * {@code false} simply when the target profile is disabled.
+ *
+ * @hide
+ */
+ public boolean canConfigureInteractAcrossProfiles(@NonNull String packageName) {
+ try {
+ return mService.canConfigureInteractAcrossProfiles(packageName);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * For each of the packages defined in {@code previousCrossProfilePackages} but not included in
+ * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission
+ * #INTERACT_ACROSS_PROFILES} back to its default value if it can no longer be configured by
+ * users in Settings, as defined by {@link #canConfigureInteractAcrossProfiles(String)}.
+ *
+ * <p>This method should be used whenever an app's ability to interact across profiles could
+ * have changed as a result of non-user actions, such as changes to admin or OEM consent
+ * whitelists.
+ *
+ * @hide
+ */
+ @RequiresPermission(
+ allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES,
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
+ public void resetInteractAcrossProfilesAppOps(
+ @NonNull Collection<String> previousCrossProfilePackages,
+ @NonNull Set<String> newCrossProfilePackages) {
+ if (previousCrossProfilePackages.isEmpty()) {
+ return;
+ }
+ final List<String> unsetCrossProfilePackages =
+ previousCrossProfilePackages.stream()
+ .filter(packageName -> !newCrossProfilePackages.contains(packageName))
+ .collect(Collectors.toList());
+ if (unsetCrossProfilePackages.isEmpty()) {
+ return;
+ }
+ try {
+ mService.resetInteractAcrossProfilesAppOps(unsetCrossProfilePackages);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
private void verifyCanAccessUser(UserHandle userHandle) {
if (!getTargetUserProfiles().contains(userHandle)) {
throw new SecurityException("Not allowed to access " + userHandle);
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index 755950cd5ebe..a69b9881aa01 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -35,4 +35,6 @@ interface ICrossProfileApps {
boolean canInteractAcrossProfiles(in String callingPackage);
boolean canRequestInteractAcrossProfiles(in String callingPackage);
void setInteractAcrossProfilesAppOp(in String packageName, int newMode);
+ boolean canConfigureInteractAcrossProfiles(in String packageName);
+ void resetInteractAcrossProfilesAppOps(in List<String> packageNames);
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index fc08ddeddf82..74d2efeceb63 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -286,21 +286,21 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
}
private List<UserHandle> getTargetUserProfilesUnchecked(
- String callingPackage, @UserIdInt int callingUserId) {
+ String packageName, @UserIdInt int userId) {
final long ident = mInjector.clearCallingIdentity();
try {
final int[] enabledProfileIds =
- mInjector.getUserManager().getEnabledProfileIds(callingUserId);
+ mInjector.getUserManager().getEnabledProfileIds(userId);
List<UserHandle> targetProfiles = new ArrayList<>();
- for (final int userId : enabledProfileIds) {
- if (userId == callingUserId) {
+ for (final int profileId : enabledProfileIds) {
+ if (profileId == userId) {
continue;
}
- if (!isPackageEnabled(callingPackage, userId)) {
+ if (!isPackageEnabled(packageName, profileId)) {
continue;
}
- targetProfiles.add(UserHandle.of(userId));
+ targetProfiles.add(UserHandle.of(profileId));
}
return targetProfiles;
} finally {
@@ -385,14 +385,9 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
"INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL is required to set the"
+ " app-op for interacting across profiles.");
}
- if (!isPermissionGranted(Manifest.permission.MANAGE_APP_OPS_MODES, callingUid)) {
- throw new SecurityException(
- "MANAGE_APP_OPS_MODES is required to set the app-op for interacting across"
- + " profiles.");
- }
final int callingUserId = mInjector.getCallingUserId();
if (newMode == AppOpsManager.MODE_ALLOWED
- && !canRequestInteractAcrossProfilesUnchecked(packageName, callingUserId)) {
+ && !canConfigureInteractAcrossProfiles(packageName)) {
// The user should not be prompted for apps that cannot request to interact across
// profiles. However, we return early here if required to avoid race conditions.
Slog.e(TAG, "Tried to turn on the appop for interacting across profiles for invalid"
@@ -441,7 +436,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
final int uid = mInjector.getPackageManager()
.getPackageUidAsUser(packageName, /* flags= */ 0, userId);
if (currentModeEquals(newMode, packageName, uid)) {
- Slog.w(TAG,"Attempt to set mode to existing value of " + newMode + " for "
+ Slog.w(TAG, "Attempt to set mode to existing value of " + newMode + " for "
+ packageName + " on user ID " + userId);
return;
}
@@ -462,18 +457,73 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
private void sendCanInteractAcrossProfilesChangedBroadcast(
String packageName, int uid, UserHandle userHandle) {
- final Intent intent = new Intent(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED)
- .setPackage(packageName);
- if (!appDeclaresCrossProfileAttribute(uid)) {
+ final Intent intent =
+ new Intent(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED).setPackage(packageName);
+ if (appDeclaresCrossProfileAttribute(uid)) {
+ intent.addFlags(
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_FOREGROUND);
+ } else {
intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY);
}
- mInjector.sendBroadcastAsUser(intent, userHandle);
+ for (ResolveInfo receiver : findBroadcastReceiversForUser(intent, userHandle)) {
+ intent.setComponent(receiver.getComponentInfo().getComponentName());
+ mInjector.sendBroadcastAsUser(intent, userHandle);
+ }
+ }
+
+ private List<ResolveInfo> findBroadcastReceiversForUser(Intent intent, UserHandle userHandle) {
+ return mInjector.getPackageManager()
+ .queryBroadcastReceiversAsUser(intent, /* flags= */ 0, userHandle);
}
private boolean appDeclaresCrossProfileAttribute(int uid) {
return mInjector.getPackageManagerInternal().getPackage(uid).isCrossProfile();
}
+ @Override
+ public boolean canConfigureInteractAcrossProfiles(String packageName) {
+ if (!hasOtherProfileWithPackageInstalled(packageName, mInjector.getCallingUserId())) {
+ return false;
+ }
+ if (!hasRequestedAppOpPermission(
+ AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) {
+ return false;
+ }
+ return isCrossProfilePackageWhitelisted(packageName);
+ }
+
+ private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) {
+ final long ident = mInjector.clearCallingIdentity();
+ try {
+ final int[] profileIds =
+ mInjector.getUserManager().getProfileIds(userId, /* enabledOnly= */ false);
+ for (int profileId : profileIds) {
+ if (profileId != userId && isPackageInstalled(packageName, profileId)) {
+ return true;
+ }
+ }
+ } finally {
+ mInjector.restoreCallingIdentity(ident);
+ }
+ return false;
+ }
+
+ @Override
+ public void resetInteractAcrossProfilesAppOps(List<String> packageNames) {
+ packageNames.forEach(this::resetInteractAcrossProfilesAppOp);
+ }
+
+ private void resetInteractAcrossProfilesAppOp(String packageName) {
+ if (canConfigureInteractAcrossProfiles(packageName)) {
+ Slog.w(TAG, "Not resetting app-op for package " + packageName
+ + " since it is still configurable by users.");
+ return;
+ }
+ final String op =
+ AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES);
+ setInteractAcrossProfilesAppOp(packageName, AppOpsManager.opToDefaultMode(op));
+ }
+
private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
final long ident = mInjector.clearCallingIdentity();
try {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0c79a6f611a5..bed2f239dc13 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -156,6 +156,7 @@ import android.content.IntentFilter;
import android.content.PermissionChecker;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.CrossProfileApps;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
@@ -302,6 +303,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -310,6 +312,7 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
+import java.util.stream.Collectors;
/**
* Implementation of the device policy APIs.
@@ -14887,12 +14890,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(packageNames, "Package names is null");
+ final List<String> previousCrossProfilePackages;
synchronized (getLockObject()) {
final ActiveAdmin admin =
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ previousCrossProfilePackages = admin.mCrossProfilePackages;
admin.mCrossProfilePackages = packageNames;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
+ final CrossProfileApps crossProfileApps = mContext.getSystemService(CrossProfileApps.class);
+ mInjector.binderWithCleanCallingIdentity(
+ () -> crossProfileApps.resetInteractAcrossProfilesAppOps(
+ previousCrossProfilePackages, new HashSet<>(packageNames)));
}
@Override
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index 96ff9c1ba726..1a7b1d3f6039 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -18,6 +18,8 @@ package com.android.server.pm;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES;
+import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
+import static android.content.Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND;
import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
import static android.content.pm.CrossProfileApps.ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED;
@@ -37,11 +39,13 @@ import android.app.AppOpsManager.Mode;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
import android.content.pm.parsing.AndroidPackage;
import android.content.pm.parsing.PackageImpl;
import android.os.Process;
@@ -70,6 +74,7 @@ import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -106,6 +111,7 @@ public class CrossProfileAppsServiceImplRoboTest {
MockitoAnnotations.initMocks(this);
mockCrossProfileAppInstalledAndEnabledOnEachProfile();
mockCrossProfileAppRequestsInteractAcrossProfiles();
+ mockCrossProfileAppRegistersBroadcastReceiver();
mockCrossProfileAppWhitelisted();
}
@@ -113,19 +119,21 @@ public class CrossProfileAppsServiceImplRoboTest {
// They are enabled by default, so we simply have to ensure that a package info with an
// application info is returned.
final PackageInfo packageInfo = buildTestPackageInfo();
+ mockCrossProfileAppInstalledOnProfile(
+ packageInfo, PERSONAL_PROFILE_USER_ID, PERSONAL_PROFILE_UID);
+ mockCrossProfileAppInstalledOnProfile(packageInfo, WORK_PROFILE_USER_ID, WORK_PROFILE_UID);
+ }
+
+ private void mockCrossProfileAppInstalledOnProfile(
+ PackageInfo packageInfo, @UserIdInt int userId, int uid) {
when(mPackageManagerInternal.getPackageInfo(
eq(CROSS_PROFILE_APP_PACKAGE_NAME),
/* flags= */ anyInt(),
/* filterCallingUid= */ anyInt(),
- eq(PERSONAL_PROFILE_USER_ID)))
+ eq(userId)))
.thenReturn(packageInfo);
- when(mPackageManagerInternal.getPackageInfo(
- eq(CROSS_PROFILE_APP_PACKAGE_NAME),
- /* flags= */ anyInt(),
- /* filterCallingUid= */ anyInt(),
- eq(WORK_PROFILE_USER_ID)))
- .thenReturn(packageInfo);
- mockCrossProfileAndroidPackage(PackageImpl.forParsing(CROSS_PROFILE_APP_PACKAGE_NAME));
+ when(mPackageManagerInternal.getPackage(uid))
+ .thenReturn(PackageImpl.forParsing(CROSS_PROFILE_APP_PACKAGE_NAME));
}
private PackageInfo buildTestPackageInfo() {
@@ -140,6 +148,31 @@ public class CrossProfileAppsServiceImplRoboTest {
.thenReturn(new String[] {CROSS_PROFILE_APP_PACKAGE_NAME});
}
+ private void mockCrossProfileAppRegistersBroadcastReceiver() {
+ final ShadowApplicationPackageManager shadowApplicationPackageManager =
+ Shadow.extract(mPackageManager);
+ final Intent baseIntent =
+ new Intent(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED)
+ .setPackage(CROSS_PROFILE_APP_PACKAGE_NAME);
+ final Intent manifestIntent =
+ new Intent(baseIntent)
+ .setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ final Intent registeredIntent =
+ new Intent(baseIntent).setFlags(FLAG_RECEIVER_REGISTERED_ONLY);
+ final List<ResolveInfo> resolveInfos = Lists.newArrayList(buildTestResolveInfo());
+ shadowApplicationPackageManager.setResolveInfosForIntent(manifestIntent, resolveInfos);
+ shadowApplicationPackageManager.setResolveInfosForIntent(registeredIntent, resolveInfos);
+ }
+
+ private ResolveInfo buildTestResolveInfo() {
+ final ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = new ActivityInfo();
+ resolveInfo.activityInfo.packageName = CROSS_PROFILE_APP_PACKAGE_NAME;
+ resolveInfo.activityInfo.name = CROSS_PROFILE_APP_PACKAGE_NAME + ".Receiver";
+ return resolveInfo;
+ }
+
private void mockCrossProfileAppWhitelisted() {
when(mDevicePolicyManagerInternal.getAllCrossProfilePackages())
.thenReturn(Lists.newArrayList(CROSS_PROFILE_APP_PACKAGE_NAME));
@@ -191,16 +224,6 @@ public class CrossProfileAppsServiceImplRoboTest {
}
@Test
- public void setInteractAcrossProfilesAppOp_missingManageAppOpsModes_throwsSecurityException() {
- denyPermissions(Manifest.permission.MANAGE_APP_OPS_MODES);
- try {
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
- CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
- fail();
- } catch (SecurityException expected) {}
- }
-
- @Test
public void setInteractAcrossProfilesAppOp_setsAppOp() {
mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
@@ -294,6 +317,54 @@ public class CrossProfileAppsServiceImplRoboTest {
assertThat(receivedManifestCanInteractAcrossProfilesChangedBroadcast()).isTrue();
}
+ @Test
+ public void canConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsFalse() {
+ mockUninstallCrossProfileAppFromWorkProfile();
+ assertThat(mCrossProfileAppsServiceImpl
+ .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .isFalse();
+ }
+
+ private void mockUninstallCrossProfileAppFromWorkProfile() {
+ when(mPackageManagerInternal.getPackageInfo(
+ eq(CROSS_PROFILE_APP_PACKAGE_NAME),
+ /* flags= */ anyInt(),
+ /* filterCallingUid= */ anyInt(),
+ eq(WORK_PROFILE_USER_ID)))
+ .thenReturn(null);
+ when(mPackageManagerInternal.getPackage(WORK_PROFILE_UID)).thenReturn(null);
+ }
+
+ @Test
+ public void canConfigureInteractAcrossProfiles_packageDoesNotRequestInteractAcrossProfiles_returnsFalse()
+ throws Exception {
+ mockCrossProfileAppDoesNotRequestInteractAcrossProfiles();
+ assertThat(mCrossProfileAppsServiceImpl
+ .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .isFalse();
+ }
+
+ private void mockCrossProfileAppDoesNotRequestInteractAcrossProfiles() throws Exception {
+ final String permissionName = Manifest.permission.INTERACT_ACROSS_PROFILES;
+ when(mIPackageManager.getAppOpPermissionPackages(permissionName))
+ .thenReturn(new String[] {});
+ }
+
+ @Test
+ public void canConfigureInteractAcrossProfiles_packageNotWhitelisted_returnsFalse() {
+ mockCrossProfileAppNotWhitelisted();
+ assertThat(mCrossProfileAppsServiceImpl
+ .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .isFalse();
+ }
+
+ @Test
+ public void canConfigureInteractAcrossProfiles_returnsTrue() {
+ assertThat(mCrossProfileAppsServiceImpl
+ .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .isTrue();
+ }
+
private void explicitlySetInteractAcrossProfilesAppOp(@Mode int mode) {
explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode);
}
@@ -311,7 +382,6 @@ public class CrossProfileAppsServiceImplRoboTest {
shadowOf(mContext).denyPermissions(Process.myPid(), CALLING_UID, permissions);
}
-
private @Mode int getCrossProfileAppOp() {
return getCrossProfileAppOp(PERSONAL_PROFILE_UID);
}
@@ -365,10 +435,12 @@ public class CrossProfileAppsServiceImplRoboTest {
}
private boolean isBroadcastManifestCanInteractAcrossProfilesChanged(Intent intent) {
- // The manifest check is negative since the FLAG_RECEIVER_REGISTERED_ONLY flag means that
- // manifest receivers can NOT receive the broadcast.
return isBroadcastCanInteractAcrossProfilesChanged(intent)
- && (intent.getFlags() & FLAG_RECEIVER_REGISTERED_ONLY) == 0;
+ && (intent.getFlags() & FLAG_RECEIVER_REGISTERED_ONLY) == 0
+ && (intent.getFlags() & FLAG_RECEIVER_INCLUDE_BACKGROUND) != 0
+ && (intent.getFlags() & FLAG_RECEIVER_FOREGROUND) != 0
+ && intent.getComponent() != null
+ && intent.getComponent().getPackageName().equals(CROSS_PROFILE_APP_PACKAGE_NAME);
}
private void declareCrossProfileAttributeOnCrossProfileApp(boolean value) {
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
index 1443eabf07d5..aea36e555ad7 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
@@ -19,7 +19,10 @@ package com.android.server.testing.shadows;
import static android.content.pm.PackageManager.NameNotFoundException;
import android.app.ApplicationPackageManager;
+import android.content.Intent;
import android.content.pm.PackageInfo;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
import android.util.ArrayMap;
import org.robolectric.annotation.Implements;
@@ -100,6 +103,13 @@ public class ShadowApplicationPackageManager
return sPackageUids.get(packageName);
}
+ @Override
+ protected List<ResolveInfo> queryBroadcastReceiversAsUser(
+ Intent intent, int flags, UserHandle userHandle) {
+ // Currently does not handle multi-user.
+ return queryBroadcastReceivers(intent, flags);
+ }
+
/** Clear package state. */
@Resetter
public static void reset() {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 6cf6b67430ae..12228c19ca00 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -221,6 +221,8 @@ public class DpmMockContext extends MockContext {
return mMockSystemServices.telephonyManager;
case Context.APP_OPS_SERVICE:
return mMockSystemServices.appOpsManager;
+ case Context.CROSS_PROFILE_APPS_SERVICE:
+ return mMockSystemServices.crossProfileApps;
}
throw new UnsupportedOperationException();
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index b9fb1aab65ba..37d40811571f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -42,6 +42,7 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.CrossProfileApps;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -120,6 +121,7 @@ public class MockSystemServices {
public final TimeDetector timeDetector;
public final TimeZoneDetector timeZoneDetector;
public final KeyChain.KeyChainConnection keyChainConnection;
+ public final CrossProfileApps crossProfileApps;
public final PersistentDataBlockManagerInternal persistentDataBlockManagerInternal;
public final AppOpsManager appOpsManager;
/** Note this is a partial mock, not a real mock. */
@@ -165,6 +167,7 @@ public class MockSystemServices {
timeDetector = mock(TimeDetector.class);
timeZoneDetector = mock(TimeZoneDetector.class);
keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS);
+ crossProfileApps = mock(CrossProfileApps.class);
persistentDataBlockManagerInternal = mock(PersistentDataBlockManagerInternal.class);
appOpsManager = mock(AppOpsManager.class);