Loading core/java/android/app/admin/DevicePolicySafetyChecker.java +12 −3 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package android.app.admin; import android.annotation.NonNull; import android.app.admin.DevicePolicyManager.DevicePolicyOperation; import com.android.internal.os.IResultReceiver; /** * Interface responsible to check if a {@link DevicePolicyManager} API can be safely executed. * Loading @@ -28,9 +30,7 @@ public interface DevicePolicySafetyChecker { /** * Returns whether the given {@code operation} can be safely executed at the moment. */ default boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation) { return true; } boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation); /** * Returns a new exception for when the given {@code operation} cannot be safely executed. Loading @@ -39,4 +39,13 @@ public interface DevicePolicySafetyChecker { default UnsafeStateException newUnsafeStateException(@DevicePolicyOperation int operation) { return new UnsafeStateException(operation); } /** * Called when a request was made to factory reset the device, so it can be delayed if it's not * safe to proceed. * * @param callback callback whose {@code send()} method must be called when it's safe to factory * reset. */ void onFactoryReset(IResultReceiver callback); } services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java +14 −2 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ import android.os.UserHandle; * All parameters are verified on object creation unless the component name is null and the * caller is a delegate. */ class CallerIdentity { final class CallerIdentity { private final int mUid; @Nullable Loading Loading @@ -66,4 +66,16 @@ class CallerIdentity { public boolean hasPackage() { return mPackageName != null; } @Override public String toString() { StringBuilder builder = new StringBuilder("CallerIdentity[uid=").append(mUid); if (mPackageName != null) { builder.append(", pkg=").append(mPackageName); } if (mComponentName != null) { builder.append(", cmp=").append(mComponentName.flattenToShortString()); } return builder.append("]").toString(); } } services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +23 −4 Original line number Diff line number Diff line Loading @@ -983,8 +983,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) { Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as " + safetyChecker); CallerIdentity callerIdentity = getCallerIdentity(); Preconditions.checkCallAuthorization(mIsAutomotive || isAdb(callerIdentity), "can only set " + "DevicePolicySafetyChecker on automotive builds or from ADB (but caller is %s)", callerIdentity); setDevicePolicySafetyCheckerUnchecked(safetyChecker); } /** * Used by {@code setDevicePolicySafetyChecker()} above and {@link OneTimeSafetyChecker}. */ void setDevicePolicySafetyCheckerUnchecked(DevicePolicySafetyChecker safetyChecker) { Slog.i(LOG_TAG, String.format("Setting DevicePolicySafetyChecker as %s", safetyChecker)); mSafetyChecker = safetyChecker; mInjector.setDevicePolicySafetyChecker(safetyChecker); } /** Loading Loading @@ -1021,8 +1033,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setNextOperationSafety(@DevicePolicyOperation int operation, boolean safe) { Preconditions.checkCallAuthorization( hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)); Slog.i(LOG_TAG, "setNextOperationSafety(" + DevicePolicyManager.operationToString(operation) + ", " + safe + ")"); Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %b)", DevicePolicyManager.operationToString(operation), safe)); mSafetyChecker = new OneTimeSafetyChecker(this, operation, safe); } Loading @@ -1034,6 +1046,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public final Context mContext; private @Nullable DevicePolicySafetyChecker mSafetyChecker; Injector(Context context) { mContext = context; } Loading Loading @@ -1278,7 +1292,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { void recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force, boolean wipeEuicc, boolean wipeExtRequested, boolean wipeResetProtectionData) throws IOException { FactoryResetter.newBuilder(mContext).setReason(reason).setShutdown(shutdown) FactoryResetter.newBuilder(mContext).setSafetyChecker(mSafetyChecker) .setReason(reason).setShutdown(shutdown) .setForce(force).setWipeEuicc(wipeEuicc) .setWipeAdoptableStorage(wipeExtRequested) .setWipeFactoryResetProtection(wipeResetProtectionData) Loading Loading @@ -1433,6 +1448,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public boolean isChangeEnabled(long changeId, String packageName, int userId) { return CompatChanges.isChangeEnabled(changeId, packageName, UserHandle.of(userId)); } void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) { mSafetyChecker = safetyChecker; } } /** Loading services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java +48 −10 Original line number Diff line number Diff line Loading @@ -17,14 +17,18 @@ 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 android.util.Log; import android.util.Slog; import com.android.internal.os.IResultReceiver; import com.android.internal.util.Preconditions; import java.io.IOException; Loading @@ -38,6 +42,7 @@ 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; Loading @@ -45,18 +50,40 @@ public final class FactoryResetter { private final boolean mWipeAdoptableStorage; private final boolean mWipeFactoryResetProtection; /** * Factory reset the device according to the builder's arguments. */ public void factoryReset() throws IOException { Log.i(TAG, String.format("factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b" + ", wipeAdoptableStorage=%b, wipeFRP=%b", mReason, mShutdown, mForce, mWipeEuicc, mWipeAdoptableStorage, mWipeFactoryResetProtection)); Preconditions.checkCallAuthorization(mContext.checkCallingOrSelfPermission( android.Manifest.permission.MASTER_CLEAR) == PackageManager.PERMISSION_GRANTED); if (mSafetyChecker == null) { factoryResetInternalUnchecked(); return; } IResultReceiver receiver = new IResultReceiver.Stub() { @Override public void send(int resultCode, Bundle resultData) throws RemoteException { Slog.i(TAG, String.format("Factory reset confirmed by %s, proceeding", mSafetyChecker)); try { factoryResetInternalUnchecked(); } catch (IOException e) { // Shouldn't happen Slog.wtf(TAG, "IOException calling underlying systems", e); } } }; Slog.i(TAG, String.format("Delaying factory reset until %s confirms", mSafetyChecker)); mSafetyChecker.onFactoryReset(receiver); } private void factoryResetInternalUnchecked() throws IOException { Slog.i(TAG, String.format("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."); Loading @@ -66,15 +93,15 @@ public final class FactoryResetter { PersistentDataBlockManager manager = mContext .getSystemService(PersistentDataBlockManager.class); if (manager != null) { Log.w(TAG, "Wiping factory reset protection"); Slog.w(TAG, "Wiping factory reset protection"); manager.wipe(); } else { Log.w(TAG, "No need to wipe factory reset protection"); Slog.w(TAG, "No need to wipe factory reset protection"); } } if (mWipeAdoptableStorage) { Log.w(TAG, "Wiping adoptable storage"); Slog.w(TAG, "Wiping adoptable storage"); StorageManager sm = mContext.getSystemService(StorageManager.class); sm.wipeAdoptableDisks(); } Loading @@ -84,8 +111,9 @@ public final class FactoryResetter { private FactoryResetter(Builder builder) { mContext = builder.mContext; mShutdown = builder.mShutdown; mSafetyChecker = builder.mSafetyChecker; mReason = builder.mReason; mShutdown = builder.mShutdown; mForce = builder.mForce; mWipeEuicc = builder.mWipeEuicc; mWipeAdoptableStorage = builder.mWipeAdoptableStorage; Loading @@ -105,6 +133,7 @@ public final class FactoryResetter { public static final class Builder { private final Context mContext; private @Nullable DevicePolicySafetyChecker mSafetyChecker; private @Nullable String mReason; private boolean mShutdown; private boolean mForce; Loading @@ -116,6 +145,15 @@ public final class FactoryResetter { 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 */ Loading services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java +8 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ import android.app.admin.DevicePolicyManager.DevicePolicyOperation; import android.app.admin.DevicePolicySafetyChecker; import android.util.Slog; import com.android.internal.os.IResultReceiver; import java.util.Objects; //TODO(b/172376923): add unit tests Loading Loading @@ -61,7 +63,12 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker { } Slog.i(TAG, "isDevicePolicyOperationSafe(" + name + "): returning " + safe + " and restoring DevicePolicySafetyChecker to " + mRealSafetyChecker); mService.setDevicePolicySafetyChecker(mRealSafetyChecker); mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker); return safe; } @Override public void onFactoryReset(IResultReceiver callback) { throw new UnsupportedOperationException(); } } Loading
core/java/android/app/admin/DevicePolicySafetyChecker.java +12 −3 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package android.app.admin; import android.annotation.NonNull; import android.app.admin.DevicePolicyManager.DevicePolicyOperation; import com.android.internal.os.IResultReceiver; /** * Interface responsible to check if a {@link DevicePolicyManager} API can be safely executed. * Loading @@ -28,9 +30,7 @@ public interface DevicePolicySafetyChecker { /** * Returns whether the given {@code operation} can be safely executed at the moment. */ default boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation) { return true; } boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation); /** * Returns a new exception for when the given {@code operation} cannot be safely executed. Loading @@ -39,4 +39,13 @@ public interface DevicePolicySafetyChecker { default UnsafeStateException newUnsafeStateException(@DevicePolicyOperation int operation) { return new UnsafeStateException(operation); } /** * Called when a request was made to factory reset the device, so it can be delayed if it's not * safe to proceed. * * @param callback callback whose {@code send()} method must be called when it's safe to factory * reset. */ void onFactoryReset(IResultReceiver callback); }
services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java +14 −2 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ import android.os.UserHandle; * All parameters are verified on object creation unless the component name is null and the * caller is a delegate. */ class CallerIdentity { final class CallerIdentity { private final int mUid; @Nullable Loading Loading @@ -66,4 +66,16 @@ class CallerIdentity { public boolean hasPackage() { return mPackageName != null; } @Override public String toString() { StringBuilder builder = new StringBuilder("CallerIdentity[uid=").append(mUid); if (mPackageName != null) { builder.append(", pkg=").append(mPackageName); } if (mComponentName != null) { builder.append(", cmp=").append(mComponentName.flattenToShortString()); } return builder.append("]").toString(); } }
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +23 −4 Original line number Diff line number Diff line Loading @@ -983,8 +983,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) { Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as " + safetyChecker); CallerIdentity callerIdentity = getCallerIdentity(); Preconditions.checkCallAuthorization(mIsAutomotive || isAdb(callerIdentity), "can only set " + "DevicePolicySafetyChecker on automotive builds or from ADB (but caller is %s)", callerIdentity); setDevicePolicySafetyCheckerUnchecked(safetyChecker); } /** * Used by {@code setDevicePolicySafetyChecker()} above and {@link OneTimeSafetyChecker}. */ void setDevicePolicySafetyCheckerUnchecked(DevicePolicySafetyChecker safetyChecker) { Slog.i(LOG_TAG, String.format("Setting DevicePolicySafetyChecker as %s", safetyChecker)); mSafetyChecker = safetyChecker; mInjector.setDevicePolicySafetyChecker(safetyChecker); } /** Loading Loading @@ -1021,8 +1033,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setNextOperationSafety(@DevicePolicyOperation int operation, boolean safe) { Preconditions.checkCallAuthorization( hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)); Slog.i(LOG_TAG, "setNextOperationSafety(" + DevicePolicyManager.operationToString(operation) + ", " + safe + ")"); Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %b)", DevicePolicyManager.operationToString(operation), safe)); mSafetyChecker = new OneTimeSafetyChecker(this, operation, safe); } Loading @@ -1034,6 +1046,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public final Context mContext; private @Nullable DevicePolicySafetyChecker mSafetyChecker; Injector(Context context) { mContext = context; } Loading Loading @@ -1278,7 +1292,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { void recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force, boolean wipeEuicc, boolean wipeExtRequested, boolean wipeResetProtectionData) throws IOException { FactoryResetter.newBuilder(mContext).setReason(reason).setShutdown(shutdown) FactoryResetter.newBuilder(mContext).setSafetyChecker(mSafetyChecker) .setReason(reason).setShutdown(shutdown) .setForce(force).setWipeEuicc(wipeEuicc) .setWipeAdoptableStorage(wipeExtRequested) .setWipeFactoryResetProtection(wipeResetProtectionData) Loading Loading @@ -1433,6 +1448,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public boolean isChangeEnabled(long changeId, String packageName, int userId) { return CompatChanges.isChangeEnabled(changeId, packageName, UserHandle.of(userId)); } void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) { mSafetyChecker = safetyChecker; } } /** Loading
services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java +48 −10 Original line number Diff line number Diff line Loading @@ -17,14 +17,18 @@ 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 android.util.Log; import android.util.Slog; import com.android.internal.os.IResultReceiver; import com.android.internal.util.Preconditions; import java.io.IOException; Loading @@ -38,6 +42,7 @@ 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; Loading @@ -45,18 +50,40 @@ public final class FactoryResetter { private final boolean mWipeAdoptableStorage; private final boolean mWipeFactoryResetProtection; /** * Factory reset the device according to the builder's arguments. */ public void factoryReset() throws IOException { Log.i(TAG, String.format("factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b" + ", wipeAdoptableStorage=%b, wipeFRP=%b", mReason, mShutdown, mForce, mWipeEuicc, mWipeAdoptableStorage, mWipeFactoryResetProtection)); Preconditions.checkCallAuthorization(mContext.checkCallingOrSelfPermission( android.Manifest.permission.MASTER_CLEAR) == PackageManager.PERMISSION_GRANTED); if (mSafetyChecker == null) { factoryResetInternalUnchecked(); return; } IResultReceiver receiver = new IResultReceiver.Stub() { @Override public void send(int resultCode, Bundle resultData) throws RemoteException { Slog.i(TAG, String.format("Factory reset confirmed by %s, proceeding", mSafetyChecker)); try { factoryResetInternalUnchecked(); } catch (IOException e) { // Shouldn't happen Slog.wtf(TAG, "IOException calling underlying systems", e); } } }; Slog.i(TAG, String.format("Delaying factory reset until %s confirms", mSafetyChecker)); mSafetyChecker.onFactoryReset(receiver); } private void factoryResetInternalUnchecked() throws IOException { Slog.i(TAG, String.format("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."); Loading @@ -66,15 +93,15 @@ public final class FactoryResetter { PersistentDataBlockManager manager = mContext .getSystemService(PersistentDataBlockManager.class); if (manager != null) { Log.w(TAG, "Wiping factory reset protection"); Slog.w(TAG, "Wiping factory reset protection"); manager.wipe(); } else { Log.w(TAG, "No need to wipe factory reset protection"); Slog.w(TAG, "No need to wipe factory reset protection"); } } if (mWipeAdoptableStorage) { Log.w(TAG, "Wiping adoptable storage"); Slog.w(TAG, "Wiping adoptable storage"); StorageManager sm = mContext.getSystemService(StorageManager.class); sm.wipeAdoptableDisks(); } Loading @@ -84,8 +111,9 @@ public final class FactoryResetter { private FactoryResetter(Builder builder) { mContext = builder.mContext; mShutdown = builder.mShutdown; mSafetyChecker = builder.mSafetyChecker; mReason = builder.mReason; mShutdown = builder.mShutdown; mForce = builder.mForce; mWipeEuicc = builder.mWipeEuicc; mWipeAdoptableStorage = builder.mWipeAdoptableStorage; Loading @@ -105,6 +133,7 @@ public final class FactoryResetter { public static final class Builder { private final Context mContext; private @Nullable DevicePolicySafetyChecker mSafetyChecker; private @Nullable String mReason; private boolean mShutdown; private boolean mForce; Loading @@ -116,6 +145,15 @@ public final class FactoryResetter { 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 */ Loading
services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java +8 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ import android.app.admin.DevicePolicyManager.DevicePolicyOperation; import android.app.admin.DevicePolicySafetyChecker; import android.util.Slog; import com.android.internal.os.IResultReceiver; import java.util.Objects; //TODO(b/172376923): add unit tests Loading Loading @@ -61,7 +63,12 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker { } Slog.i(TAG, "isDevicePolicyOperationSafe(" + name + "): returning " + safe + " and restoring DevicePolicySafetyChecker to " + mRealSafetyChecker); mService.setDevicePolicySafetyChecker(mRealSafetyChecker); mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker); return safe; } @Override public void onFactoryReset(IResultReceiver callback) { throw new UnsupportedOperationException(); } }