blob: 964be38943bfbad8ddedc6c992247c266efe50ee [file] [log] [blame]
/*
* 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.annotation.Nullable;
import android.app.admin.DevicePolicySafetyChecker;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.RecoverySystem;
import android.os.RemoteException;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.service.persistentdata.PersistentDataBlockManager;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.Preconditions;
import com.android.server.utils.Slogf;
import java.io.IOException;
import java.util.Objects;
/**
* Entry point for "factory reset" requests.
*/
public final class FactoryResetter {
private static final String TAG = FactoryResetter.class.getSimpleName();
private final Context mContext;
private final @Nullable DevicePolicySafetyChecker mSafetyChecker;
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 according to the builder's arguments.
*
* @return {@code true} if device was factory reset, or {@code false} if it was delayed by the
* {@link DevicePolicySafetyChecker}.
*/
public boolean factoryReset() throws IOException {
Preconditions.checkCallAuthorization(mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MASTER_CLEAR) == PackageManager.PERMISSION_GRANTED);
if (mSafetyChecker == null) {
factoryResetInternalUnchecked();
return true;
}
IResultReceiver receiver = new IResultReceiver.Stub() {
@Override
public void send(int resultCode, Bundle resultData) throws RemoteException {
Slogf.i(TAG, "Factory reset confirmed by %s, proceeding", mSafetyChecker);
try {
factoryResetInternalUnchecked();
} catch (IOException e) {
// Shouldn't happen
Slogf.wtf(TAG, e, "IOException calling underlying systems");
}
}
};
Slogf.i(TAG, "Delaying factory reset until %s confirms", mSafetyChecker);
mSafetyChecker.onFactoryReset(receiver);
return false;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("FactoryResetter[");
if (mReason == null) {
builder.append("no_reason");
} else {
builder.append("reason='").append(mReason).append("'");
}
if (mSafetyChecker != null) {
builder.append(",hasSafetyChecker");
}
if (mShutdown) {
builder.append(",shutdown");
}
if (mForce) {
builder.append(",force");
}
if (mWipeEuicc) {
builder.append(",wipeEuicc");
}
if (mWipeAdoptableStorage) {
builder.append(",wipeAdoptableStorage");
}
if (mWipeFactoryResetProtection) {
builder.append(",ipeFactoryResetProtection");
}
return builder.append(']').toString();
}
private void factoryResetInternalUnchecked() throws IOException {
Slogf.i(TAG, "factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, "
+ "wipeAdoptableStorage=%b, wipeFRP=%b", mReason, mShutdown, mForce, mWipeEuicc,
mWipeAdoptableStorage, mWipeFactoryResetProtection);
UserManager um = mContext.getSystemService(UserManager.class);
if (!mForce && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
throw new SecurityException("Factory reset is not allowed for this user.");
}
if (mWipeFactoryResetProtection) {
PersistentDataBlockManager manager = mContext
.getSystemService(PersistentDataBlockManager.class);
if (manager != null) {
Slogf.w(TAG, "Wiping factory reset protection");
manager.wipe();
} else {
Slogf.w(TAG, "No need to wipe factory reset protection");
}
}
if (mWipeAdoptableStorage) {
Slogf.w(TAG, "Wiping adoptable storage");
StorageManager sm = mContext.getSystemService(StorageManager.class);
sm.wipeAdoptableDisks();
}
RecoverySystem.rebootWipeUserData(mContext, mShutdown, mReason, mForce, mWipeEuicc);
}
private FactoryResetter(Builder builder) {
mContext = builder.mContext;
mSafetyChecker = builder.mSafetyChecker;
mReason = builder.mReason;
mShutdown = builder.mShutdown;
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 DevicePolicySafetyChecker mSafetyChecker;
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 a {@link DevicePolicySafetyChecker} object that will be used to delay the
* factory reset when it's not safe to do so.
*/
public Builder setSafetyChecker(@Nullable DevicePolicySafetyChecker safetyChecker) {
mSafetyChecker = safetyChecker;
return this;
}
/**
* 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;
}
/**
* 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);
}
}
}