Make owner transfer an atomic operation.
Test: bit FrameworksServicesTests:com.android.server.devicepolicy.TransferOwnershipMetadataManagerTest
Test: runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/devicepolicy/OwnerTransferParamsManagerTest.java
Test: bit FrameworksServicesTests:com.android.server.devicepolicy.DevicePolicyManagerTest#testRevertDeviceOwnership_noMetadataFile
Test: bit FrameworksServicesTests:com.android.server.devicepolicy.DevicePolicyManagerTest#testRevertDeviceOwnership_adminAndDeviceMigrated
Test: bit FrameworksServicesTests:com.android.server.devicepolicy.DevicePolicyManagerTest#testRevertDeviceOwnership_deviceNotMigrated
Test: bit FrameworksServicesTests:com.android.server.devicepolicy.DevicePolicyManagerTest#testRevertDeviceOwnership_adminAndDeviceNotMigrated
Test: bit FrameworksServicesTests:com.android.server.devicepolicy.DevicePolicyManagerTest#testRevertProfileOwnership_noMetadataFile
Test: bit FrameworksServicesTests:com.android.server.devicepolicy.DevicePolicyManagerTest#testRevertProfileOwnership_adminAndProfileMigrated
Test: bit FrameworksServicesTests:com.android.server.devicepolicy.DevicePolicyManagerTest#testRevertProfileOwnership_profileNotMigrated
Test: bit FrameworksServicesTests:com.android.server.devicepolicy.DevicePolicyManagerTest#testRevertProfileOwnership_adminAndProfileNotMigrated
Bug: 69543005
(cherry picked from commit 5a6d391dedcdec13fcc4cf1770e7bf9fc1be8643)
Change-Id: Ic2d729d48fdb47b0ebd60030b45615b0cec174a2
diff --git a/api/current.txt b/api/current.txt
index e85beab..0711ecd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6369,7 +6369,7 @@
field public static final java.lang.String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
field public static final java.lang.String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING";
field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE";
- field public static final java.lang.String EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE = "android.app.extra.TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE";
+ field public static final java.lang.String EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE = "android.app.extra.TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE";
field public static final java.lang.String SUPPORT_TRANSFER_OWNERSHIP_META_DATA = "android.app.support_transfer_ownership";
}
@@ -6457,6 +6457,7 @@
method public boolean getStorageEncryption(android.content.ComponentName);
method public int getStorageEncryptionStatus();
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
+ method public android.os.PersistableBundle getTransferOwnershipBundle();
method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName);
method public android.os.Bundle getUserRestrictions(android.content.ComponentName);
method public java.lang.String getWifiMacAddress(android.content.ComponentName);
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index ffb3aff..d65545d 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -487,15 +487,14 @@
* allows a mobile device management application to pass data to the management application
* instance after owner transfer.
*
- * <p>
- * If the transfer is successful, the new device owner receives the data in
+ * <p>If the transfer is successful, the new owner receives the data in
* {@link DeviceAdminReceiver#onTransferOwnershipComplete(Context, PersistableBundle)}.
* The bundle is not changed during the ownership transfer.
*
* @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
*/
- public static final String EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE =
- "android.app.extra.TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE";
+ public static final String EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE =
+ "android.app.extra.TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE";
/**
* Name under which a device administration component indicates whether it supports transfer of
@@ -1063,7 +1062,7 @@
onUserSwitched(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
} else if (ACTION_TRANSFER_OWNERSHIP_COMPLETE.equals(action)) {
PersistableBundle bundle =
- intent.getParcelableExtra(EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE);
+ intent.getParcelableExtra(EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE);
onTransferOwnershipComplete(context, bundle);
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 95e7fe0..204325e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9144,9 +9144,13 @@
* <li>A profile owner can only be transferred to a new profile owner</li>
* </ul>
*
- * <p>Use the {@code bundle} parameter to pass data to the new administrator. The parameters
+ * <p>Use the {@code bundle} parameter to pass data to the new administrator. The data
* will be received in the
- * {@link DeviceAdminReceiver#onTransferOwnershipComplete(Context, PersistableBundle)} callback.
+ * {@link DeviceAdminReceiver#onTransferOwnershipComplete(Context, PersistableBundle)}
+ * callback of the new administrator.
+ *
+ * <p>The transfer has failed if the original administrator is still the corresponding owner
+ * after calling this method.
*
* <p>The incoming target administrator must have the
* {@link DeviceAdminReceiver#SUPPORT_TRANSFER_OWNERSHIP_META_DATA} <code>meta-data</code> tag
@@ -9157,11 +9161,11 @@
* @param target which {@link DeviceAdminReceiver} we want the new administrator to be
* @param bundle data to be sent to the new administrator
* @throws SecurityException if {@code admin} is not a device owner nor a profile owner
- * @throws IllegalArgumentException if {@code admin} or {@code target} is {@code null},
- * both are components in the same package or {@code target} is not an active admin
+ * @throws IllegalArgumentException if {@code admin} or {@code target} is {@code null}, they
+ * are components in the same package or {@code target} is not an active admin
*/
public void transferOwnership(@NonNull ComponentName admin, @NonNull ComponentName target,
- PersistableBundle bundle) {
+ @Nullable PersistableBundle bundle) {
throwIfParentInstance("transferOwnership");
try {
mService.transferOwnership(admin, target, bundle);
@@ -9434,4 +9438,24 @@
}
return false;
}
+
+ /**
+ * Returns the data passed from the current administrator to the new administrator during an
+ * ownership transfer. This is the same {@code bundle} passed in
+ * {@link #transferOwnership(ComponentName, ComponentName, PersistableBundle)}.
+ *
+ * <p>Returns <code>null</code> if no ownership transfer was started for the calling user.
+ *
+ * @see #transferOwnership
+ * @see DeviceAdminReceiver#onTransferOwnershipComplete(Context, PersistableBundle)
+ */
+ @Nullable
+ public PersistableBundle getTransferOwnershipBundle() {
+ throwIfParentInstance("getTransferOwnershipBundle");
+ try {
+ return mService.getTransferOwnershipBundle();
+ } 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 a5ca4cf..5606d85 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -391,7 +391,9 @@
boolean isLogoutEnabled();
List<String> getDisallowedSystemApps(in ComponentName admin, int userId, String provisioningAction);
+
void transferOwnership(in ComponentName admin, in ComponentName target, in PersistableBundle bundle);
+ PersistableBundle getTransferOwnershipBundle();
void setStartUserSessionMessage(in ComponentName admin, in CharSequence startUserSessionMessage);
void setEndUserSessionMessage(in ComponentName admin, in CharSequence endUserSessionMessage);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 886747cc..1d372d6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -70,6 +70,10 @@
public void transferOwnership(ComponentName admin, ComponentName target, PersistableBundle bundle) {}
+ public PersistableBundle getTransferOwnershipBundle() {
+ return null;
+ }
+
public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm,
ParcelableKeyGenParameterSpec keySpec, int idAttestationFlags,
KeymasterCertificateChain attestationChain) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 38e2168..a45ca42 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -20,7 +20,7 @@
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.USER_OP_SUCCESS;
-import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE;
+import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
import static android.app.admin.DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED;
@@ -58,6 +58,7 @@
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+
import static android.provider.Telephony.Carriers.DPC_URI;
import static android.provider.Telephony.Carriers.ENFORCE_KEY;
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
@@ -67,6 +68,12 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
+
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
+
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.TEXT;
@@ -181,6 +188,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.AtomicFile;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -240,6 +248,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -256,6 +265,9 @@
private static final String DEVICE_POLICIES_XML = "device_policies.xml";
+ private static final String TRANSFER_OWNERSHIP_PARAMETERS_XML =
+ "transfer-ownership-parameters.xml";
+
private static final String TAG_ACCEPTED_CA_CERTIFICATES = "accepted-ca-certificate";
private static final String TAG_LOCK_TASK_COMPONENTS = "lock-task-component";
@@ -460,6 +472,9 @@
private SetupContentObserver mSetupContentObserver;
+ @VisibleForTesting
+ final TransferOwnershipMetadataManager mTransferOwnershipMetadataManager;
+
private final Runnable mRemoteBugreportTimeoutRunnable = new Runnable() {
@Override
public void run() {
@@ -2023,6 +2038,10 @@
void postOnSystemServerInitThreadPool(Runnable runnable) {
SystemServerInitThreadPool.get().submit(runnable, LOG_TAG);
}
+
+ public TransferOwnershipMetadataManager newTransferOwnershipMetadataManager() {
+ return new TransferOwnershipMetadataManager();
+ }
}
/**
@@ -2067,6 +2086,8 @@
mOverlayPackagesProvider = new OverlayPackagesProvider(mContext);
+ mTransferOwnershipMetadataManager = mInjector.newTransferOwnershipMetadataManager();
+
if (!mHasFeature) {
// Skip the rest of the initialization
return;
@@ -3308,6 +3329,29 @@
activityManagerInternal.setSwitchingToSystemUserMessage(
deviceOwner.endUserSessionMessage);
}
+
+ revertTransferOwnershipIfNecessaryLocked();
+ }
+ }
+
+ private void revertTransferOwnershipIfNecessaryLocked() {
+ if (!mTransferOwnershipMetadataManager.metadataFileExists()) {
+ return;
+ }
+ Slog.e(LOG_TAG, "Owner transfer metadata file exists! Reverting transfer.");
+ final TransferOwnershipMetadataManager.Metadata metadata =
+ mTransferOwnershipMetadataManager.loadMetadataFile();
+ // Revert transfer
+ if (metadata.adminType.equals(ADMIN_TYPE_PROFILE_OWNER)) {
+ transferProfileOwnershipLocked(metadata.targetComponent, metadata.sourceComponent,
+ metadata.userId);
+ deleteTransferOwnershipMetadataFileLocked();
+ deleteTransferOwnershipBundleLocked(metadata.userId);
+ } else if (metadata.adminType.equals(ADMIN_TYPE_DEVICE_OWNER)) {
+ transferDeviceOwnershipLocked(metadata.targetComponent, metadata.sourceComponent,
+ metadata.userId);
+ deleteTransferOwnershipMetadataFileLocked();
+ deleteTransferOwnershipBundleLocked(metadata.userId);
}
}
@@ -3568,6 +3612,11 @@
private void transferActiveAdminUncheckedLocked(ComponentName incomingReceiver,
ComponentName outgoingReceiver, int userHandle) {
final DevicePolicyData policy = getUserData(userHandle);
+ if (!policy.mAdminMap.containsKey(outgoingReceiver)
+ && policy.mAdminMap.containsKey(incomingReceiver)) {
+ // Nothing to transfer - the incoming receiver is already the active admin.
+ return;
+ }
final DeviceAdminInfo incomingDeviceInfo = findAdmin(incomingReceiver, userHandle,
/* throwForMissingPermission= */ true);
final ActiveAdmin adminToTransfer = policy.mAdminMap.get(outgoingReceiver);
@@ -3581,7 +3630,6 @@
}
saveSettingsLocked(userHandle);
- //TODO: Make sure we revert back when we detect a failure.
sendAdminCommandLocked(adminToTransfer, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
null, null);
}
@@ -7331,6 +7379,7 @@
mInjector.securityLogSetLoggingEnabledProperty(false);
mSecurityLogMonitor.stop();
setNetworkLoggingActiveInternal(false);
+ deleteTransferOwnershipBundleLocked(userId);
try {
if (mInjector.getIBackupManager() != null) {
@@ -7433,6 +7482,7 @@
clearUserPoliciesLocked(userId);
mOwners.removeProfileOwner(userId);
mOwners.writeProfileOwner(userId);
+ deleteTransferOwnershipBundleLocked(userId);
}
@Override
@@ -12286,10 +12336,9 @@
mOverlayPackagesProvider.getNonRequiredApps(admin, userId, provisioningAction));
}
- //TODO: Add callback information to the javadoc once it is completed.
- //TODO: Make transferOwnership atomic.
@Override
- public void transferOwnership(ComponentName admin, ComponentName target, PersistableBundle bundle) {
+ public void transferOwnership(@NonNull ComponentName admin, @NonNull ComponentName target,
+ @Nullable PersistableBundle bundle) {
if (!mHasFeature) {
return;
}
@@ -12322,12 +12371,38 @@
final long id = mInjector.binderClearCallingIdentity();
try {
- //STOPSHIP add support for COMP, edge cases when device is rebooted/work mode off
synchronized (this) {
+ /*
+ * We must ensure the whole process is atomic to prevent the device from ending up
+ * in an invalid state (e.g. no active admin). This could happen if the device
+ * is rebooted or work mode is turned off mid-transfer.
+ * In order to guarantee atomicity, we:
+ *
+ * 1. Save an atomic journal file describing the transfer process
+ * 2. Perform the transfer itself
+ * 3. Delete the journal file
+ *
+ * That way if the journal file exists on device boot, we know that the transfer
+ * must be reverted back to the original administrator. This logic is implemented in
+ * revertTransferOwnershipIfNecessaryLocked.
+ * */
+ if (bundle == null) {
+ bundle = new PersistableBundle();
+ }
if (isProfileOwner(admin, callingUserId)) {
- transferProfileOwnerLocked(admin, target, callingUserId, bundle);
+ prepareTransfer(admin, target, bundle, callingUserId,
+ ADMIN_TYPE_PROFILE_OWNER);
+ transferProfileOwnershipLocked(admin, target, callingUserId);
+ sendProfileOwnerCommand(DeviceAdminReceiver.ACTION_TRANSFER_OWNERSHIP_COMPLETE,
+ getTransferOwnershipAdminExtras(bundle), callingUserId);
+ postTransfer(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED, callingUserId);
} else if (isDeviceOwner(admin, callingUserId)) {
- transferDeviceOwnerLocked(admin, target, callingUserId, bundle);
+ prepareTransfer(admin, target, bundle, callingUserId,
+ ADMIN_TYPE_DEVICE_OWNER);
+ transferDeviceOwnershipLocked(admin, target, callingUserId);
+ sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_TRANSFER_OWNERSHIP_COMPLETE,
+ getTransferOwnershipAdminExtras(bundle));
+ postTransfer(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, callingUserId);
}
}
} finally {
@@ -12335,43 +12410,48 @@
}
}
+ private void prepareTransfer(ComponentName admin, ComponentName target,
+ PersistableBundle bundle, int callingUserId, String adminType) {
+ saveTransferOwnershipBundleLocked(bundle, callingUserId);
+ mTransferOwnershipMetadataManager.saveMetadataFile(
+ new TransferOwnershipMetadataManager.Metadata(admin, target,
+ callingUserId, adminType));
+ }
+
+ private void postTransfer(String broadcast, int callingUserId) {
+ deleteTransferOwnershipMetadataFileLocked();
+ sendOwnerChangedBroadcast(broadcast, callingUserId);
+ }
+
/**
* Transfers the profile owner for user with id profileOwnerUserId from admin to target.
*/
- private void transferProfileOwnerLocked(ComponentName admin, ComponentName target,
- int profileOwnerUserId, PersistableBundle bundle) {
+ private void transferProfileOwnershipLocked(ComponentName admin, ComponentName target,
+ int profileOwnerUserId) {
transferActiveAdminUncheckedLocked(target, admin, profileOwnerUserId);
mOwners.transferProfileOwner(target, profileOwnerUserId);
Slog.i(LOG_TAG, "Profile owner set: " + target + " on user " + profileOwnerUserId);
mOwners.writeProfileOwner(profileOwnerUserId);
mDeviceAdminServiceController.startServiceForOwner(
target.getPackageName(), profileOwnerUserId, "transfer-profile-owner");
- sendProfileOwnerCommand(DeviceAdminReceiver.ACTION_TRANSFER_OWNERSHIP_COMPLETE,
- getTransferOwnerAdminExtras(bundle), profileOwnerUserId);
- sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED,
- profileOwnerUserId);
}
/**
* Transfers the device owner for user with id userId from admin to target.
*/
- private void transferDeviceOwnerLocked(ComponentName admin, ComponentName target, int userId,
- PersistableBundle bundle) {
+ private void transferDeviceOwnershipLocked(ComponentName admin, ComponentName target, int userId) {
transferActiveAdminUncheckedLocked(target, admin, userId);
- mOwners.transferDeviceOwner(target);
+ mOwners.transferDeviceOwnership(target);
Slog.i(LOG_TAG, "Device owner set: " + target + " on user " + userId);
mOwners.writeDeviceOwner();
mDeviceAdminServiceController.startServiceForOwner(
target.getPackageName(), userId, "transfer-device-owner");
- sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_TRANSFER_OWNERSHIP_COMPLETE,
- getTransferOwnerAdminExtras(bundle));
- sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId);
}
- private Bundle getTransferOwnerAdminExtras(PersistableBundle bundle) {
+ private Bundle getTransferOwnershipAdminExtras(PersistableBundle bundle) {
Bundle extras = new Bundle();
if (bundle != null) {
- extras.putParcelable(EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE, bundle);
+ extras.putParcelable(EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE, bundle);
}
return extras;
}
@@ -12476,6 +12556,34 @@
}
}
+ private void deleteTransferOwnershipMetadataFileLocked() {
+ mTransferOwnershipMetadataManager.deleteMetadataFile();
+ }
+
+ @Override
+ @Nullable
+ public PersistableBundle getTransferOwnershipBundle() {
+ synchronized (this) {
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
+ getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ final File bundleFile = new File(
+ mInjector.environmentGetUserSystemDirectory(callingUserId),
+ TRANSFER_OWNERSHIP_PARAMETERS_XML);
+ if (!bundleFile.exists()) {
+ return null;
+ }
+ try (FileInputStream stream = new FileInputStream(bundleFile)) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+ return PersistableBundle.restoreFromXml(parser);
+ } catch (IOException | XmlPullParserException | IllegalArgumentException e) {
+ Slog.e(LOG_TAG, "Caught exception while trying to load the "
+ + "owner transfer parameters from file " + bundleFile, e);
+ return null;
+ }
+ }
+ }
+
/**
* Returns whether printing is enabled for current user.
* @hide
@@ -12724,4 +12832,32 @@
}
return false;
}
+
+ @VisibleForTesting
+ void saveTransferOwnershipBundleLocked(PersistableBundle bundle, int userId) {
+ final File parametersFile = new File(
+ mInjector.environmentGetUserSystemDirectory(userId),
+ TRANSFER_OWNERSHIP_PARAMETERS_XML);
+ final AtomicFile atomicFile = new AtomicFile(parametersFile);
+ FileOutputStream stream = null;
+ try {
+ stream = atomicFile.startWrite();
+ final XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(stream, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ bundle.saveToXml(serializer);
+ atomicFile.finishWrite(stream);
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(LOG_TAG, "Caught exception while trying to save the "
+ + "owner transfer parameters to file " + parametersFile, e);
+ parametersFile.delete();
+ atomicFile.failWrite(stream);
+ }
+ }
+
+ void deleteTransferOwnershipBundleLocked(int userId) {
+ final File parametersFile = new File(mInjector.environmentGetUserSystemDirectory(userId),
+ TRANSFER_OWNERSHIP_PARAMETERS_XML);
+ parametersFile.delete();
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 2a23888..d2151ed 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -34,6 +34,7 @@
import android.util.SparseArray;
import android.util.Xml;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import org.xmlpull.v1.XmlPullParser;
@@ -110,13 +111,23 @@
private SystemUpdateInfo mSystemUpdateInfo;
private final Object mLock = new Object();
+ private final Injector mInjector;
public Owners(UserManager userManager,
UserManagerInternal userManagerInternal,
PackageManagerInternal packageManagerInternal) {
+ this(userManager, userManagerInternal, packageManagerInternal, new Injector());
+ }
+
+ @VisibleForTesting
+ Owners(UserManager userManager,
+ UserManagerInternal userManagerInternal,
+ PackageManagerInternal packageManagerInternal,
+ Injector injector) {
mUserManager = userManager;
mUserManagerInternal = userManagerInternal;
mPackageManagerInternal = packageManagerInternal;
+ mInjector = injector;
}
/**
@@ -125,7 +136,7 @@
void load() {
synchronized (mLock) {
// First, try to read from the legacy file.
- final File legacy = getLegacyConfigFileWithTestOverride();
+ final File legacy = getLegacyConfigFile();
final List<UserInfo> users = mUserManager.getUsers(true);
@@ -288,7 +299,7 @@
}
}
- void transferDeviceOwner(ComponentName target) {
+ void transferDeviceOwnership(ComponentName target) {
synchronized (mLock) {
// We don't set a name because it's not used anyway.
// See DevicePolicyManagerService#getDeviceOwnerName
@@ -642,7 +653,7 @@
private class DeviceOwnerReadWriter extends FileReadWriter {
protected DeviceOwnerReadWriter() {
- super(getDeviceOwnerFileWithTestOverride());
+ super(getDeviceOwnerFile());
}
@Override
@@ -713,7 +724,7 @@
private final int mUserId;
ProfileOwnerReadWriter(int userId) {
- super(getProfileOwnerFileWithTestOverride(userId));
+ super(getProfileOwnerFile(userId));
mUserId = userId;
}
@@ -870,15 +881,29 @@
}
}
- File getLegacyConfigFileWithTestOverride() {
- return new File(Environment.getDataSystemDirectory(), DEVICE_OWNER_XML_LEGACY);
+ @VisibleForTesting
+ File getLegacyConfigFile() {
+ return new File(mInjector.environmentGetDataSystemDirectory(), DEVICE_OWNER_XML_LEGACY);
}
- File getDeviceOwnerFileWithTestOverride() {
- return new File(Environment.getDataSystemDirectory(), DEVICE_OWNER_XML);
+ @VisibleForTesting
+ File getDeviceOwnerFile() {
+ return new File(mInjector.environmentGetDataSystemDirectory(), DEVICE_OWNER_XML);
}
- File getProfileOwnerFileWithTestOverride(int userId) {
- return new File(Environment.getUserSystemDirectory(userId), PROFILE_OWNER_XML);
+ @VisibleForTesting
+ File getProfileOwnerFile(int userId) {
+ return new File(mInjector.environmentGetUserSystemDirectory(userId), PROFILE_OWNER_XML);
+ }
+
+ @VisibleForTesting
+ public static class Injector {
+ File environmentGetDataSystemDirectory() {
+ return Environment.getDataSystemDirectory();
+ }
+
+ File environmentGetUserSystemDirectory(int userId) {
+ return Environment.getUserSystemDirectory(userId);
+ }
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java
new file mode 100644
index 0000000..1addeb6
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2018 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.os.Environment;
+import android.text.TextUtils;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Preconditions;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Handles reading and writing of the owner transfer metadata file.
+ *
+ * Before we perform a device or profile owner transfer, we save this xml file with information
+ * about the current admin, target admin, user id and admin type (device owner or profile owner).
+ * After {@link DevicePolicyManager#transferOwnership} completes, we delete the file. If after
+ * device boot the file is still there, this indicates that the transfer was interrupted by a
+ * reboot.
+ *
+ * Note that this class is not thread safe.
+ */
+class TransferOwnershipMetadataManager {
+ final static String ADMIN_TYPE_DEVICE_OWNER = "device-owner";
+ final static String ADMIN_TYPE_PROFILE_OWNER = "profile-owner";
+ private final static String TAG_USER_ID = "user-id";
+ private final static String TAG_SOURCE_COMPONENT = "source-component";
+ private final static String TAG_TARGET_COMPONENT = "target-component";
+ private final static String TAG_ADMIN_TYPE = "admin-type";
+ private final static String TAG = TransferOwnershipMetadataManager.class.getName();
+ public static final String OWNER_TRANSFER_METADATA_XML = "owner-transfer-metadata.xml";
+
+ private final Injector mInjector;
+
+ TransferOwnershipMetadataManager() {
+ this(new Injector());
+ }
+
+ @VisibleForTesting
+ TransferOwnershipMetadataManager(Injector injector) {
+ mInjector = injector;
+ }
+
+ boolean saveMetadataFile(Metadata params) {
+ final File transferOwnershipMetadataFile = new File(mInjector.getOwnerTransferMetadataDir(),
+ OWNER_TRANSFER_METADATA_XML);
+ final AtomicFile atomicFile = new AtomicFile(transferOwnershipMetadataFile);
+ FileOutputStream stream = null;
+ try {
+ stream = atomicFile.startWrite();
+ final XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(stream, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ insertSimpleTag(serializer, TAG_USER_ID, Integer.toString(params.userId));
+ insertSimpleTag(serializer,
+ TAG_SOURCE_COMPONENT, params.sourceComponent.flattenToString());
+ insertSimpleTag(serializer,
+ TAG_TARGET_COMPONENT, params.targetComponent.flattenToString());
+ insertSimpleTag(serializer, TAG_ADMIN_TYPE, params.adminType);
+ serializer.endDocument();
+ atomicFile.finishWrite(stream);
+ return true;
+ } catch (IOException e) {
+ Slog.e(TAG, "Caught exception while trying to save Owner Transfer "
+ + "Params to file " + transferOwnershipMetadataFile, e);
+ transferOwnershipMetadataFile.delete();
+ atomicFile.failWrite(stream);
+ }
+ return false;
+ }
+
+ private void insertSimpleTag(XmlSerializer serializer, String tagName, String value)
+ throws IOException {
+ serializer.startTag(null, tagName);
+ serializer.text(value);
+ serializer.endTag(null, tagName);
+ }
+
+ @Nullable
+ Metadata loadMetadataFile() {
+ final File transferOwnershipMetadataFile =
+ new File(mInjector.getOwnerTransferMetadataDir(), OWNER_TRANSFER_METADATA_XML);
+ if (!transferOwnershipMetadataFile.exists()) {
+ return null;
+ }
+ Slog.d(TAG, "Loading TransferOwnershipMetadataManager from "
+ + transferOwnershipMetadataFile);
+ try (FileInputStream stream = new FileInputStream(transferOwnershipMetadataFile)) {
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+ return parseMetadataFile(parser);
+ } catch (IOException | XmlPullParserException | IllegalArgumentException e) {
+ Slog.e(TAG, "Caught exception while trying to load the "
+ + "owner transfer params from file " + transferOwnershipMetadataFile, e);
+ }
+ return null;
+ }
+
+ private Metadata parseMetadataFile(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int type;
+ final int outerDepth = parser.getDepth();
+ int userId = 0;
+ String adminComponent = null;
+ String targetComponent = null;
+ String adminType = null;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ switch (parser.getName()) {
+ case TAG_USER_ID:
+ parser.next();
+ userId = Integer.parseInt(parser.getText());
+ break;
+ case TAG_TARGET_COMPONENT:
+ parser.next();
+ targetComponent = parser.getText();
+ break;
+ case TAG_SOURCE_COMPONENT:
+ parser.next();
+ adminComponent = parser.getText();
+ break;
+ case TAG_ADMIN_TYPE:
+ parser.next();
+ adminType = parser.getText();
+ break;
+ }
+ }
+ return new Metadata(adminComponent, targetComponent, userId, adminType);
+ }
+
+ void deleteMetadataFile() {
+ new File(mInjector.getOwnerTransferMetadataDir(), OWNER_TRANSFER_METADATA_XML).delete();
+ }
+
+ boolean metadataFileExists() {
+ return new File(mInjector.getOwnerTransferMetadataDir(),
+ OWNER_TRANSFER_METADATA_XML).exists();
+ }
+
+ static class Metadata {
+ final int userId;
+ final ComponentName sourceComponent;
+ final ComponentName targetComponent;
+ final String adminType;
+
+ Metadata(@NonNull String sourceComponent, @NonNull String targetComponent,
+ @NonNull int userId, @NonNull String adminType) {
+ this.sourceComponent = ComponentName.unflattenFromString(sourceComponent);
+ this.targetComponent = ComponentName.unflattenFromString(targetComponent);
+ Preconditions.checkNotNull(sourceComponent);
+ Preconditions.checkNotNull(targetComponent);
+ Preconditions.checkStringNotEmpty(adminType);
+ this.userId = userId;
+ this.adminType = adminType;
+ }
+
+ Metadata(@NonNull ComponentName sourceComponent, @NonNull ComponentName targetComponent,
+ @NonNull int userId, @NonNull String adminType) {
+ this(sourceComponent.flattenToString(), targetComponent.flattenToString(),
+ userId, adminType);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Metadata)) {
+ return false;
+ }
+ Metadata params = (Metadata) obj;
+
+ return userId == params.userId
+ && sourceComponent.equals(params.sourceComponent)
+ && targetComponent.equals(params.targetComponent)
+ && TextUtils.equals(adminType, params.adminType);
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = 1;
+ hashCode = 31 * hashCode + userId;
+ hashCode = 31 * hashCode + sourceComponent.hashCode();
+ hashCode = 31 * hashCode + targetComponent.hashCode();
+ hashCode = 31 * hashCode + adminType.hashCode();
+ return hashCode;
+ }
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ public File getOwnerTransferMetadataDir() {
+ return Environment.getDataSystemDirectory();
+ }
+ }
+}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 94e4e30..372b5be 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -60,6 +60,7 @@
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 06f138b..00e27c9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -60,40 +60,33 @@
*/
public static class OwnersTestable extends Owners {
public static final String LEGACY_FILE = "legacy.xml";
- public static final String DEVICE_OWNER_FILE = "device_owner2.xml";
- public static final String PROFILE_OWNER_FILE = "profile_owner.xml";
-
- private final File mLegacyFile;
- private final File mDeviceOwnerFile;
- private final File mUsersDataDir;
public OwnersTestable(MockSystemServices services) {
super(services.userManager, services.userManagerInternal,
- services.packageManagerInternal);
- mLegacyFile = new File(services.dataDir, LEGACY_FILE);
- mDeviceOwnerFile = new File(services.dataDir, DEVICE_OWNER_FILE);
- mUsersDataDir = new File(services.dataDir, "users");
+ services.packageManagerInternal, new MockInjector(services));
}
- @Override
- File getLegacyConfigFileWithTestOverride() {
- return mLegacyFile;
- }
+ static class MockInjector extends Injector {
+ private final MockSystemServices mServices;
- @Override
- File getDeviceOwnerFileWithTestOverride() {
- return mDeviceOwnerFile;
- }
+ private MockInjector(MockSystemServices services) {
+ mServices = services;
+ }
- @Override
- File getProfileOwnerFileWithTestOverride(int userId) {
- final File userDir = new File(mUsersDataDir, String.valueOf(userId));
- return new File(userDir, PROFILE_OWNER_FILE);
+ @Override
+ File environmentGetDataSystemDirectory() {
+ return mServices.dataDir;
+ }
+
+ @Override
+ File environmentGetUserSystemDirectory(int userId) {
+ return mServices.environment.getUserSystemDirectory(userId);
+ }
}
}
public final DpmMockContext context;
- private final MockInjector mMockInjector;
+ protected final MockInjector mMockInjector;
public DevicePolicyManagerServiceTestable(MockSystemServices services, DpmMockContext context) {
this(new MockInjector(services, context));
@@ -124,8 +117,7 @@
}
}
-
- private static class MockInjector extends Injector {
+ static class MockInjector extends Injector {
public final DpmMockContext context;
private final MockSystemServices services;
@@ -133,7 +125,7 @@
// Key is a pair of uri and userId
private final Map<Pair<Uri, Integer>, ContentObserver> mContentObservers = new ArrayMap<>();
- private MockInjector(MockSystemServices services, DpmMockContext context) {
+ public MockInjector(MockSystemServices services, DpmMockContext context) {
super(context);
this.services = services;
this.context = context;
@@ -449,5 +441,11 @@
void postOnSystemServerInitThreadPool(Runnable runnable) {
runnable.run();
}
+
+ @Override
+ public TransferOwnershipMetadataManager newTransferOwnershipMetadataManager() {
+ return new TransferOwnershipMetadataManager(
+ new TransferOwnershipMetadataManagerTest.MockInjector());
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index 85835f7..cb6a747 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -42,21 +42,21 @@
{
final OwnersTestable owners = new OwnersTestable(getServices());
- DpmTestUtils.writeToFile(owners.getLegacyConfigFileWithTestOverride(),
+ DpmTestUtils.writeToFile(owners.getLegacyConfigFile(),
DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test01/input.xml"));
owners.load();
// The legacy file should be removed.
- assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
+ assertFalse(owners.getLegacyConfigFile().exists());
// File was empty, so no new files should be created.
- assertFalse(owners.getDeviceOwnerFileWithTestOverride().exists());
+ assertFalse(owners.getDeviceOwnerFile().exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(10).exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(11).exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(20).exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(21).exists());
+ assertFalse(owners.getProfileOwnerFile(10).exists());
+ assertFalse(owners.getProfileOwnerFile(11).exists());
+ assertFalse(owners.getProfileOwnerFile(20).exists());
+ assertFalse(owners.getProfileOwnerFile(21).exists());
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
@@ -95,20 +95,20 @@
{
final OwnersTestable owners = new OwnersTestable(getServices());
- DpmTestUtils.writeToFile(owners.getLegacyConfigFileWithTestOverride(),
+ DpmTestUtils.writeToFile(owners.getLegacyConfigFile(),
DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test02/input.xml"));
owners.load();
// The legacy file should be removed.
- assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
+ assertFalse(owners.getLegacyConfigFile().exists());
- assertTrue(owners.getDeviceOwnerFileWithTestOverride().exists()); // TODO Check content
+ assertTrue(owners.getDeviceOwnerFile().exists()); // TODO Check content
- assertFalse(owners.getProfileOwnerFileWithTestOverride(10).exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(11).exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(20).exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(21).exists());
+ assertFalse(owners.getProfileOwnerFile(10).exists());
+ assertFalse(owners.getProfileOwnerFile(11).exists());
+ assertFalse(owners.getProfileOwnerFile(20).exists());
+ assertFalse(owners.getProfileOwnerFile(21).exists());
assertTrue(owners.hasDeviceOwner());
assertEquals(null, owners.getDeviceOwnerName());
@@ -153,20 +153,20 @@
{
final OwnersTestable owners = new OwnersTestable(getServices());
- DpmTestUtils.writeToFile(owners.getLegacyConfigFileWithTestOverride(),
+ DpmTestUtils.writeToFile(owners.getLegacyConfigFile(),
DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test03/input.xml"));
owners.load();
// The legacy file should be removed.
- assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
+ assertFalse(owners.getLegacyConfigFile().exists());
- assertFalse(owners.getDeviceOwnerFileWithTestOverride().exists());
+ assertFalse(owners.getDeviceOwnerFile().exists());
- assertTrue(owners.getProfileOwnerFileWithTestOverride(10).exists());
- assertTrue(owners.getProfileOwnerFileWithTestOverride(11).exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(20).exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(21).exists());
+ assertTrue(owners.getProfileOwnerFile(10).exists());
+ assertTrue(owners.getProfileOwnerFile(11).exists());
+ assertFalse(owners.getProfileOwnerFile(20).exists());
+ assertFalse(owners.getProfileOwnerFile(21).exists());
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
@@ -231,20 +231,20 @@
{
final OwnersTestable owners = new OwnersTestable(getServices());
- DpmTestUtils.writeToFile(owners.getLegacyConfigFileWithTestOverride(),
+ DpmTestUtils.writeToFile(owners.getLegacyConfigFile(),
DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test04/input.xml"));
owners.load();
// The legacy file should be removed.
- assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
+ assertFalse(owners.getLegacyConfigFile().exists());
- assertTrue(owners.getDeviceOwnerFileWithTestOverride().exists());
+ assertTrue(owners.getDeviceOwnerFile().exists());
- assertTrue(owners.getProfileOwnerFileWithTestOverride(10).exists());
- assertTrue(owners.getProfileOwnerFileWithTestOverride(11).exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(20).exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(21).exists());
+ assertTrue(owners.getProfileOwnerFile(10).exists());
+ assertTrue(owners.getProfileOwnerFile(11).exists());
+ assertFalse(owners.getProfileOwnerFile(20).exists());
+ assertFalse(owners.getProfileOwnerFile(21).exists());
assertTrue(owners.hasDeviceOwner());
assertEquals(null, owners.getDeviceOwnerName());
@@ -341,20 +341,20 @@
{
final OwnersTestable owners = new OwnersTestable(getServices());
- DpmTestUtils.writeToFile(owners.getLegacyConfigFileWithTestOverride(),
+ DpmTestUtils.writeToFile(owners.getLegacyConfigFile(),
DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test05/input.xml"));
owners.load();
// The legacy file should be removed.
- assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
+ assertFalse(owners.getLegacyConfigFile().exists());
// Note device initializer is no longer supported. No need to write the DO file.
- assertFalse(owners.getDeviceOwnerFileWithTestOverride().exists());
+ assertFalse(owners.getDeviceOwnerFile().exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(10).exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(11).exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(20).exists());
+ assertFalse(owners.getProfileOwnerFile(10).exists());
+ assertFalse(owners.getProfileOwnerFile(11).exists());
+ assertFalse(owners.getProfileOwnerFile(20).exists());
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
@@ -397,19 +397,19 @@
{
final OwnersTestable owners = new OwnersTestable(getServices());
- DpmTestUtils.writeToFile(owners.getLegacyConfigFileWithTestOverride(),
+ DpmTestUtils.writeToFile(owners.getLegacyConfigFile(),
DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test06/input.xml"));
owners.load();
// The legacy file should be removed.
- assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
+ assertFalse(owners.getLegacyConfigFile().exists());
- assertTrue(owners.getDeviceOwnerFileWithTestOverride().exists());
+ assertTrue(owners.getDeviceOwnerFile().exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(10).exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(11).exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(20).exists());
+ assertFalse(owners.getProfileOwnerFile(10).exists());
+ assertFalse(owners.getProfileOwnerFile(11).exists());
+ assertFalse(owners.getProfileOwnerFile(20).exists());
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
@@ -451,16 +451,16 @@
final OwnersTestable owners = new OwnersTestable(getServices());
// First, migrate to create new-style config files.
- DpmTestUtils.writeToFile(owners.getLegacyConfigFileWithTestOverride(),
+ DpmTestUtils.writeToFile(owners.getLegacyConfigFile(),
DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test04/input.xml"));
owners.load();
- assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
+ assertFalse(owners.getLegacyConfigFile().exists());
- assertTrue(owners.getDeviceOwnerFileWithTestOverride().exists());
- assertTrue(owners.getProfileOwnerFileWithTestOverride(10).exists());
- assertTrue(owners.getProfileOwnerFileWithTestOverride(11).exists());
+ assertTrue(owners.getDeviceOwnerFile().exists());
+ assertTrue(owners.getProfileOwnerFile(10).exists());
+ assertTrue(owners.getProfileOwnerFile(11).exists());
// Then clear all information and save.
owners.clearDeviceOwner();
@@ -475,8 +475,8 @@
owners.writeProfileOwner(21);
// Now all files should be removed.
- assertFalse(owners.getDeviceOwnerFileWithTestOverride().exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(10).exists());
- assertFalse(owners.getProfileOwnerFileWithTestOverride(11).exists());
+ assertFalse(owners.getDeviceOwnerFile().exists());
+ assertFalse(owners.getProfileOwnerFile(10).exists());
+ assertFalse(owners.getProfileOwnerFile(11).exists());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java
new file mode 100644
index 0000000..03cabb2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 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.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
+
+
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
+ .OWNER_TRANSFER_METADATA_XML;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Environment;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.devicepolicy.TransferOwnershipMetadataManager.Injector;
+import com.android.server.devicepolicy.TransferOwnershipMetadataManager.Metadata;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+* Unit tests for {@link TransferOwnershipMetadataManager}.
+ *
+ * bit FrameworksServicesTests:com.android.server.devicepolicy.TransferOwnershipMetadataManagerTest
+ * runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java
+* */
+
+@RunWith(AndroidJUnit4.class)
+public class TransferOwnershipMetadataManagerTest {
+ private final static String ADMIN_PACKAGE = "com.dummy.admin.package";
+ private final static String TARGET_PACKAGE = "com.dummy.target.package";
+ private final static int USER_ID = 123;
+ private final static Metadata TEST_PARAMS = new Metadata(ADMIN_PACKAGE,
+ TARGET_PACKAGE, USER_ID, ADMIN_TYPE_DEVICE_OWNER);
+
+ private MockInjector mMockInjector;
+
+ @Before
+ public void setUp() {
+ mMockInjector = new MockInjector();
+ getOwnerTransferParams().deleteMetadataFile();
+ }
+
+ @Test
+ public void testSave() {
+ TransferOwnershipMetadataManager paramsManager = getOwnerTransferParams();
+ assertTrue(paramsManager.saveMetadataFile(TEST_PARAMS));
+ assertTrue(paramsManager.metadataFileExists());
+ }
+
+ @Test
+ public void testFileContentValid() {
+ TransferOwnershipMetadataManager paramsManager = getOwnerTransferParams();
+ assertTrue(paramsManager.saveMetadataFile(TEST_PARAMS));
+ Path path = Paths.get(new File(mMockInjector.getOwnerTransferMetadataDir(),
+ OWNER_TRANSFER_METADATA_XML).getAbsolutePath());
+ try {
+ String contents = new String(Files.readAllBytes(path), Charset.forName("UTF-8"));
+ assertEquals(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<user-id>" + USER_ID + "</user-id>\n"
+ + "<admin-component>" + ADMIN_PACKAGE + "</admin-component>\n"
+ + "<target-component>" + TARGET_PACKAGE + "</target-component>\n"
+ + "<admin-type>" + ADMIN_TYPE_DEVICE_OWNER + "</admin-type>\n",
+ contents);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void testLoad() {
+ TransferOwnershipMetadataManager paramsManager = getOwnerTransferParams();
+ paramsManager.saveMetadataFile(TEST_PARAMS);
+ assertEquals(TEST_PARAMS, paramsManager.loadMetadataFile());
+ }
+
+ @Test
+ public void testDelete() {
+ TransferOwnershipMetadataManager paramsManager = getOwnerTransferParams();
+ paramsManager.saveMetadataFile(TEST_PARAMS);
+ paramsManager.deleteMetadataFile();
+ assertFalse(paramsManager.metadataFileExists());
+ }
+
+ @After
+ public void tearDown() {
+ getOwnerTransferParams().deleteMetadataFile();
+ }
+
+ private TransferOwnershipMetadataManager getOwnerTransferParams() {
+ return new TransferOwnershipMetadataManager(mMockInjector);
+ }
+
+ static class MockInjector extends Injector {
+ @Override
+ public File getOwnerTransferMetadataDir() {
+ return Environment.getExternalStorageDirectory();
+ }
+ }
+
+}