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

Commit f8dcca3c authored by Felipe Leme's avatar Felipe Leme
Browse files

Introducing FactoryResetter, a single entry point for factory reset calls.

The new class has options to wipe external storage and factory reset
proctection, so it can be a single entry point from both
DPM.wipeData() and the ManagedProvisioning app, which in turn will make
it easier to postpone these operations on automotive when the
vehicle is moving.

Test: atest FrameworksServicesTests:DevicePolicyManagerTest \
            FrameworksMockingServicesTests:FactoryResetterTest

Test: adb shell dumpsys activity service --user 0 com.afwsamples.testdpc wipe-data 7

Bug: 171603586
Bug: 175392542
Fixes: 172697975

Change-Id: I37d5f8c59645459e48620047261ffd06d90ac2be
parent a3c423c3
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -12675,4 +12675,21 @@ public class DevicePolicyManager {
            }
        }
    }
    // TODO(b/175392542): remove if not needed by ManagedProvisioning app anymore
    /**
     * Used by ManagedProvisioning app to factory reset the device when DO cannto be provisioned.
     *
     * @hide
     */
    @RequiresPermission(android.Manifest.permission.MASTER_CLEAR)
    public void factoryReset(String reason) {
        if (mService != null) {
            try {
                mService.factoryReset(reason);
            } catch (RemoteException re) {
                throw re.rethrowFromSystemServer();
            }
        }
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -488,4 +488,6 @@ interface IDevicePolicyManager {
    boolean canProfileOwnerResetPasswordWhenLocked(int userId);

    void setNextOperationSafety(int operation, boolean safe);
    // TODO(b/175392542): remove if not needed by ManagedProvisioning app anymore
    void factoryReset(String reason);
}
+31 −25
Original line number Diff line number Diff line
@@ -215,7 +215,6 @@ import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RecoverySystem;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -245,7 +244,6 @@ import android.security.keymaster.KeymasterCertificateChain;
import android.security.keystore.AttestationUtils;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.ParcelableKeyGenParameterSpec;
import android.service.persistentdata.PersistentDataBlockManager;
import android.stats.devicepolicy.DevicePolicyEnums;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
@@ -1026,6 +1024,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        mSafetyChecker = new OneTimeSafetyChecker(this, operation, safe);
    }
    // TODO(b/175392542): remove if not needed by ManagedProvisioning app anymore
    @Override
    public void factoryReset(String reason) {
        Preconditions.checkCallAuthorization(
                hasCallingOrSelfPermission(permission.MASTER_CLEAR));
        Slog.w(LOG_TAG, "factoryReset(): " + reason);
        final long identity = Binder.clearCallingIdentity();
        try {
            FactoryResetter.factoryReset(mContext, /* shutdown= */ false, reason,
                    /* force= */ false, /* wipeEuicc= */ false, /* wipeAdoptableStorage= */ false,
                    /* wipeFactoryResetProtection= */ false);
        } catch (IOException e) {
            // Shouldn't happen.
            Slog.wtf(LOG_TAG, "Could not factory reset", e);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }
    /**
     * Unit test will subclass it to inject mocks.
     */
@@ -1276,8 +1293,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        }
        void recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force,
                boolean wipeEuicc) throws IOException {
            RecoverySystem.rebootWipeUserData(mContext, shutdown, reason, force, wipeEuicc);
                boolean wipeEuicc, boolean wipeExtRequested, boolean wipeResetProtectionData)
                        throws IOException {
            FactoryResetter.factoryReset(mContext, shutdown, reason, force, wipeEuicc,
                    wipeExtRequested, wipeResetProtectionData);
        }
        boolean systemPropertiesGetBoolean(String key, boolean def) {
@@ -6073,17 +6092,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                        caller.getUserId()));
    }
    private void forceWipeDeviceNoLock(boolean wipeExtRequested, String reason, boolean wipeEuicc) {
    private void forceWipeDeviceNoLock(boolean wipeExtRequested, String reason, boolean wipeEuicc,
            boolean wipeResetProtectionData) {
        wtfIfInLock();
        boolean success = false;
        try {
            if (wipeExtRequested) {
                StorageManager sm = (StorageManager) mContext.getSystemService(
                    Context.STORAGE_SERVICE);
                sm.wipeAdoptableDisks();
            }
            mInjector.recoverySystemRebootWipeUserData(
                /*shutdown=*/ false, reason, /*force=*/ true, /*wipeEuicc=*/ wipeEuicc);
                    /* shutdown= */ false, reason, /* force= */ true, /* wipeEuicc= */ wipeEuicc,
                    wipeExtRequested, wipeResetProtectionData);
            success = true;
        } catch (IOException | SecurityException e) {
            Slog.w(LOG_TAG, "Failed requesting data wipe", e);
@@ -6230,22 +6246,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                        + " restriction is set for user " + userId);
            }
            if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
                PersistentDataBlockManager manager = (PersistentDataBlockManager)
                        mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
                if (manager != null) {
                    manager.wipe();
                }
            }
            // TODO If split user is enabled and the device owner is set in the primary user
            // (rather than system), we should probably trigger factory reset. Current code just
            // removes that user (but still clears FRP...)
            if (userId == UserHandle.USER_SYSTEM) {
                forceWipeDeviceNoLock(/*wipeExtRequested=*/ (
                        flags & WIPE_EXTERNAL_STORAGE) != 0,
                forceWipeDeviceNoLock(
                        (flags & WIPE_EXTERNAL_STORAGE) != 0,
                        internalReason,
                        /*wipeEuicc=*/ (flags & WIPE_EUICC) != 0);
                        (flags & WIPE_EUICC) != 0,
                        (flags & WIPE_RESET_PROTECTION_DATA) != 0);
            } else {
                forceWipeUser(userId, wipeReasonForUser, (flags & WIPE_SILENTLY) != 0);
            }
+82 −0
Original line number Diff line number Diff line
/*
 * 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.PackageManager;
import android.os.RecoverySystem;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.service.persistentdata.PersistentDataBlockManager;
import android.util.Log;

import com.android.internal.util.Preconditions;

import java.io.IOException;

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

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

    // TODO(b/171603586): use an object that's constructed with a Builder instead (then update
    // javadoc)
    /**
     * Factory reset the device.
     */
    public static void factoryReset(Context context, boolean shutdown, String reason,
            boolean force, boolean wipeEuicc, boolean wipeAdoptableStorage,
            boolean wipeFactoryResetProtection) throws IOException {
        Log.i(TAG, "factoryReset(): shutdown=" + shutdown + ", force=" + force
                + ", wipeEuicc=" + wipeEuicc + ", wipeAdoptableStorage=" + wipeAdoptableStorage
                + ", wipeFRP=" + wipeFactoryResetProtection);

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

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

        if (wipeFactoryResetProtection) {
            PersistentDataBlockManager manager = (PersistentDataBlockManager)
                    context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
            if (manager != null) {
                Log.w(TAG, "Wiping factory reset protection");
                manager.wipe();
            } else {
                Log.w(TAG, "No need to wipe factory reset protection");
            }
        }

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

        RecoverySystem.rebootWipeUserData(context, shutdown, reason, force, wipeEuicc);
    }

    private FactoryResetter() {
        throw new UnsupportedOperationException("Contains only static methods");
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ android_test {

    static_libs: [
        "services.core",
        "services.devicepolicy",
        "services.net",
        "services.usage",
        "service-jobscheduler",
Loading