Merge "Support setting the _PROFILES app-op"
This commit is contained in:
committed by
Android (Google) Code Review
commit
55e4896d52
@@ -11399,6 +11399,7 @@ package android.content.pm {
|
||||
method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle);
|
||||
method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles();
|
||||
method public void startMainActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
|
||||
field public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED = "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED";
|
||||
}
|
||||
|
||||
public final class FeatureGroupInfo implements android.os.Parcelable {
|
||||
|
||||
@@ -19,6 +19,7 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SystemApi;
|
||||
import android.app.AppOpsManager.Mode;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -42,6 +43,18 @@ import java.util.Set;
|
||||
* use this class to start its main activity in managed profile.
|
||||
*/
|
||||
public class CrossProfileApps {
|
||||
|
||||
/**
|
||||
* Broadcast signalling that the receiving app's ability to interact across profiles has
|
||||
* changed, as defined by the return value of {@link #canInteractAcrossProfiles()}.
|
||||
*
|
||||
* <p>Apps that have set the {@code android:crossProfile} manifest attribute to {@code true}
|
||||
* can receive this broadcast in manifest broadcast receivers. Otherwise, it can only be
|
||||
* received by dynamically-registered broadcast receivers.
|
||||
*/
|
||||
public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED =
|
||||
"android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED";
|
||||
|
||||
private final Context mContext;
|
||||
private final ICrossProfileApps mService;
|
||||
private final UserManager mUserManager;
|
||||
@@ -254,6 +267,38 @@ public class CrossProfileApps {
|
||||
return settingsIntent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the app-op for {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} that is
|
||||
* 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>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.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(
|
||||
allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES,
|
||||
android.Manifest.permission.INTERACT_ACROSS_USERS})
|
||||
public void setInteractAcrossProfilesAppOp(@NonNull String packageName, @Mode int newMode) {
|
||||
try {
|
||||
mService.setInteractAcrossProfilesAppOp(packageName, newMode);
|
||||
} catch (RemoteException ex) {
|
||||
throw ex.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyCanAccessUser(UserHandle userHandle) {
|
||||
if (!getTargetUserProfiles().contains(userHandle)) {
|
||||
throw new SecurityException("Not allowed to access " + userHandle);
|
||||
|
||||
@@ -32,4 +32,5 @@ interface ICrossProfileApps {
|
||||
List<UserHandle> getTargetUserProfiles(in String callingPackage);
|
||||
boolean canInteractAcrossProfiles(in String callingPackage);
|
||||
boolean canRequestInteractAcrossProfiles(in String callingPackage);
|
||||
void setInteractAcrossProfilesAppOp(in String packageName, int newMode);
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
package com.android.server.pm;
|
||||
|
||||
import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES;
|
||||
import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
|
||||
import static android.content.pm.CrossProfileApps.ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED;
|
||||
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
|
||||
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
|
||||
|
||||
@@ -26,6 +28,7 @@ import android.app.ActivityManagerInternal;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.AppGlobals;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.AppOpsManager.Mode;
|
||||
import android.app.IApplicationThread;
|
||||
import android.app.admin.DevicePolicyEventLogger;
|
||||
import android.app.admin.DevicePolicyManagerInternal;
|
||||
@@ -66,8 +69,6 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
|
||||
private Context mContext;
|
||||
private Injector mInjector;
|
||||
private AppOpsService mAppOpsService;
|
||||
private final DevicePolicyManagerInternal mDpmi;
|
||||
private final IPackageManager mIpm;
|
||||
|
||||
public CrossProfileAppsServiceImpl(Context context) {
|
||||
this(context, new InjectorImpl(context));
|
||||
@@ -77,8 +78,6 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
|
||||
CrossProfileAppsServiceImpl(Context context, Injector injector) {
|
||||
mContext = context;
|
||||
mInjector = injector;
|
||||
mIpm = AppGlobals.getPackageManager();
|
||||
mDpmi = LocalServices.getService(DevicePolicyManagerInternal.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -144,7 +143,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
|
||||
// must have the required permission and the users must be in the same profile group
|
||||
// in order to launch any of its own activities.
|
||||
if (callerUserId != userId) {
|
||||
final int permissionFlag = ActivityManager.checkComponentPermission(
|
||||
final int permissionFlag = mInjector.checkComponentPermission(
|
||||
android.Manifest.permission.INTERACT_ACROSS_PROFILES, callingUid,
|
||||
-1, true);
|
||||
if (permissionFlag != PackageManager.PERMISSION_GRANTED
|
||||
@@ -172,23 +171,27 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
|
||||
public boolean canRequestInteractAcrossProfiles(String callingPackage) {
|
||||
Objects.requireNonNull(callingPackage);
|
||||
verifyCallingPackage(callingPackage);
|
||||
|
||||
final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(
|
||||
return canRequestInteractAcrossProfilesUnchecked(
|
||||
callingPackage, mInjector.getCallingUserId());
|
||||
}
|
||||
|
||||
private boolean canRequestInteractAcrossProfilesUnchecked(
|
||||
String packageName, @UserIdInt int userId) {
|
||||
List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(packageName, userId);
|
||||
if (targetUserProfiles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasRequestedAppOpPermission(
|
||||
AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), callingPackage)) {
|
||||
AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) {
|
||||
return false;
|
||||
}
|
||||
return isCrossProfilePackageWhitelisted(callingPackage);
|
||||
return isCrossProfilePackageWhitelisted(packageName);
|
||||
}
|
||||
|
||||
private boolean hasRequestedAppOpPermission(String permission, String packageName) {
|
||||
try {
|
||||
String[] packages = mIpm.getAppOpPermissionPackages(permission);
|
||||
String[] packages =
|
||||
mInjector.getIPackageManager().getAppOpPermissionPackages(permission);
|
||||
return ArrayUtils.contains(packages, packageName);
|
||||
} catch (RemoteException exc) {
|
||||
Slog.e(TAG, "PackageManager dead. Cannot get permission info");
|
||||
@@ -206,7 +209,6 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
|
||||
if (targetUserProfiles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int callingUid = mInjector.getCallingUid();
|
||||
return isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
|
||||
|| isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS, callingUid)
|
||||
@@ -219,7 +221,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
|
||||
private boolean isCrossProfilePackageWhitelisted(String packageName) {
|
||||
final long ident = mInjector.clearCallingIdentity();
|
||||
try {
|
||||
return mDpmi.getAllCrossProfilePackages().contains(packageName);
|
||||
return mInjector.getDevicePolicyManagerInternal()
|
||||
.getAllCrossProfilePackages().contains(packageName);
|
||||
} finally {
|
||||
mInjector.restoreCallingIdentity(ident);
|
||||
}
|
||||
@@ -295,6 +298,104 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInteractAcrossProfilesAppOp(String packageName, @Mode int newMode) {
|
||||
final int callingUid = mInjector.getCallingUid();
|
||||
if (!isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
|
||||
&& !isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS, callingUid)) {
|
||||
throw new SecurityException(
|
||||
"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)) {
|
||||
// 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"
|
||||
+ " app " + packageName);
|
||||
return;
|
||||
}
|
||||
final int[] profileIds =
|
||||
mInjector.getUserManager().getProfileIds(callingUserId, /* enabledOnly= */ false);
|
||||
for (int profileId : profileIds) {
|
||||
if (!isPackageInstalled(packageName, profileId)) {
|
||||
continue;
|
||||
}
|
||||
setInteractAcrossProfilesAppOpForUser(packageName, newMode, profileId);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPackageInstalled(String packageName, @UserIdInt int userId) {
|
||||
final int callingUid = mInjector.getCallingUid();
|
||||
final long identity = mInjector.clearCallingIdentity();
|
||||
try {
|
||||
final PackageInfo info =
|
||||
mInjector.getPackageManagerInternal()
|
||||
.getPackageInfo(
|
||||
packageName,
|
||||
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
|
||||
callingUid,
|
||||
userId);
|
||||
return info != null;
|
||||
} finally {
|
||||
mInjector.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
private void setInteractAcrossProfilesAppOpForUser(
|
||||
String packageName, @Mode int newMode, @UserIdInt int userId) {
|
||||
try {
|
||||
setInteractAcrossProfilesAppOpForUserOrThrow(packageName, newMode, userId);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Slog.e(TAG, "Missing package " + packageName + " on user ID " + userId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setInteractAcrossProfilesAppOpForUserOrThrow(
|
||||
String packageName, @Mode int newMode, @UserIdInt int userId)
|
||||
throws PackageManager.NameNotFoundException {
|
||||
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 "
|
||||
+ packageName + " on user ID " + userId);
|
||||
return;
|
||||
}
|
||||
mInjector.getAppOpsManager()
|
||||
.setMode(OP_INTERACT_ACROSS_PROFILES,
|
||||
uid,
|
||||
packageName,
|
||||
newMode);
|
||||
sendCanInteractAcrossProfilesChangedBroadcast(packageName, uid, UserHandle.of(userId));
|
||||
}
|
||||
|
||||
private boolean currentModeEquals(@Mode int otherMode, String packageName, int uid) {
|
||||
final String op =
|
||||
AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES);
|
||||
return otherMode ==
|
||||
mInjector.getAppOpsManager().unsafeCheckOpNoThrow(op, uid, packageName);
|
||||
}
|
||||
|
||||
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)) {
|
||||
intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY);
|
||||
}
|
||||
mInjector.sendBroadcastAsUser(intent, userHandle);
|
||||
}
|
||||
|
||||
private boolean appDeclaresCrossProfileAttribute(int uid) {
|
||||
return mInjector.getPackageManagerInternal().getPackage(uid).isCrossProfile();
|
||||
}
|
||||
|
||||
private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
|
||||
final long ident = mInjector.clearCallingIdentity();
|
||||
try {
|
||||
@@ -311,8 +412,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
|
||||
mInjector.getAppOpsManager().checkPackage(mInjector.getCallingUid(), callingPackage);
|
||||
}
|
||||
|
||||
private static boolean isPermissionGranted(String permission, int uid) {
|
||||
return PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
|
||||
private boolean isPermissionGranted(String permission, int uid) {
|
||||
return PackageManager.PERMISSION_GRANTED == mInjector.checkComponentPermission(
|
||||
permission, uid, /* owningUid= */-1, /* exported= */ true);
|
||||
}
|
||||
|
||||
@@ -376,6 +477,27 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
|
||||
public ActivityTaskManagerInternal getActivityTaskManagerInternal() {
|
||||
return LocalServices.getService(ActivityTaskManagerInternal.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPackageManager getIPackageManager() {
|
||||
return AppGlobals.getPackageManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DevicePolicyManagerInternal getDevicePolicyManagerInternal() {
|
||||
return LocalServices.getService(DevicePolicyManagerInternal.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBroadcastAsUser(Intent intent, UserHandle user) {
|
||||
mContext.sendBroadcastAsUser(intent, user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkComponentPermission(
|
||||
String permission, int uid, int owningUid, boolean exported) {
|
||||
return ActivityManager.checkComponentPermission(permission, uid, owningUid, exported);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -401,5 +523,13 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
|
||||
ActivityManagerInternal getActivityManagerInternal();
|
||||
|
||||
ActivityTaskManagerInternal getActivityTaskManagerInternal();
|
||||
|
||||
IPackageManager getIPackageManager();
|
||||
|
||||
DevicePolicyManagerInternal getDevicePolicyManagerInternal();
|
||||
|
||||
void sendBroadcastAsUser(Intent intent, UserHandle user);
|
||||
|
||||
int checkComponentPermission(String permission, int uid, int owningUid, boolean exported);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ android_robolectric_test {
|
||||
"platform-test-annotations",
|
||||
"testng",
|
||||
],
|
||||
static_libs: [
|
||||
"androidx.test.ext.truth",
|
||||
],
|
||||
|
||||
instrumentation_for: "FrameworksServicesLib",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static android.app.AppOpsManager.MODE_ALLOWED;
|
||||
import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES;
|
||||
import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
|
||||
import static android.content.pm.CrossProfileApps.ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.AppOpsManager.Mode;
|
||||
import android.app.admin.DevicePolicyManagerInternal;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
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.parsing.AndroidPackage;
|
||||
import android.content.pm.parsing.PackageImpl;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.testing.shadows.ShadowApplicationPackageManager;
|
||||
import com.android.server.testing.shadows.ShadowUserManager;
|
||||
import com.android.server.wm.ActivityTaskManagerInternal;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** Unit tests for {@link CrossProfileAppsServiceImpl}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Presubmit
|
||||
@Config(shadows = {ShadowUserManager.class, ShadowApplicationPackageManager.class})
|
||||
public class CrossProfileAppsServiceImplRoboTest {
|
||||
private static final int CALLING_UID = 1111;
|
||||
private static final String CROSS_PROFILE_APP_PACKAGE_NAME =
|
||||
"com.android.server.pm.crossprofileappsserviceimplrobotest.crossprofileapp";
|
||||
private static final int PERSONAL_PROFILE_USER_ID = 0;
|
||||
private static final int PERSONAL_PROFILE_UID = 2222;
|
||||
private static final int WORK_PROFILE_USER_ID = 10;
|
||||
private static final int WORK_PROFILE_UID = 3333;
|
||||
private static final int OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID = 20;
|
||||
|
||||
private final ContextWrapper mContext = ApplicationProvider.getApplicationContext();
|
||||
private final UserManager mUserManager = mContext.getSystemService(UserManager.class);
|
||||
private final AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
|
||||
private final PackageManager mPackageManager = mContext.getPackageManager();
|
||||
private final TestInjector mInjector = new TestInjector();
|
||||
private final CrossProfileAppsServiceImpl mCrossProfileAppsServiceImpl =
|
||||
new CrossProfileAppsServiceImpl(mContext, mInjector);
|
||||
private final Map<UserHandle, Set<Intent>> mSentUserBroadcasts = new HashMap<>();
|
||||
|
||||
@Mock private PackageManagerInternal mPackageManagerInternal;
|
||||
@Mock private IPackageManager mIPackageManager;
|
||||
@Mock private DevicePolicyManagerInternal mDevicePolicyManagerInternal;
|
||||
|
||||
@Before
|
||||
public void initializeMocks() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mockCrossProfileAppInstalledAndEnabledOnEachProfile();
|
||||
mockCrossProfileAppRequestsInteractAcrossProfiles();
|
||||
mockCrossProfileAppWhitelisted();
|
||||
}
|
||||
|
||||
private void mockCrossProfileAppInstalledAndEnabledOnEachProfile() {
|
||||
// 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();
|
||||
when(mPackageManagerInternal.getPackageInfo(
|
||||
eq(CROSS_PROFILE_APP_PACKAGE_NAME),
|
||||
/* flags= */ anyInt(),
|
||||
/* filterCallingUid= */ anyInt(),
|
||||
eq(PERSONAL_PROFILE_USER_ID)))
|
||||
.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));
|
||||
}
|
||||
|
||||
private PackageInfo buildTestPackageInfo() {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
return packageInfo;
|
||||
}
|
||||
|
||||
private void mockCrossProfileAppRequestsInteractAcrossProfiles() throws Exception {
|
||||
final String permissionName = Manifest.permission.INTERACT_ACROSS_PROFILES;
|
||||
when(mIPackageManager.getAppOpPermissionPackages(permissionName))
|
||||
.thenReturn(new String[] {CROSS_PROFILE_APP_PACKAGE_NAME});
|
||||
}
|
||||
|
||||
private void mockCrossProfileAppWhitelisted() {
|
||||
when(mDevicePolicyManagerInternal.getAllCrossProfilePackages())
|
||||
.thenReturn(Lists.newArrayList(CROSS_PROFILE_APP_PACKAGE_NAME));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpCrossProfileAppUidsAndPackageNames() {
|
||||
ShadowApplicationPackageManager.setPackageUidAsUser(
|
||||
CROSS_PROFILE_APP_PACKAGE_NAME, PERSONAL_PROFILE_UID, PERSONAL_PROFILE_USER_ID);
|
||||
ShadowApplicationPackageManager.setPackageUidAsUser(
|
||||
CROSS_PROFILE_APP_PACKAGE_NAME, WORK_PROFILE_UID, WORK_PROFILE_USER_ID);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void grantPermissions() {
|
||||
grantPermissions(
|
||||
Manifest.permission.MANAGE_APP_OPS_MODES,
|
||||
Manifest.permission.INTERACT_ACROSS_USERS,
|
||||
Manifest.permission.INTERACT_ACROSS_USERS_FULL);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpProfiles() {
|
||||
final ShadowUserManager shadowUserManager = Shadow.extract(mUserManager);
|
||||
shadowUserManager.addProfileIds(
|
||||
PERSONAL_PROFILE_USER_ID,
|
||||
WORK_PROFILE_USER_ID,
|
||||
OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setInteractAcrossProfilesAppOpDefault() {
|
||||
// It seems to be necessary to provide the shadow with the default already specified in
|
||||
// AppOpsManager.
|
||||
final int defaultMode = AppOpsManager.opToDefaultMode(OP_INTERACT_ACROSS_PROFILES);
|
||||
explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, defaultMode);
|
||||
explicitlySetInteractAcrossProfilesAppOp(WORK_PROFILE_UID, defaultMode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setInteractAcrossProfilesAppOp_missingInteractAcrossUsersAndFull_throwsSecurityException() {
|
||||
denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS);
|
||||
denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
|
||||
try {
|
||||
mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
|
||||
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
|
||||
fail();
|
||||
} catch (SecurityException expected) {}
|
||||
}
|
||||
|
||||
@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);
|
||||
assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setInteractAcrossProfilesAppOp_setsAppOpWithUsersAndWithoutFull() {
|
||||
denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
|
||||
mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
|
||||
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
|
||||
assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setInteractAcrossProfilesAppOp_setsAppOpWithFullAndWithoutUsers() {
|
||||
denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS);
|
||||
mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
|
||||
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
|
||||
assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setInteractAcrossProfilesAppOp_setsAppOpOnOtherProfile() {
|
||||
mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
|
||||
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
|
||||
assertThat(getCrossProfileAppOp(WORK_PROFILE_UID)).isEqualTo(MODE_ALLOWED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setInteractAcrossProfilesAppOp_sendsBroadcast() {
|
||||
mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
|
||||
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
|
||||
assertThat(receivedCanInteractAcrossProfilesChangedBroadcast()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setInteractAcrossProfilesAppOp_sendsBroadcastToOtherProfile() {
|
||||
mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
|
||||
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
|
||||
assertThat(receivedCanInteractAcrossProfilesChangedBroadcast(WORK_PROFILE_USER_ID))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setInteractAcrossProfilesAppOp_doesNotSendBroadcastToProfileWithoutPackage() {
|
||||
mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
|
||||
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
|
||||
assertThat(receivedCanInteractAcrossProfilesChangedBroadcast(
|
||||
OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setInteractAcrossProfilesAppOp_toSameAsCurrent_doesNotSendBroadcast() {
|
||||
explicitlySetInteractAcrossProfilesAppOp(MODE_ALLOWED);
|
||||
mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
|
||||
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
|
||||
assertThat(receivedCanInteractAcrossProfilesChangedBroadcast()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setInteractAcrossProfilesAppOp_toAllowed_whenNotAbleToRequest_doesNotSet() {
|
||||
mockCrossProfileAppNotWhitelisted();
|
||||
mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
|
||||
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
|
||||
assertThat(getCrossProfileAppOp()).isNotEqualTo(MODE_ALLOWED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setInteractAcrossProfilesAppOp_toAllowed_whenNotAbleToRequest_doesNotSendBroadcast() {
|
||||
mockCrossProfileAppNotWhitelisted();
|
||||
mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
|
||||
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
|
||||
assertThat(receivedCanInteractAcrossProfilesChangedBroadcast()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setInteractAcrossProfilesAppOp_withoutCrossProfileAttribute_manifestReceiversDoNotGetBroadcast() {
|
||||
declareCrossProfileAttributeOnCrossProfileApp(false);
|
||||
mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
|
||||
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
|
||||
assertThat(receivedManifestCanInteractAcrossProfilesChangedBroadcast()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setInteractAcrossProfilesAppOp_withCrossProfileAttribute_manifestReceiversGetBroadcast() {
|
||||
declareCrossProfileAttributeOnCrossProfileApp(true);
|
||||
mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
|
||||
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
|
||||
assertThat(receivedManifestCanInteractAcrossProfilesChangedBroadcast()).isTrue();
|
||||
}
|
||||
|
||||
private void explicitlySetInteractAcrossProfilesAppOp(@Mode int mode) {
|
||||
explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode);
|
||||
}
|
||||
|
||||
private void explicitlySetInteractAcrossProfilesAppOp(int uid, @Mode int mode) {
|
||||
shadowOf(mAppOpsManager).setMode(
|
||||
OP_INTERACT_ACROSS_PROFILES, uid, CROSS_PROFILE_APP_PACKAGE_NAME, mode);
|
||||
}
|
||||
|
||||
private void grantPermissions(String... permissions) {
|
||||
shadowOf(mContext).grantPermissions(Process.myPid(), CALLING_UID, permissions);
|
||||
}
|
||||
|
||||
private void denyPermissions(String... permissions) {
|
||||
shadowOf(mContext).denyPermissions(Process.myPid(), CALLING_UID, permissions);
|
||||
}
|
||||
|
||||
|
||||
private @Mode int getCrossProfileAppOp() {
|
||||
return getCrossProfileAppOp(PERSONAL_PROFILE_UID);
|
||||
}
|
||||
|
||||
private @Mode int getCrossProfileAppOp(int uid) {
|
||||
return mAppOpsManager.unsafeCheckOpNoThrow(
|
||||
AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES),
|
||||
uid,
|
||||
CROSS_PROFILE_APP_PACKAGE_NAME);
|
||||
}
|
||||
|
||||
private boolean receivedCanInteractAcrossProfilesChangedBroadcast() {
|
||||
return receivedCanInteractAcrossProfilesChangedBroadcast(PERSONAL_PROFILE_USER_ID);
|
||||
}
|
||||
|
||||
private boolean receivedCanInteractAcrossProfilesChangedBroadcast(@UserIdInt int userId) {
|
||||
final UserHandle userHandle = UserHandle.of(userId);
|
||||
if (!mSentUserBroadcasts.containsKey(userHandle)) {
|
||||
return false;
|
||||
}
|
||||
return mSentUserBroadcasts.get(userHandle)
|
||||
.stream()
|
||||
.anyMatch(this::isBroadcastCanInteractAcrossProfilesChanged);
|
||||
}
|
||||
|
||||
private boolean isBroadcastCanInteractAcrossProfilesChanged(Intent intent) {
|
||||
return intent.getAction().equals(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED)
|
||||
&& CROSS_PROFILE_APP_PACKAGE_NAME.equals(intent.getPackage());
|
||||
}
|
||||
|
||||
private void mockCrossProfileAndroidPackage(AndroidPackage androidPackage) {
|
||||
when(mPackageManagerInternal.getPackage(CROSS_PROFILE_APP_PACKAGE_NAME))
|
||||
.thenReturn(androidPackage);
|
||||
when(mPackageManagerInternal.getPackage(PERSONAL_PROFILE_UID)).thenReturn(androidPackage);
|
||||
when(mPackageManagerInternal.getPackage(WORK_PROFILE_UID)).thenReturn(androidPackage);
|
||||
}
|
||||
|
||||
private void mockCrossProfileAppNotWhitelisted() {
|
||||
when(mDevicePolicyManagerInternal.getAllCrossProfilePackages())
|
||||
.thenReturn(new ArrayList<>());
|
||||
}
|
||||
|
||||
private boolean receivedManifestCanInteractAcrossProfilesChangedBroadcast() {
|
||||
final UserHandle userHandle = UserHandle.of(PERSONAL_PROFILE_USER_ID);
|
||||
if (!mSentUserBroadcasts.containsKey(userHandle)) {
|
||||
return false;
|
||||
}
|
||||
return mSentUserBroadcasts.get(userHandle)
|
||||
.stream()
|
||||
.anyMatch(this::isBroadcastManifestCanInteractAcrossProfilesChanged);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private void declareCrossProfileAttributeOnCrossProfileApp(boolean value) {
|
||||
mockCrossProfileAndroidPackage(
|
||||
PackageImpl.forParsing(CROSS_PROFILE_APP_PACKAGE_NAME).setCrossProfile(value));
|
||||
}
|
||||
|
||||
private class TestInjector implements CrossProfileAppsServiceImpl.Injector {
|
||||
|
||||
@Override
|
||||
public int getCallingUid() {
|
||||
return CALLING_UID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @UserIdInt int getCallingUserId() {
|
||||
return PERSONAL_PROFILE_USER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserHandle getCallingUserHandle() {
|
||||
return UserHandle.of(getCallingUserId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long clearCallingIdentity() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreCallingIdentity(long token) {}
|
||||
|
||||
@Override
|
||||
public UserManager getUserManager() {
|
||||
return mUserManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageManagerInternal getPackageManagerInternal() {
|
||||
return mPackageManagerInternal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageManager getPackageManager() {
|
||||
return mPackageManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppOpsManager getAppOpsManager() {
|
||||
return mAppOpsManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivityManagerInternal getActivityManagerInternal() {
|
||||
return LocalServices.getService(ActivityManagerInternal.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivityTaskManagerInternal getActivityTaskManagerInternal() {
|
||||
return LocalServices.getService(ActivityTaskManagerInternal.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPackageManager getIPackageManager() {
|
||||
return mIPackageManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DevicePolicyManagerInternal getDevicePolicyManagerInternal() {
|
||||
return mDevicePolicyManagerInternal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBroadcastAsUser(Intent intent, UserHandle user) {
|
||||
// Robolectric's shadows do not currently support sendBroadcastAsUser.
|
||||
final Set<Intent> broadcasts =
|
||||
mSentUserBroadcasts.containsKey(user)
|
||||
? mSentUserBroadcasts.get(user)
|
||||
: new HashSet<>();
|
||||
broadcasts.add(intent);
|
||||
mSentUserBroadcasts.put(user, broadcasts);
|
||||
mContext.sendBroadcastAsUser(intent, user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkComponentPermission(
|
||||
String permission, int uid, int owningUid, boolean exported) {
|
||||
// ActivityManager#checkComponentPermission calls through to
|
||||
// AppGlobals.getPackageManager()#checkUidPermission, which calls through to
|
||||
// ShadowActivityThread with Robolectric. This method is currently not supported there.
|
||||
return mContext.checkPermission(permission, Process.myPid(), uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.annotation.Resetter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -39,6 +40,7 @@ public class ShadowApplicationPackageManager
|
||||
private static final Map<String, PackageInfo> sPackageInfos = new ArrayMap<>();
|
||||
private static final List<PackageInfo> sInstalledPackages = new ArrayList<>();
|
||||
private static final Map<String, Integer> sPackageUids = new ArrayMap<>();
|
||||
private static final Map<Integer, Map<String, Integer>> sUserPackageUids = new ArrayMap<>();
|
||||
|
||||
/**
|
||||
* Registers the package {@code packageName} to be returned when invoking {@link
|
||||
@@ -58,6 +60,19 @@ public class ShadowApplicationPackageManager
|
||||
sPackageUids.put(packageName, packageUid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the package uid {@code packageUid} for the package {@code packageName} to be returned
|
||||
* when invoking {@link ApplicationPackageManager#getPackageUidAsUser(String, int, int)}.
|
||||
*/
|
||||
public static void setPackageUidAsUser(String packageName, int packageUid, int userId) {
|
||||
final Map<String, Integer> userPackageUids =
|
||||
sUserPackageUids.containsKey(userId)
|
||||
? sUserPackageUids.get(userId)
|
||||
: new HashMap<>();
|
||||
userPackageUids.put(packageName, packageUid);
|
||||
sUserPackageUids.put(userId, userPackageUids);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
|
||||
throws NameNotFoundException {
|
||||
@@ -75,6 +90,10 @@ public class ShadowApplicationPackageManager
|
||||
@Override
|
||||
protected int getPackageUidAsUser(String packageName, int flags, int userId)
|
||||
throws NameNotFoundException {
|
||||
if (sUserPackageUids.containsKey(userId)
|
||||
&& sUserPackageUids.get(userId).containsKey(packageName)) {
|
||||
return sUserPackageUids.get(userId).get(packageName);
|
||||
}
|
||||
if (!sPackageUids.containsKey(packageName)) {
|
||||
throw new NameNotFoundException(packageName);
|
||||
}
|
||||
|
||||
@@ -16,18 +16,46 @@
|
||||
|
||||
package com.android.server.testing.shadows;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.os.UserManager;
|
||||
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** Shadow for {@link UserManager}. */
|
||||
@Implements(UserManager.class)
|
||||
public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager {
|
||||
private final Map<Integer, Set<Integer>> profileIds = new HashMap<>();
|
||||
|
||||
/** @see UserManager#isUserUnlocked() */
|
||||
@Implementation
|
||||
public boolean isUserUnlocked(@UserIdInt int userId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @see UserManager#getProfileIds(int, boolean) () */
|
||||
@Implementation
|
||||
@NonNull
|
||||
public int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) {
|
||||
// Currently, enabledOnly is ignored.
|
||||
if (!profileIds.containsKey(userId)) {
|
||||
return new int[] {userId};
|
||||
}
|
||||
return profileIds.get(userId).stream().mapToInt(Number::intValue).toArray();
|
||||
}
|
||||
|
||||
/** Add a collection of profile IDs, all within the same profile group. */
|
||||
public void addProfileIds(@UserIdInt int... userIds) {
|
||||
final Set<Integer> profileGroup = new HashSet<>();
|
||||
for (int userId : userIds) {
|
||||
profileGroup.add(userId);
|
||||
profileIds.put(userId, profileGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,14 +13,17 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.IApplicationThread;
|
||||
import android.app.admin.DevicePolicyManagerInternal;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
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;
|
||||
@@ -76,6 +79,10 @@ public class CrossProfileAppsServiceImplTest {
|
||||
private ActivityManagerInternal mActivityManagerInternal;
|
||||
@Mock
|
||||
private ActivityTaskManagerInternal mActivityTaskManagerInternal;
|
||||
@Mock
|
||||
private IPackageManager mIPackageManager;
|
||||
@Mock
|
||||
private DevicePolicyManagerInternal mDevicePolicyManagerInternal;
|
||||
|
||||
private TestInjector mTestInjector;
|
||||
private ActivityInfo mActivityInfo;
|
||||
@@ -578,5 +585,26 @@ public class CrossProfileAppsServiceImplTest {
|
||||
public ActivityTaskManagerInternal getActivityTaskManagerInternal() {
|
||||
return mActivityTaskManagerInternal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPackageManager getIPackageManager() {
|
||||
return mIPackageManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DevicePolicyManagerInternal getDevicePolicyManagerInternal() {
|
||||
return mDevicePolicyManagerInternal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBroadcastAsUser(Intent intent, UserHandle user) {
|
||||
mContext.sendBroadcastAsUser(intent, user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkComponentPermission(
|
||||
String permission, int uid, int owningUid, boolean exported) {
|
||||
return ActivityManager.checkComponentPermission(permission, uid, owningUid, exported);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user