import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
/**
* Public interface for managing policies enforced on a device. Most clients of this class must be
return 0;
}
}
+
+ /**
+ * @hide
+ * Indicates the entity that controls the device or profile owner. A user/profile is considered
+ * affiliated if it is managed by the same entity as the device.
+ *
+ * <p> By definition, the user that the device owner runs on is always affiliated. Any other
+ * user/profile is considered affiliated if the following conditions are both met:
+ * <ul>
+ * <li>The device owner and the user's/profile's profile owner have called this method,
+ * specifying a set of opaque affiliation ids each. If the sets specified by the device owner
+ * and a profile owner intersect, they must have come from the same source, which means that
+ * the device owner and profile owner are controlled by the same entity.</li>
+ * <li>The device owner's and profile owner's package names are the same.</li>
+ * </ul>
+ *
+ * @param admin Which profile or device owner this request is associated with.
+ * @param ids A set of opaque affiliation ids.
+ */
+ public void setAffiliationIds(@NonNull ComponentName admin, Set<String> ids) {
+ try {
+ mService.setAffiliationIds(admin, new ArrayList<String>(ids));
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+
+ /**
+ * @hide
+ * Returns whether this user/profile is affiliated with the device. See
+ * {@link #setAffiliationIds} for the definition of affiliation.
+ *
+ * @return whether this user/profile is affiliated with the device.
+ */
+ public boolean isAffiliatedUser() {
+ try {
+ return mService != null && mService.isAffiliatedUser();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ return false;
+ }
+ }
}
void setOrganizationColor(in ComponentName admin, in int color);
int getOrganizationColor(in ComponentName admin);
int getOrganizationColorForUser(int userHandle);
+
+ void setAffiliationIds(in ComponentName admin, in List<String> ids);
+ boolean isAffiliatedUser();
}
private static final String TAG_STATUS_BAR = "statusbar";
+ private static final String TAG_AFFILIATION_ID = "affiliation-id";
+
private static final String ATTR_DISABLED = "disabled";
private static final String DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML =
String mApplicationRestrictionsManagingPackage;
+ Set<String> mAffiliationIds = new ArraySet<>();
+
public DevicePolicyData(int userHandle) {
mUserHandle = userHandle;
}
out.endTag(null, DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML);
}
+ for (String id : policy.mAffiliationIds) {
+ out.startTag(null, TAG_AFFILIATION_ID);
+ out.attribute(null, "id", id);
+ out.endTag(null, TAG_AFFILIATION_ID);
+ }
+
out.endTag(null, "policies");
out.endDocument();
policy.mLockTaskPackages.clear();
policy.mAdminList.clear();
policy.mAdminMap.clear();
+ policy.mAffiliationIds.clear();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
parser.getAttributeValue(null, ATTR_DISABLED));
} else if (DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML.equals(tag)) {
policy.doNotAskCredentialsOnBoot = true;
+ } else if (TAG_AFFILIATION_ID.equals(tag)) {
+ policy.mAffiliationIds.add(parser.getAttributeValue(null, "id"));
} else {
Slog.w(LOG_TAG, "Unknown tag: " + tag);
XmlUtils.skipCurrentTag(parser);
: ActiveAdmin.DEF_ORGANIZATION_COLOR;
}
}
+
+ @Override
+ public void setAffiliationIds(ComponentName admin, List<String> ids) {
+ final Set<String> affiliationIds = new ArraySet<String>(ids);
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
+
+ synchronized (this) {
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ getUserData(callingUserId).mAffiliationIds = affiliationIds;
+ saveSettingsLocked(callingUserId);
+ if (callingUserId != UserHandle.USER_SYSTEM && isDeviceOwner(admin, callingUserId)) {
+ // Affiliation ids specified by the device owner are additionally stored in
+ // UserHandle.USER_SYSTEM's DevicePolicyData.
+ getUserData(UserHandle.USER_SYSTEM).mAffiliationIds = affiliationIds;
+ saveSettingsLocked(UserHandle.USER_SYSTEM);
+ }
+ }
+ }
+
+ @Override
+ public boolean isAffiliatedUser() {
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
+
+ synchronized (this) {
+ if (mOwners.getDeviceOwnerUserId() == callingUserId) {
+ // The user that the DO is installed on is always affiliated.
+ return true;
+ }
+ final ComponentName profileOwner = getProfileOwner(callingUserId);
+ if (profileOwner == null
+ || !profileOwner.getPackageName().equals(mOwners.getDeviceOwnerPackageName())) {
+ return false;
+ }
+ final Set<String> userAffiliationIds = getUserData(callingUserId).mAffiliationIds;
+ final Set<String> deviceAffiliationIds =
+ getUserData(UserHandle.USER_SYSTEM).mAffiliationIds;
+ for (String id : userAffiliationIds) {
+ if (deviceAffiliationIds.contains(id)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
import android.os.UserHandle;
import android.os.UserManager;
import android.test.MoreAsserts;
+import android.util.ArraySet;
import android.util.Pair;
import org.mockito.ArgumentCaptor;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
assertNull(dpm.getLongSupportMessage(admin1));
}
}
+
+ /**
+ * Test for:
+ * {@link DevicePolicyManager#setAffiliationIds}
+ * {@link DevicePolicyManager#isAffiliatedUser}
+ */
+ public void testUserAffiliation() throws Exception {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+ // Check that the system user is unaffiliated.
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ assertFalse(dpm.isAffiliatedUser());
+
+ // Set a device owner on the system user. Check that the system user becomes affiliated.
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+ assertTrue(dpm.setDeviceOwner(admin1, "owner-name"));
+ assertTrue(dpm.isAffiliatedUser());
+
+ // Install a profile owner whose package name matches the device owner on a test user. Check
+ // that the test user is unaffiliated.
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ setAsProfileOwner(admin2);
+ assertFalse(dpm.isAffiliatedUser());
+
+ // Have the profile owner specify a set of affiliation ids. Check that the test user remains
+ // unaffiliated.
+ final Set<String> userAffiliationIds = new ArraySet<>();
+ userAffiliationIds.add("red");
+ userAffiliationIds.add("green");
+ userAffiliationIds.add("blue");
+ dpm.setAffiliationIds(admin2, userAffiliationIds);
+ assertFalse(dpm.isAffiliatedUser());
+
+ // Have the device owner specify a set of affiliation ids that do not intersect with those
+ // specified by the profile owner. Check that the test user remains unaffiliated.
+ final Set<String> deviceAffiliationIds = new ArraySet<>();
+ deviceAffiliationIds.add("cyan");
+ deviceAffiliationIds.add("yellow");
+ deviceAffiliationIds.add("magenta");
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ dpm.setAffiliationIds(admin1, deviceAffiliationIds);
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ assertFalse(dpm.isAffiliatedUser());
+
+ // Have the profile owner specify a set of affiliation ids that intersect with those
+ // specified by the device owner. Check that the test user becomes affiliated.
+ userAffiliationIds.add("yellow");
+ dpm.setAffiliationIds(admin2, userAffiliationIds);
+ assertTrue(dpm.isAffiliatedUser());
+
+ // Change the profile owner to one whose package name does not match the device owner. Check
+ // that the test user is not affiliated anymore.
+ dpm.clearProfileOwner(admin2);
+ final ComponentName admin = new ComponentName("test", "test");
+ markPackageAsInstalled(admin.getPackageName(), null, DpmMockContext.CALLER_USER_HANDLE);
+ assertTrue(dpm.setProfileOwner(admin, "owner-name", DpmMockContext.CALLER_USER_HANDLE));
+ assertFalse(dpm.isAffiliatedUser());
+
+ // Check that the system user remains affiliated.
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ assertTrue(dpm.isAffiliatedUser());
+ }
}
return mMockContext;
}
+ protected void markPackageAsInstalled(String packageName, ApplicationInfo ai, int userId)
+ throws Exception {
+ final PackageInfo pi = DpmTestUtils.cloneParcelable(
+ mRealTestContext.getPackageManager().getPackageInfo(
+ mRealTestContext.getPackageName(), 0));
+ assertTrue(pi.applicationInfo.flags != 0);
+
+ if (ai != null) {
+ pi.applicationInfo = ai;
+ }
+
+ doReturn(pi).when(mMockContext.ipackageManager).getPackageInfo(
+ eq(packageName),
+ eq(0),
+ eq(userId));
+ }
protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid)
throws Exception {
eq(UserHandle.getUserId(packageUid)));
// Set up getPackageInfo().
-
- final PackageInfo pi = DpmTestUtils.cloneParcelable(
- mRealTestContext.getPackageManager().getPackageInfo(
- admin.getPackageName(), 0));
- assertTrue(pi.applicationInfo.flags != 0);
-
- pi.applicationInfo = ai;
-
- doReturn(pi).when(mMockContext.ipackageManager).getPackageInfo(
- eq(admin.getPackageName()),
- eq(0),
- eq(UserHandle.getUserId(packageUid)));
+ markPackageAsInstalled(admin.getPackageName(), ai, UserHandle.getUserId(packageUid));
}
}