Merge "Initial definition of DevicePolicySafetyChecker."

This commit is contained in:
TreeHugger Robot
2020-11-12 01:49:29 +00:00
committed by Android (Google) Code Review
6 changed files with 212 additions and 5 deletions

View File

@@ -122,6 +122,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
// TODO(b/172376923) - add CarDevicePolicyManager examples below (or remove reference to it).
/**
* Public interface for managing policies enforced on a device. Most clients of this class must be
* registered with the system as a <a href="{@docRoot}guide/topics/admin/device-admin.html">device
@@ -130,6 +131,13 @@ import java.util.concurrent.Executor;
* for that method specifies that it is restricted to either device or profile owners. Any
* application calling an api may only pass as an argument a device administrator component it
* owns. Otherwise, a {@link SecurityException} will be thrown.
*
* <p><b>Note: </b>on
* {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE automotive builds}, some methods can
* throw an {@link UnsafeStateException} exception (for example, if the vehicle is moving), so
* callers running on automotive builds should wrap every method call under the methods provided by
* {@code android.car.admin.CarDevicePolicyManager}.
*
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>
@@ -2445,6 +2453,19 @@ public class DevicePolicyManager {
@Retention(RetentionPolicy.SOURCE)
public @interface PersonalAppsSuspensionReason {}
/** @hide */
// TODO(b/172376923): make it TestApi
public static final int OPERATION_LOCK_NOW = 1;
// TODO(b/172376923) - add all operations
/** @hide */
@IntDef(prefix = "OPERATION_", value = {
OPERATION_LOCK_NOW,
})
@Retention(RetentionPolicy.SOURCE)
public static @interface DevicePolicyOperation {
}
/**
* Return true if the given administrator component is currently active (enabled) in the system.
*

View File

@@ -0,0 +1,42 @@
/*
* 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 android.app.admin;
import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
/**
* Interface responsible to check if a {@link DevicePolicyManager} API can be safely executed.
*
* @hide
*/
public interface DevicePolicySafetyChecker {
/**
* Returns whether the given {@code operation} can be safely executed at the moment.
*/
default boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation) {
return true;
}
/**
* Returns a new exception for when the given {@code operation} cannot be safely executed.
*/
@NonNull
default UnsafeStateException newUnsafeStateException(@DevicePolicyOperation int operation) {
return new UnsafeStateException(operation);
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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 android.app.admin;
import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Exception thrown when a {@link DevicePolicyManager} operation failed because it was not safe
* to be executed at that moment.
*
* <p>For example, it can be thrown on
* {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE automotive devices} when the vehicle
* is moving.
*
* @hide
*/
// TODO(b/172376923): make it public
@SuppressWarnings("serial")
public final class UnsafeStateException extends IllegalStateException implements Parcelable {
private final @DevicePolicyOperation int mOperation;
/** @hide */
public UnsafeStateException(@DevicePolicyOperation int operation) {
super();
mOperation = operation;
}
/** @hide */
// TODO(b/172376923): make it TestApi
public @DevicePolicyOperation int getOperation() {
return mOperation;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mOperation);
}
@NonNull
public static final Creator<UnsafeStateException> CREATOR =
new Creator<UnsafeStateException>() {
@Override
public UnsafeStateException createFromParcel(Parcel source) {
return new UnsafeStateException(source.readInt());
}
@Override
public UnsafeStateException[] newArray(int size) {
return new UnsafeStateException[size];
}
};
}

View File

@@ -15,8 +15,10 @@
*/
package com.android.server.devicepolicy;
import android.app.admin.DevicePolicySafetyChecker;
import android.app.admin.IDevicePolicyManager;
import android.content.ComponentName;
import android.util.Slog;
import com.android.server.SystemService;
@@ -30,6 +32,9 @@ import com.android.server.SystemService;
* should be added here to avoid build breakage in downstream branches.
*/
abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
private static final String TAG = BaseIDevicePolicyManager.class.getSimpleName();
/**
* To be called by {@link DevicePolicyManagerService#Lifecycle} during the various boot phases.
*
@@ -55,6 +60,16 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
*/
abstract void handleStopUser(int userId);
/**
* Sets the {@link DevicePolicySafetyChecker}.
*
* <p>Currently, it's called only by {@code SystemServer} on
* {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE automotive builds}
*/
public void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
Slog.w(TAG, "setDevicePolicySafetyChecker() not implemented by " + getClass());
}
public void clearSystemUpdatePolicyFreezePeriodRecord() {
}

View File

