Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8c4e05fc authored by Felipe Leme's avatar Felipe Leme
Browse files

Refactored FactoryResetter to use a Builder.

Test: atest FrameworksServicesTests:DevicePolicyManagerTest \
            FrameworksMockingServicesTests:FactoryResetterTest

Bug: 171603586
Change-Id: I643a9d580bc06bd09cedcfcb36892ff2495de93e
parent f8dcca3c
Loading
Loading
Loading
Loading
+6 −5
Original line number Original line Diff line number Diff line
@@ -1032,9 +1032,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        Slog.w(LOG_TAG, "factoryReset(): " + reason);
        Slog.w(LOG_TAG, "factoryReset(): " + reason);
        final long identity = Binder.clearCallingIdentity();
        final long identity = Binder.clearCallingIdentity();
        try {
        try {
            FactoryResetter.factoryReset(mContext, /* shutdown= */ false, reason,
            FactoryResetter.newBuilder(mContext).setReason(reason).build().factoryReset();
                    /* force= */ false, /* wipeEuicc= */ false, /* wipeAdoptableStorage= */ false,
                    /* wipeFactoryResetProtection= */ false);
        } catch (IOException e) {
        } catch (IOException e) {
            // Shouldn't happen.
            // Shouldn't happen.
            Slog.wtf(LOG_TAG, "Could not factory reset", e);
            Slog.wtf(LOG_TAG, "Could not factory reset", e);
@@ -1295,8 +1293,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        void recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force,
        void recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force,
                boolean wipeEuicc, boolean wipeExtRequested, boolean wipeResetProtectionData)
                boolean wipeEuicc, boolean wipeExtRequested, boolean wipeResetProtectionData)
                        throws IOException {
                        throws IOException {
            FactoryResetter.factoryReset(mContext, shutdown, reason, force, wipeEuicc,
            FactoryResetter.newBuilder(mContext).setReason(reason).setShutdown(shutdown)
                    wipeExtRequested, wipeResetProtectionData);
                    .setForce(force).setWipeEuicc(wipeEuicc)
                    .setWipeAdoptableStorage(wipeExtRequested)
                    .setWipeFactoryResetProtection(wipeResetProtectionData)
                    .build().factoryReset();
        }
        }
        boolean systemPropertiesGetBoolean(String key, boolean def) {
        boolean systemPropertiesGetBoolean(String key, boolean def) {
+117 −23
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.server.devicepolicy;
package com.android.server.devicepolicy;


import android.annotation.Nullable;
import android.content.Context;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.os.RecoverySystem;
import android.os.RecoverySystem;
@@ -27,37 +28,43 @@ import android.util.Log;
import com.android.internal.util.Preconditions;
import com.android.internal.util.Preconditions;


import java.io.IOException;
import java.io.IOException;
import java.util.Objects;


/**
/**
 * Entry point for "factory reset" requests.
 * Entry point for "factory reset" requests.
 */
 */
final class FactoryResetter {
public final class FactoryResetter {


    private static final String TAG = FactoryResetter.class.getSimpleName();
    private static final String TAG = FactoryResetter.class.getSimpleName();


    // TODO(b/171603586): use an object that's constructed with a Builder instead (then update
    private final Context mContext;
    // javadoc)
    private final @Nullable String mReason;
    private final boolean mShutdown;
    private final boolean mForce;
    private final boolean mWipeEuicc;
    private final boolean mWipeAdoptableStorage;
    private final boolean mWipeFactoryResetProtection;


    /**
    /**
     * Factory reset the device.
     * Factory reset the device according to the builder's arguments.
     */
     */
    public static void factoryReset(Context context, boolean shutdown, String reason,
    public void factoryReset() throws IOException {
            boolean force, boolean wipeEuicc, boolean wipeAdoptableStorage,
        Log.i(TAG, String.format("factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b"
            boolean wipeFactoryResetProtection) throws IOException {
                + ", wipeAdoptableStorage=%b, wipeFRP=%b", mReason, mShutdown, mForce, mWipeEuicc,
        Log.i(TAG, "factoryReset(): shutdown=" + shutdown + ", force=" + force
                mWipeAdoptableStorage, mWipeFactoryResetProtection));
                + ", wipeEuicc=" + wipeEuicc + ", wipeAdoptableStorage=" + wipeAdoptableStorage

                + ", wipeFRP=" + wipeFactoryResetProtection);
        Preconditions.checkCallAuthorization(mContext.checkCallingOrSelfPermission(

        Preconditions.checkCallAuthorization(context.checkCallingOrSelfPermission(
                android.Manifest.permission.MASTER_CLEAR) == PackageManager.PERMISSION_GRANTED);
                android.Manifest.permission.MASTER_CLEAR) == PackageManager.PERMISSION_GRANTED);


        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
        UserManager um = mContext.getSystemService(UserManager.class);
        if (!force && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
        if (!mForce && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
            throw new SecurityException("Factory reset is not allowed for this user.");
            throw new SecurityException("Factory reset is not allowed for this user.");
        }
        }


        if (wipeFactoryResetProtection) {
        if (mWipeFactoryResetProtection) {
            PersistentDataBlockManager manager = (PersistentDataBlockManager)
            PersistentDataBlockManager manager = mContext
                    context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
                    .getSystemService(PersistentDataBlockManager.class);
            if (manager != null) {
            if (manager != null) {
                Log.w(TAG, "Wiping factory reset protection");
                Log.w(TAG, "Wiping factory reset protection");
                manager.wipe();
                manager.wipe();
@@ -66,17 +73,104 @@ final class FactoryResetter {
            }
            }
        }
        }


        if (wipeAdoptableStorage) {
        if (mWipeAdoptableStorage) {
            Log.w(TAG, "Wiping adoptable storage");
            Log.w(TAG, "Wiping adoptable storage");
            StorageManager sm = (StorageManager) context.getSystemService(
            StorageManager sm = mContext.getSystemService(StorageManager.class);
                    Context.STORAGE_SERVICE);
            sm.wipeAdoptableDisks();
            sm.wipeAdoptableDisks();
        }
        }


        RecoverySystem.rebootWipeUserData(context, shutdown, reason, force, wipeEuicc);
        RecoverySystem.rebootWipeUserData(mContext, mShutdown, mReason, mForce, mWipeEuicc);
    }

    private FactoryResetter(Builder builder) {
        mContext = builder.mContext;
        mShutdown = builder.mShutdown;
        mReason = builder.mReason;
        mForce = builder.mForce;
        mWipeEuicc = builder.mWipeEuicc;
        mWipeAdoptableStorage = builder.mWipeAdoptableStorage;
        mWipeFactoryResetProtection = builder.mWipeFactoryResetProtection;
    }

    /**
     * Creates a new builder.
     */
    public static Builder newBuilder(Context context) {
        return new Builder(context);
    }

    /**
     * Builder for {@link FactoryResetter} instances.
     */
    public static final class Builder {

        private final Context mContext;
        private @Nullable String mReason;
        private boolean mShutdown;
        private boolean mForce;
        private boolean mWipeEuicc;
        private boolean mWipeAdoptableStorage;
        private boolean mWipeFactoryResetProtection;

        private Builder(Context context) {
            mContext = Objects.requireNonNull(context);
        }

        /**
         * Sets the (non-null) reason for the factory reset that is visible in the logs
         */
        public Builder setReason(String reason) {
            mReason = Objects.requireNonNull(reason);
            return this;
        }

        /**
         * Sets whether the device will be powered down after the wipe completes, rather than being
         * rebooted back to the regular system.
         */
        public Builder setShutdown(boolean value) {
            mShutdown = value;
            return this;
        }

        /**
         * Sets whether the {@link UserManager.DISALLOW_FACTORY_RESET} user restriction should be
         * ignored.
         */
        public Builder setForce(boolean value) {
            mForce = value;
            return this;
        }
        }


    private FactoryResetter() {
        /**
        throw new UnsupportedOperationException("Contains only static methods");
         * Sets whether to wipe the {@code euicc} data.
         */
        public Builder setWipeEuicc(boolean value) {
            mWipeEuicc = value;
            return this;
        }

        /**
         * Sets whether to wipe the adoptable external storage (if any).
         */
        public Builder setWipeAdoptableStorage(boolean value) {
            mWipeAdoptableStorage = value;
            return this;
        }

        /**
         * Sets whether to reset the factory reset protection.
         */
        public Builder setWipeFactoryResetProtection(boolean value) {
            mWipeFactoryResetProtection = value;
            return this;
        }

        /**
         * Builds the {@link FactoryResetter} instance.
         */
        public FactoryResetter build() {
            return new FactoryResetter(this);
        }
    }
    }
}
}
+82 −65
Original line number Original line Diff line number Diff line
@@ -50,18 +50,7 @@ public final class FactoryResetterTest {


    private static final String TAG = FactoryResetterTest.class.getSimpleName();
    private static final String TAG = FactoryResetterTest.class.getSimpleName();


    // Fixed parameters
    private static final String REASON = "self-destruct";
    private static final String REASON = "self-destruct";
    private static final boolean SHUTDOWN = true;
    private static final boolean WIPE_EUICC = true;

    // Parameters under test
    private static final boolean FORCE = true;
    private static final boolean NO_FORCE = false;
    private static final boolean WIPE_ADOPTABLE_STORAGE = true;
    private static final boolean NO_WIPE_ADOPTABLE_STORAGE = false;
    private static final boolean WIPE_FACTORY_RESET_PROTECTION = true;
    private static final boolean NO_WIPE_FACTORY_RESET_PROTECTION = false;


    private MockitoSession mSession;
    private MockitoSession mSession;


@@ -78,19 +67,13 @@ public final class FactoryResetterTest {
                .strictness(Strictness.LENIENT)
                .strictness(Strictness.LENIENT)
                .startMocking();
                .startMocking();


        when(mContext.getSystemService(any(String.class))).thenAnswer((inv) -> {
        when(mContext.getSystemService(any(Class.class))).thenAnswer((inv) -> {
            Log.d(TAG, "Mocking " + inv);
            Log.d(TAG, "Mocking " + inv);
            String service = (String) inv.getArguments()[0];
            Class serviceClass = (Class) inv.getArguments()[0];
            switch (service) {
            if (serviceClass.equals(PersistentDataBlockManager.class)) return mPdbm;
                case Context.PERSISTENT_DATA_BLOCK_SERVICE:
            if (serviceClass.equals(StorageManager.class)) return mSm;
                    return mPdbm;
            if (serviceClass.equals(UserManager.class)) return mUm;
                case Context.STORAGE_SERVICE:
            throw new IllegalArgumentException("Not expecting call for " + serviceClass);
                    return mSm;
                case Context.USER_SERVICE:
                    return mUm;
                default:
                    throw new IllegalArgumentException("Not expecting call for " + service);
            }
        });
        });


        doAnswer((inv) -> {
        doAnswer((inv) -> {
@@ -110,13 +93,23 @@ public final class FactoryResetterTest {
    }
    }


    @Test
    @Test
    public void testFactoryReset_noMasterClearPermission() throws Exception {
    public void testFactoryResetBuilder_nullContext() throws Exception {
        assertThrows(NullPointerException.class, () -> FactoryResetter.newBuilder(null));
    }

    @Test
    public void testFactoryResetBuilder_nullReason() throws Exception {
        assertThrows(NullPointerException.class,
                () -> FactoryResetter.newBuilder(mContext).setReason(null));
    }

    @Test
    public void testFactoryReset_minimumArgs_noMasterClearPermission() throws Exception {
        revokeMasterClearPermission();
        revokeMasterClearPermission();
        setFactoryResetRestriction(/* allowed= */ true);
        allowFactoryReset();


        assertThrows(SecurityException.class,
        assertThrows(SecurityException.class,
                () -> FactoryResetter.factoryReset(mContext, SHUTDOWN, REASON, NO_FORCE,
                () -> FactoryResetter.newBuilder(mContext).build().factoryReset());
                        WIPE_EUICC, WIPE_ADOPTABLE_STORAGE, WIPE_FACTORY_RESET_PROTECTION));


        verifyWipeAdoptableStorageNotCalled();
        verifyWipeAdoptableStorageNotCalled();
        verifyWipeFactoryResetProtectionNotCalled();
        verifyWipeFactoryResetProtectionNotCalled();
@@ -124,13 +117,11 @@ public final class FactoryResetterTest {
    }
    }


    @Test
    @Test
    public void testFactoryReset_noForceDisallowed()
    public void testFactoryReset_minimumArgs_withRestriction_notForced() throws Exception {
            throws Exception {
        disallowFactoryReset();
        setFactoryResetRestriction(/* allowed= */ false);


        assertThrows(SecurityException.class,
        assertThrows(SecurityException.class,
                () -> FactoryResetter.factoryReset(mContext, SHUTDOWN, REASON, NO_FORCE,
                () -> FactoryResetter.newBuilder(mContext).build().factoryReset());
                        WIPE_EUICC, WIPE_ADOPTABLE_STORAGE, WIPE_FACTORY_RESET_PROTECTION));


        verifyWipeAdoptableStorageNotCalled();
        verifyWipeAdoptableStorageNotCalled();
        verifyWipeFactoryResetProtectionNotCalled();
        verifyWipeFactoryResetProtectionNotCalled();
@@ -138,57 +129,69 @@ public final class FactoryResetterTest {
    }
    }


    @Test
    @Test
    public void testFactoryReset_noForceAllowed() throws Exception {
    public void testFactoryReset_minimumArgs_noRestriction_notForced() throws Exception {
        setFactoryResetRestriction(/* allowed= */ true);
        allowFactoryReset();


        FactoryResetter.factoryReset(mContext, SHUTDOWN, REASON, NO_FORCE,
        FactoryResetter.newBuilder(mContext).build().factoryReset();
                WIPE_EUICC, WIPE_ADOPTABLE_STORAGE, WIPE_FACTORY_RESET_PROTECTION);


        verifyWipeAdoptableStorageCalled();
        verifyWipeAdoptableStorageNotCalled();
        verifyWipeFactoryResetProtectionCalled();
        verifyWipeFactoryResetProtectionNotCalled();
        verifyRebootWipeUserDataCalled(NO_FORCE);
        verifyRebootWipeUserDataMinimumArgsCalled();
    }
    }


    @Test
    @Test
    public void testFactoryReset_forceDisallowed() throws Exception {
    public void testFactoryReset_minimumArgs_withRestriction_forced() throws Exception {
        setFactoryResetRestriction(/* allowed= */ false);
        disallowFactoryReset();

        FactoryResetter.factoryReset(mContext, SHUTDOWN, REASON, FORCE,
                WIPE_EUICC, WIPE_ADOPTABLE_STORAGE, WIPE_FACTORY_RESET_PROTECTION);

        verifyWipeAdoptableStorageCalled();
        verifyWipeFactoryResetProtectionCalled();
        verifyRebootWipeUserDataCalled(FORCE);
    }


    @Test
        FactoryResetter.newBuilder(mContext).setForce(true).build().factoryReset();
    public void testFactoryReset_bothFalse() throws Exception {
        FactoryResetter.factoryReset(mContext, SHUTDOWN, REASON, FORCE,
                WIPE_EUICC, NO_WIPE_ADOPTABLE_STORAGE, NO_WIPE_FACTORY_RESET_PROTECTION);


        verifyWipeAdoptableStorageNotCalled();
        verifyWipeAdoptableStorageNotCalled();
        verifyWipeFactoryResetProtectionNotCalled();
        verifyWipeFactoryResetProtectionNotCalled();
        verifyRebootWipeUserDataCalled(FORCE);
        verifyRebootWipeUserDataMinimumArgsButForceCalled();
    }
    }


    @Test
    @Test
    public void testFactoryReset_storageOnly() throws Exception {
    public void testFactoryReset_storageOnly() throws Exception {
        FactoryResetter.factoryReset(mContext, SHUTDOWN, REASON, FORCE,
        allowFactoryReset();
                WIPE_EUICC, WIPE_ADOPTABLE_STORAGE, NO_WIPE_FACTORY_RESET_PROTECTION);

        FactoryResetter.newBuilder(mContext)
                .setWipeAdoptableStorage(true).build()
                .factoryReset();


        verifyWipeAdoptableStorageCalled();
        verifyWipeAdoptableStorageCalled();
        verifyWipeFactoryResetProtectionNotCalled();
        verifyWipeFactoryResetProtectionNotCalled();
        verifyRebootWipeUserDataCalled(FORCE);
        verifyRebootWipeUserDataMinimumArgsCalled();
    }
    }


    @Test
    @Test
    public void testFactoryReset_frpOnly() throws Exception {
    public void testFactoryReset_frpOnly() throws Exception {
        FactoryResetter.factoryReset(mContext, SHUTDOWN, REASON, FORCE,
        allowFactoryReset();
                WIPE_EUICC, NO_WIPE_ADOPTABLE_STORAGE, WIPE_FACTORY_RESET_PROTECTION);

        FactoryResetter.newBuilder(mContext)
                .setWipeFactoryResetProtection(true)
                .build().factoryReset();


        verifyWipeAdoptableStorageNotCalled();
        verifyWipeAdoptableStorageNotCalled();
        verifyWipeFactoryResetProtectionCalled();
        verifyWipeFactoryResetProtectionCalled();
        verifyRebootWipeUserDataCalled(FORCE);
        verifyRebootWipeUserDataMinimumArgsCalled();
    }

    @Test
    public void testFactoryReset_allArgs() throws Exception {
        allowFactoryReset();

        FactoryResetter.newBuilder(mContext)
                .setReason(REASON)
                .setForce(true)
                .setShutdown(true)
                .setWipeEuicc(true)
                .setWipeAdoptableStorage(true)
                .setWipeFactoryResetProtection(true)
                .build().factoryReset();

        verifyWipeAdoptableStorageCalled();
        verifyWipeFactoryResetProtectionCalled();
        verifyRebootWipeUserDataAllArgsCalled();
    }
    }


    private void revokeMasterClearPermission() {
    private void revokeMasterClearPermission() {
@@ -196,8 +199,12 @@ public final class FactoryResetterTest {
                .thenReturn(PackageManager.PERMISSION_DENIED);
                .thenReturn(PackageManager.PERMISSION_DENIED);
    }
    }


    private void setFactoryResetRestriction(boolean allowed) {
    private void allowFactoryReset() {
        when(mUm.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)).thenReturn(!allowed);
        when(mUm.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)).thenReturn(false);
    }

    private void disallowFactoryReset() {
        when(mUm.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)).thenReturn(true);
    }
    }


    private void verifyRebootWipeUserDataNotCalled() {
    private void verifyRebootWipeUserDataNotCalled() {
@@ -205,9 +212,19 @@ public final class FactoryResetterTest {
                anyBoolean()), never());
                anyBoolean()), never());
    }
    }


    private void verifyRebootWipeUserDataCalled(boolean force) {
    private void verifyRebootWipeUserDataMinimumArgsCalled() {
        verify(() -> RecoverySystem.rebootWipeUserData(mContext, SHUTDOWN, REASON, force,
        verify(() -> RecoverySystem.rebootWipeUserData(mContext, /* shutdown= */ false,
                WIPE_EUICC));
                /* reason= */ null, /* force= */ false, /* wipeEuicc= */ false));
    }

    private void verifyRebootWipeUserDataMinimumArgsButForceCalled() {
        verify(() -> RecoverySystem.rebootWipeUserData(mContext, /* shutdown= */ false,
                /* reason= */ null, /* force= */ true, /* wipeEuicc= */ false));
    }

    private void verifyRebootWipeUserDataAllArgsCalled() {
        verify(() -> RecoverySystem.rebootWipeUserData(mContext, /* shutdown= */ true,
                /* reason= */ REASON, /* force= */ true, /* wipeEuicc= */ true));
    }
    }


    private void verifyWipeAdoptableStorageNotCalled() {
    private void verifyWipeAdoptableStorageNotCalled() {