diff options
author | Eran Messeri <eranm@google.com> | 2020-12-12 19:18:45 +0000 |
---|---|---|
committer | Eran Messeri <eranm@google.com> | 2021-01-11 20:46:44 +0000 |
commit | a844c5988f1a686db9700cb72502988b2e879c2c (patch) | |
tree | 0f125b338abc67df992a168bad3d986b2d8e1926 | |
parent | 3d91609e47d43f8796b1f4989cdfe5081ba23e4b (diff) |
Implement Enrollment-Specific ID
Implement Enrollment-Specific ID, which is calculated using fixed device
identifiers, as well as the provisioning package and the Organization
Identifier set by the Device Policy Controller.
Test: atest FrameworksServicesTests:EnterpriseSpecificIdCalculatorTest
Test: atest com.android.cts.devicepolicy.MixedDeviceOwnerTest#testEnrollmentSpecificIdCorrectCalculation com.android.cts.devicepolicy.MixedManagedProfileOwnerTest#testEnrollmentSpecificIdCorrectCalculation com.android.cts.devicepolicy.MixedDeviceOwnerTest#testEnrollmentSpecificIdEmptyAndMultipleSet com.android.cts.devicepolicy.MixedManagedProfileOwnerTest#testEnrollmentSpecificIdEmptyAndMultipleSet
Bug: 168627890
Change-Id: I8b24efa6b8c82d6181f2b20bc8880ddeb6caa4c5
9 files changed, 438 insertions, 4 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 2ce0b35a218a..cc385e2a79d8 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -6925,6 +6925,7 @@ package android.app.admin { method @NonNull public java.util.List<java.lang.String> getDelegatedScopes(@Nullable android.content.ComponentName, @NonNull String); method public CharSequence getDeviceOwnerLockScreenInfo(); method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName); + method @NonNull public String getEnrollmentSpecificId(); method @Nullable public android.app.admin.FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(@Nullable android.content.ComponentName); method @Nullable public String getGlobalPrivateDnsHost(@NonNull android.content.ComponentName); method public int getGlobalPrivateDnsMode(@NonNull android.content.ComponentName); @@ -7075,6 +7076,7 @@ package android.app.admin { method @NonNull public java.util.List<java.lang.String> setMeteredDataDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>); method public void setNetworkLoggingEnabled(@Nullable android.content.ComponentName, boolean); method @Deprecated public void setOrganizationColor(@NonNull android.content.ComponentName, int); + method public void setOrganizationId(@NonNull String); method public void setOrganizationName(@NonNull android.content.ComponentName, @Nullable CharSequence); method public void setOverrideApnsEnabled(@NonNull android.content.ComponentName, boolean); method @NonNull public String[] setPackagesSuspended(@NonNull android.content.ComponentName, @NonNull String[], boolean); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 806cb496c4c3..3fd846a3dc90 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -39,7 +39,6 @@ import android.annotation.WorkerThread; import android.app.Activity; import android.app.IServiceConnection; import android.app.KeyguardManager; -import android.app.admin.DevicePolicyManager.DevicePolicyOperation; import android.app.admin.SecurityLog.SecurityEvent; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; @@ -12783,4 +12782,66 @@ public class DevicePolicyManager { } } } + + /** + * Returns an enrollment-specific identifier of this device, which is guaranteed to be the same + * value for the same device, enrolled into the same organization by the same managing app. + * This identifier is high-entropy, useful for uniquely identifying individual devices within + * the same organisation. + * It is available both in a work profile and on a fully-managed device. + * The identifier would be consistent even if the work profile is removed and enrolled again + * (to the same organization), or the device is factory reset and re-enrolled. + + * Can only be called by the Profile Owner or Device Owner, if the + * {@link #setOrganizationId(String)} was previously called. + * If {@link #setOrganizationId(String)} was not called, then the returned value will be an + * empty string. + * + * @return A stable, enrollment-specific identifier. + * @throws SecurityException if the caller is not a profile owner or device owner. + */ + @NonNull public String getEnrollmentSpecificId() { + if (mService == null) { + return ""; + } + + try { + return mService.getEnrollmentSpecificId(); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Sets the Enterprise ID for the work profile or managed device. This is a requirement for + * generating an enrollment-specific ID for the device, see {@link #getEnrollmentSpecificId()}. + * + * It is recommended that the Enterprise ID is at least 6 characters long, and no more than + * 64 characters. + * + * @param enterpriseId An identifier of the organization this work profile or device is + * enrolled into. + */ + public void setOrganizationId(@NonNull String enterpriseId) { + setOrganizationIdForUser(mContext.getPackageName(), enterpriseId, myUserId()); + } + + /** + * Sets the Enterprise ID for the work profile or managed device. This is a requirement for + * generating an enrollment-specific ID for the device, see + * {@link #getEnrollmentSpecificId()}. + * + * @hide + */ + public void setOrganizationIdForUser(@NonNull String packageName, + @NonNull String enterpriseId, @UserIdInt int userId) { + if (mService == null) { + return; + } + try { + mService.setOrganizationIdForUser(packageName, enterpriseId, userId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 4b87bb9cae6c..e81abfe5a409 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -489,4 +489,7 @@ interface IDevicePolicyManager { boolean canProfileOwnerResetPasswordWhenLocked(int userId); void setNextOperationSafety(int operation, boolean safe); + + String getEnrollmentSpecificId(); + void setOrganizationIdForUser(in String callerPackage, in String enterpriseId, int userId); } diff --git a/identity/java/android/security/identity/Util.java b/identity/java/android/security/identity/Util.java index 6eefeb8f3f2a..e56bd5167906 100644 --- a/identity/java/android/security/identity/Util.java +++ b/identity/java/android/security/identity/Util.java @@ -16,6 +16,8 @@ package android.security.identity; +import android.annotation.NonNull; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.InvalidKeyException; @@ -28,7 +30,10 @@ import java.util.Collection; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -class Util { +/** + * @hide + */ +public class Util { private static final String TAG = "Util"; static int[] integerCollectionToArray(Collection<Integer> collection) { @@ -91,8 +96,9 @@ class Util { * 255.DigestSize, where DigestSize is the size of the underlying HMAC. * @return size pseudorandom bytes. */ - static byte[] computeHkdf( - String macAlgorithm, final byte[] ikm, final byte[] salt, final byte[] info, int size) { + @NonNull public static byte[] computeHkdf( + @NonNull String macAlgorithm, @NonNull final byte[] ikm, @NonNull final byte[] salt, + @NonNull final byte[] info, int size) { Mac mac = null; try { mac = Mac.getInstance(macAlgorithm); @@ -137,4 +143,5 @@ class Util { } } + private Util() {} } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index a281180d77d1..48f8b1505d3a 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -134,6 +134,8 @@ class ActiveAdmin { private static final String TAG_ALWAYS_ON_VPN_LOCKDOWN = "vpn-lockdown"; private static final String TAG_COMMON_CRITERIA_MODE = "common-criteria-mode"; private static final String TAG_PASSWORD_COMPLEXITY = "password-complexity"; + private static final String TAG_ORGANIZATION_ID = "organization-id"; + private static final String TAG_ENROLLMENT_SPECIFIC_ID = "enrollment-specific-id"; private static final String ATTR_VALUE = "value"; private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification"; private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications"; @@ -273,6 +275,8 @@ class ActiveAdmin { public String mAlwaysOnVpnPackage; public boolean mAlwaysOnVpnLockdown; boolean mCommonCriteriaMode; + public String mOrganizationId; + public String mEnrollmentSpecificId; ActiveAdmin(DeviceAdminInfo info, boolean isParent) { this.info = info; @@ -533,6 +537,12 @@ class ActiveAdmin { if (mPasswordComplexity != PASSWORD_COMPLEXITY_NONE) { writeAttributeValueToXml(out, TAG_PASSWORD_COMPLEXITY, mPasswordComplexity); } + if (!TextUtils.isEmpty(mOrganizationId)) { + writeTextToXml(out, TAG_ORGANIZATION_ID, mOrganizationId); + } + if (!TextUtils.isEmpty(mEnrollmentSpecificId)) { + writeTextToXml(out, TAG_ENROLLMENT_SPECIFIC_ID, mEnrollmentSpecificId); + } } void writeTextToXml(TypedXmlSerializer out, String tag, String text) throws IOException { @@ -766,6 +776,22 @@ class ActiveAdmin { mCommonCriteriaMode = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_PASSWORD_COMPLEXITY.equals(tag)) { mPasswordComplexity = parser.getAttributeInt(null, ATTR_VALUE); + } else if (TAG_ORGANIZATION_ID.equals(tag)) { + type = parser.next(); + if (type == TypedXmlPullParser.TEXT) { + mOrganizationId = parser.getText(); + } else { + Log.w(DevicePolicyManagerService.LOG_TAG, + "Missing Organization ID."); + } + } else if (TAG_ENROLLMENT_SPECIFIC_ID.equals(tag)) { + type = parser.next(); + if (type == TypedXmlPullParser.TEXT) { + mEnrollmentSpecificId = parser.getText(); + } else { + Log.w(DevicePolicyManagerService.LOG_TAG, + "Missing Enrollment-specific ID."); + } } else { Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -1107,5 +1133,15 @@ class ActiveAdmin { pw.print("mPasswordComplexity="); pw.println(mPasswordComplexity); + + if (!TextUtils.isEmpty(mOrganizationId)) { + pw.print("mOrganizationId="); + pw.println(mOrganizationId); + } + + if (!TextUtils.isEmpty(mEnrollmentSpecificId)) { + pw.print("mEnrollmentSpecificId="); + pw.println(mEnrollmentSpecificId); + } } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 6f1d451e7224..22e9725f49ab 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -15,6 +15,7 @@ */ package com.android.server.devicepolicy; +import android.annotation.NonNull; import android.app.admin.DevicePolicySafetyChecker; import android.app.admin.IDevicePolicyManager; import android.content.ComponentName; @@ -101,4 +102,11 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public boolean canProfileOwnerResetPasswordWhenLocked(int userId) { return false; } + + public String getEnrollmentSpecificId() { + return ""; + } + + public void setOrganizationIdForUser( + @NonNull String callerPackage, @NonNull String enterpriseId, int userId) {} } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index fdbd85a77a5b..9ffe051b0277 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -15577,4 +15577,69 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return true; } } + + @Override + public String getEnrollmentSpecificId() { + if (!mHasFeature) { + return ""; + } + + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization( + isDeviceOwner(caller) || isProfileOwner(caller)); + + synchronized (getLockObject()) { + final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked( + caller.getUserId()); + final String esid = requiredAdmin.mEnrollmentSpecificId; + return esid != null ? esid : ""; + } + } + + @Override + public void setOrganizationIdForUser( + @NonNull String callerPackage, @NonNull String organizationId, int userId) { + if (!mHasFeature) { + return; + } + Objects.requireNonNull(callerPackage); + + final CallerIdentity caller = getCallerIdentity(callerPackage); + // Only the DPC can set this ID. + Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller), + "Only a Device Owner or Profile Owner may set the Enterprise ID."); + // Empty enterprise ID must not be provided in calls to this method. + Preconditions.checkArgument(!TextUtils.isEmpty(organizationId), + "Enterprise ID may not be empty."); + + Log.i(LOG_TAG, + String.format("Setting Enterprise ID to %s for user %d", organizationId, userId)); + + synchronized (getLockObject()) { + ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId); + // As the caller is the system, it must specify the component name of the profile owner + // as a safety check. + Preconditions.checkCallAuthorization( + owner != null && owner.getUserHandle().getIdentifier() == userId, + String.format("The Profile Owner or Device Owner may only set the Enterprise ID" + + " on its own user, called on user %d but owner user is %d", userId, + owner.getUserHandle().getIdentifier())); + Preconditions.checkState( + TextUtils.isEmpty(owner.mOrganizationId) || owner.mOrganizationId.equals( + organizationId), + "The organization ID has been previously set to a different value and cannot " + + "be changed"); + final String dpcPackage = owner.info.getPackageName(); + mInjector.binderWithCleanCallingIdentity(() -> { + EnterpriseSpecificIdCalculator esidCalculator = + new EnterpriseSpecificIdCalculator(mContext); + + final String esid = esidCalculator.calculateEnterpriseId(dpcPackage, + organizationId); + owner.mOrganizationId = organizationId; + owner.mEnrollmentSpecificId = esid; + saveSettingsLocked(userId); + }); + } + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java new file mode 100644 index 000000000000..df7f3084aeb3 --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java @@ -0,0 +1,145 @@ +/* + * 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.devicepolicy; + +import android.content.Context; +import android.content.pm.VerifierDeviceIdentity; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.security.identity.Util; +import android.telephony.TelephonyManager; +import android.text.TextUtils; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; + +import java.nio.ByteBuffer; + +class EnterpriseSpecificIdCalculator { + private static final int PADDED_HW_ID_LENGTH = 16; + private static final int PADDED_PROFILE_OWNER_LENGTH = 64; + private static final int PADDED_ENTERPRISE_ID_LENGTH = 64; + private static final int ESID_LENGTH = 16; + + private final String mImei; + private final String mMeid; + private final String mSerialNumber; + private final String mMacAddress; + + @VisibleForTesting + EnterpriseSpecificIdCalculator(String imei, String meid, String serialNumber, + String macAddress) { + mImei = imei; + mMeid = meid; + mSerialNumber = serialNumber; + mMacAddress = macAddress; + } + + EnterpriseSpecificIdCalculator(Context context) { + TelephonyManager telephonyService = context.getSystemService(TelephonyManager.class); + Preconditions.checkState(telephonyService != null, "Unable to access telephony service"); + mImei = telephonyService.getImei(0); + mMeid = telephonyService.getMeid(0); + mSerialNumber = Build.getSerial(); + WifiManager wifiManager = context.getSystemService(WifiManager.class); + Preconditions.checkState(wifiManager != null, "Unable to access WiFi service"); + final String[] macAddresses = wifiManager.getFactoryMacAddresses(); + if (macAddresses == null || macAddresses.length == 0) { + mMacAddress = ""; + } else { + mMacAddress = macAddresses[0]; + } + } + + private static String getPaddedTruncatedString(String input, int maxLength) { + final String paddedValue = String.format("%" + maxLength + "s", input); + return paddedValue.substring(0, maxLength); + } + + private static String getPaddedHardwareIdentifier(String hardwareIdentifier) { + if (hardwareIdentifier == null) { + hardwareIdentifier = ""; + } + return getPaddedTruncatedString(hardwareIdentifier, PADDED_HW_ID_LENGTH); + } + + String getPaddedImei() { + return getPaddedHardwareIdentifier(mImei); + } + + String getPaddedMeid() { + return getPaddedHardwareIdentifier(mMeid); + } + + String getPaddedSerialNumber() { + return getPaddedHardwareIdentifier(mSerialNumber); + } + + String getPaddedProfileOwnerName(String profileOwnerPackage) { + return getPaddedTruncatedString(profileOwnerPackage, PADDED_PROFILE_OWNER_LENGTH); + } + + String getPaddedEnterpriseId(String enterpriseId) { + return getPaddedTruncatedString(enterpriseId, PADDED_ENTERPRISE_ID_LENGTH); + } + + /** + * Calculates the ESID. + * @param profileOwnerPackage Package of the Device Policy Client that manages the device/ + * profile. May not be null. + * @param enterpriseIdString The identifier for the enterprise in which the device/profile is + * being enrolled. This parameter may not be empty, but may be null. + * If called with {@code null}, will calculate an ESID with empty + * Enterprise ID. + */ + public String calculateEnterpriseId(String profileOwnerPackage, String enterpriseIdString) { + Preconditions.checkArgument(!TextUtils.isEmpty(profileOwnerPackage), + "owner package must be specified."); + + Preconditions.checkArgument(enterpriseIdString == null || !enterpriseIdString.isEmpty(), + "enterprise ID must either be null or non-empty."); + + if (enterpriseIdString == null) { + enterpriseIdString = ""; + } + + final byte[] serialNumber = getPaddedSerialNumber().getBytes(); + final byte[] imei = getPaddedImei().getBytes(); + final byte[] meid = getPaddedMeid().getBytes(); + final byte[] macAddress = mMacAddress.getBytes(); + final int totalIdentifiersLength = serialNumber.length + imei.length + meid.length + + macAddress.length; + final ByteBuffer fixedIdentifiers = ByteBuffer.allocate(totalIdentifiersLength); + fixedIdentifiers.put(serialNumber); + fixedIdentifiers.put(imei); + fixedIdentifiers.put(meid); + fixedIdentifiers.put(macAddress); + + final byte[] dpcPackage = getPaddedProfileOwnerName(profileOwnerPackage).getBytes(); + final byte[] enterpriseId = getPaddedEnterpriseId(enterpriseIdString).getBytes(); + final ByteBuffer info = ByteBuffer.allocate(dpcPackage.length + enterpriseId.length); + info.put(dpcPackage); + info.put(enterpriseId); + final byte[] esidBytes = Util.computeHkdf("HMACSHA256", fixedIdentifiers.array(), null, + info.array(), ESID_LENGTH); + ByteBuffer esidByteBuffer = ByteBuffer.wrap(esidBytes); + + VerifierDeviceIdentity firstId = new VerifierDeviceIdentity(esidByteBuffer.getLong()); + VerifierDeviceIdentity secondId = new VerifierDeviceIdentity(esidByteBuffer.getLong()); + return firstId.toString() + secondId.toString(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/EnterpriseSpecificIdCalculatorTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/EnterpriseSpecificIdCalculatorTest.java new file mode 100644 index 000000000000..c2c1d5b4f3be --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/EnterpriseSpecificIdCalculatorTest.java @@ -0,0 +1,107 @@ +/* + * 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.devicepolicy; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.assertThrows; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class EnterpriseSpecificIdCalculatorTest { + private static final String SOME_IMEI = "56134231542345"; + private static final String SOME_SERIAL_NUMBER = "XZ663CCAJA7"; + private static final String SOME_MAC_ADDRESS = "65:ca:f3:fe:9d:b1"; + private static final String NO_MEID = null; + private static final String SOME_PACKAGE = "com.example.test.dpc"; + private static final String ANOTHER_PACKAGE = "org.example.test.another.dpc"; + private static final String SOME_ENTERPRISE_ID = "73456234"; + private static final String ANOTHER_ENTERPRISE_ID = "243441"; + + private EnterpriseSpecificIdCalculator mEsidCalculator; + + @Before + public void createDefaultEsidCalculator() { + mEsidCalculator = new EnterpriseSpecificIdCalculator(SOME_IMEI, NO_MEID, SOME_SERIAL_NUMBER, + SOME_MAC_ADDRESS); + } + + @Test + public void paddingOfIdentifiers() { + assertThat(mEsidCalculator.getPaddedImei()).isEqualTo(" 56134231542345"); + assertThat(mEsidCalculator.getPaddedMeid()).isEqualTo(" "); + assertThat(mEsidCalculator.getPaddedSerialNumber()).isEqualTo(" XZ663CCAJA7"); + } + + @Test + public void truncationOfLongIdentifier() { + EnterpriseSpecificIdCalculator esidCalculator = new EnterpriseSpecificIdCalculator( + SOME_IMEI, NO_MEID, "XZ663CCAJA7XZ663CCAJA7XZ663CCAJA7", + SOME_MAC_ADDRESS); + assertThat(esidCalculator.getPaddedSerialNumber()).isEqualTo("XZ663CCAJA7XZ663"); + } + + @Test + public void paddingOfPackageName() { + assertThat(mEsidCalculator.getPaddedProfileOwnerName(SOME_PACKAGE)).isEqualTo( + " " + SOME_PACKAGE); + } + + @Test + public void paddingOfEnterpriseId() { + assertThat(mEsidCalculator.getPaddedEnterpriseId(SOME_ENTERPRISE_ID)).isEqualTo( + " " + SOME_ENTERPRISE_ID); + } + + @Test + public void emptyEnterpriseIdYieldsEmptyEsid() { + assertThrows(IllegalArgumentException.class, () -> + mEsidCalculator.calculateEnterpriseId(SOME_PACKAGE, "")); + } + + @Test + public void emptyDpcPackageYieldsEmptyEsid() { + assertThrows(IllegalArgumentException.class, () -> + mEsidCalculator.calculateEnterpriseId("", SOME_ENTERPRISE_ID)); + } + + // On upgrade, an ESID will be calculated with an empty Enterprise ID. This is signalled + // to the EnterpriseSpecificIdCalculator by passing in null. + @Test + public void nullEnterpriseIdYieldsValidEsid() { + assertThat(mEsidCalculator.calculateEnterpriseId(SOME_PACKAGE, null)).isEqualTo( + "C4W7-VUJT-PHSA-HMY53-CLHX-L4HW-L"); + } + + @Test + public void knownValues() { + assertThat( + mEsidCalculator.calculateEnterpriseId(SOME_PACKAGE, SOME_ENTERPRISE_ID)).isEqualTo( + "FP7B-RXQW-Q77F-7J6FC-5RXZ-UJI6-6"); + assertThat(mEsidCalculator.calculateEnterpriseId(SOME_PACKAGE, + ANOTHER_ENTERPRISE_ID)).isEqualTo("ATAL-VPIX-GBNZ-NE3TF-TDEV-3OVO-C"); + assertThat(mEsidCalculator.calculateEnterpriseId(ANOTHER_PACKAGE, + SOME_ENTERPRISE_ID)).isEqualTo("JHU3-6SHH-YLHC-ZGETD-PWNI-7NPQ-S"); + assertThat(mEsidCalculator.calculateEnterpriseId(ANOTHER_PACKAGE, + ANOTHER_ENTERPRISE_ID)).isEqualTo("LEF3-QBEC-UQ6O-RIOCX-TQF6-GRLV-F"); + } +} |