@@ -135,9 +135,11 @@ import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyCache;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicySafetyChecker;
import android.app.admin.DeviceStateCache;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.NetworkEvent;
@@ -148,6 +150,7 @@ import android.app.admin.SecurityLog.SecurityEvent;
import android.app.admin.StartInstallingUpdateCallback;
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
import android.app.admin.UnsafeStateException;
import android.app.backup.IBackupManager;
import android.app.trust.TrustManager;
import android.app.usage.UsageStatsManagerInternal;
@@ -634,6 +637,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@VisibleForTesting
final TransferOwnershipMetadataManager mTransferOwnershipMetadataManager;
@Nullable
private DevicePolicySafetyChecker mSafetyChecker;
public static final class Lifecycle extends SystemService {
private BaseIDevicePolicyManager mService;
@@ -645,8 +651,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
dpmsClassName = DevicePolicyManagerService.class.getName();
}
try {
Class serviceClass = Class.forName(dpmsClassName);
Constructor constructor = serviceClass.getConstructor(Context.class);
Class<?> serviceClass = Class.forName(dpmsClassName);
Constructor<?> constructor = serviceClass.getConstructor(Context.class);
mService = (BaseIDevicePolicyManager) constructor.newInstance(context);
} catch (Exception e) {
throw new IllegalStateException(
@@ -655,6 +661,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
/** Sets the {@link DevicePolicySafetyChecker}. */
public void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
mService.setDevicePolicySafetyChecker(safetyChecker);
}
@Override
public void onStart() {
publishBinderService(Context.DEVICE_POLICY_SERVICE, mService);
@@ -953,6 +964,38 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
@Override
public void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as " + safetyChecker.getClass());
mSafetyChecker = safetyChecker;
}
/**
* Checks if the feature is supported and it's safe to execute the given {@code operation}.
*
* <p>Typically called at the beginning of each API method as:
*
* <pre><code>
*
* if (!canExecute(operation, permission)) return;
*
* </code></pre>
*
* @return {@code true} when it's safe to execute, {@code false} when the feature is not
* supported or the caller does not have the given {@code requiredPermission}.
*
* @throws UnsafeStateException if it's not safe to execute the operation.
*/
boolean canExecute(@DevicePolicyOperation int operation, @NonNull String requiredPermission) {
if (!mHasFeature && !hasCallingPermission(requiredPermission)) {
return false;
}
if (mSafetyChecker == null || mSafetyChecker.isDevicePolicyOperationSafe(operation)) {
return true;
}
throw mSafetyChecker.newUnsafeStateException(operation);
}
/**
* Unit test will subclass it to inject mocks.
*/
@@ -4760,9 +4803,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void lockNow(int flags, boolean parent) {
if (!mHasFeature && !hasCallingPermission(permission.LOCK_DEVICE)) {
if (!canExecute(DevicePolicyManager.OPERATION_LOCK_NOW, permission.LOCK_DEVICE)) {
return;
}
final CallerIdentity caller = getCallerIdentity();
final int callingUserId = caller.getUserId();
@@ -8554,6 +8598,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
pw.printf("mIsWatch=%b\n", mIsWatch);
pw.printf("mIsAutomotive=%b\n", mIsAutomotive);
pw.printf("mHasTelephonyFeature=%b\n", mHasTelephonyFeature);
String safetyChecker = mSafetyChecker == null ? "N/A" : mSafetyChecker.getClass().getName();
pw.printf("mSafetyChecker=%b\n", safetyChecker);
pw.decreaseIndent();
}

View File

@@ -34,6 +34,7 @@ import android.app.AppCompatCallbacks;
import android.app.ApplicationErrorReport;
import android.app.INotificationManager;
import android.app.SystemServiceRegistry;
import android.app.admin.DevicePolicySafetyChecker;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ContentResolver;
import android.content.Context;
@@ -1468,7 +1469,10 @@ public final class SystemServer implements Dumpable {
}
t.traceEnd();
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
final DevicePolicyManagerService.Lifecycle dpms;
if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
dpms = null;
} else {
t.traceBegin("StartLockSettingsService");
try {
mSystemServiceManager.startService(LOCK_SETTINGS_SERVICE_CLASS);
@@ -1505,7 +1509,7 @@ public final class SystemServer implements Dumpable {
// Always start the Device Policy Manager, so that the API is compatible with
// API8.
t.traceBegin("StartDevicePolicyManager");
mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
dpms = mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
t.traceEnd();
if (!isWatch) {
@@ -2427,6 +2431,9 @@ public final class SystemServer implements Dumpable {
if (cshs instanceof Dumpable) {
mDumper.addDumpable((Dumpable) cshs);
}
if (cshs instanceof DevicePolicySafetyChecker) {
dpms.setDevicePolicySafetyChecker((DevicePolicySafetyChecker) cshs);
}
t.traceEnd();
}