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

Commit 99a66a90 authored by Rubin Xu's avatar Rubin Xu
Browse files

Add three new delegation capabilities for profile/device owner

* DELEGATION_NETWORK_LOGGING
Allow delegated apps to control and retrieve network logging

* DELEGATION_CERT_SELECTION
Allow delegated apps to automatically select client certificates for apps.

* DELEGATION_PACKAGE_INSTALLATION
Allow delegated apps to silently install packages.

Also introduce DelegatedAdminReceiver which is analogue of the existing
DeviceAdminReceiver and enables delegated apps to receive system callbacks
related to their delegated capabilities.

This CL introduces the three new delegation scopes as well as some
implementations changes required to support these three delegations.
it also implements the actual logic around DELEGATION_NETWORK_LOGGING
and DELEGATION_CERT_SELECTION. Handling DELEGATION_PACKAGE_INSTALLATION
will be implmented in a subseqent CL.

Bug: 112982695
Test: atest com.android.cts.devicepolicy.MixedProfileOwnerTest#testDelegation
Test: atest com.android.cts.devicepolicy.MixedDeviceOwnerTest#testDelegation
Test: atest com.android.cts.devicepolicy.MixedManagedProfileOwnerTest#testDelegation
Test: Manual with TestDPC-replica
Change-Id: I508fdda0572041cf121d0e297c93d51e981545e3
parent c894b352
Loading
Loading
Loading
Loading
+14 −2
Original line number Diff line number Diff line
@@ -6432,6 +6432,13 @@ package android.app.admin {
    field public static final android.os.Parcelable.Creator<android.app.admin.ConnectEvent> CREATOR;
  }
  public class DelegatedAdminReceiver extends android.content.BroadcastReceiver {
    ctor public DelegatedAdminReceiver();
    method public java.lang.String onChoosePrivateKeyAlias(android.content.Context, android.content.Intent, int, android.net.Uri, java.lang.String);
    method public void onNetworkLogsAvailable(android.content.Context, android.content.Intent, long, int);
    method public void onReceive(android.content.Context, android.content.Intent);
  }
  public final class DeviceAdminInfo implements android.os.Parcelable {
    ctor public DeviceAdminInfo(android.content.Context, android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
    method public int describeContents();
@@ -6494,11 +6501,13 @@ package android.app.admin {
    method public void onUserStarted(android.content.Context, android.content.Intent, android.os.UserHandle);
    method public void onUserStopped(android.content.Context, android.content.Intent, android.os.UserHandle);
    method public void onUserSwitched(android.content.Context, android.content.Intent, android.os.UserHandle);
    field public static final java.lang.String ACTION_CHOOSE_PRIVATE_KEY_ALIAS = "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS";
    field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
    field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
    field public static final java.lang.String ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED";
    field public static final java.lang.String ACTION_LOCK_TASK_ENTERING = "android.app.action.LOCK_TASK_ENTERING";
    field public static final java.lang.String ACTION_LOCK_TASK_EXITING = "android.app.action.LOCK_TASK_EXITING";
    field public static final java.lang.String ACTION_NETWORK_LOGS_AVAILABLE = "android.app.action.NETWORK_LOGS_AVAILABLE";
    field public static final java.lang.String ACTION_PASSWORD_CHANGED = "android.app.action.ACTION_PASSWORD_CHANGED";
    field public static final java.lang.String ACTION_PASSWORD_EXPIRING = "android.app.action.ACTION_PASSWORD_EXPIRING";
    field public static final java.lang.String ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED";
@@ -6743,10 +6752,13 @@ package android.app.admin {
    field public static final java.lang.String DELEGATION_APP_RESTRICTIONS = "delegation-app-restrictions";
    field public static final java.lang.String DELEGATION_BLOCK_UNINSTALL = "delegation-block-uninstall";
    field public static final java.lang.String DELEGATION_CERT_INSTALL = "delegation-cert-install";
    field public static final java.lang.String DELEGATION_CERT_SELECTION = "delegation-cert-selection";
    field public static final java.lang.String DELEGATION_ENABLE_SYSTEM_APP = "delegation-enable-system-app";
    field public static final java.lang.String DELEGATION_INSTALL_EXISTING_PACKAGE = "delegation-install-existing-package";
    field public static final java.lang.String DELEGATION_KEEP_UNINSTALLED_PACKAGES = "delegation-keep-uninstalled-packages";
    field public static final java.lang.String DELEGATION_NETWORK_LOGGING = "delegation-network-logging";
    field public static final java.lang.String DELEGATION_PACKAGE_ACCESS = "delegation-package-access";
    field public static final java.lang.String DELEGATION_PACKAGE_INSTALLATION = "delegation-package-installation";
    field public static final java.lang.String DELEGATION_PERMISSION_GRANT = "delegation-permission-grant";
    field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
    field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
@@ -53251,7 +53263,7 @@ package android.webkit {
    method public abstract boolean getDomStorageEnabled();
    method public abstract java.lang.String getFantasyFontFamily();
    method public abstract java.lang.String getFixedFontFamily();
    method public abstract int getForceDarkMode();
    method public int getForceDarkMode();
    method public abstract boolean getJavaScriptCanOpenWindowsAutomatically();
    method public abstract boolean getJavaScriptEnabled();
    method public abstract android.webkit.WebSettings.LayoutAlgorithm getLayoutAlgorithm();
@@ -53298,7 +53310,7 @@ package android.webkit {
    method public abstract deprecated void setEnableSmoothTransition(boolean);
    method public abstract void setFantasyFontFamily(java.lang.String);
    method public abstract void setFixedFontFamily(java.lang.String);
    method public abstract void setForceDarkMode(int);
    method public void setForceDarkMode(int);
    method public abstract deprecated void setGeolocationDatabasePath(java.lang.String);
    method public abstract void setGeolocationEnabled(boolean);
    method public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean);
+129 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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 static android.app.admin.DeviceAdminReceiver.ACTION_CHOOSE_PRIVATE_KEY_ALIAS;
import static android.app.admin.DeviceAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE;
import static android.app.admin.DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_ALIAS;
import static android.app.admin.DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_SENDER_UID;
import static android.app.admin.DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_URI;
import static android.app.admin.DeviceAdminReceiver.EXTRA_NETWORK_LOGS_COUNT;
import static android.app.admin.DeviceAdminReceiver.EXTRA_NETWORK_LOGS_TOKEN;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.security.KeyChain;
import android.util.Log;

/**
 * Base class for delegated apps to handle callbacks related to their delegated capabilities.
 *
 * <p>Delegated apps are apps that receive additional capabilities from the profile owner or
 * device owner apps. Some of these capabilities involve the framework calling into the apps.
 * To receive these callbacks, delegated apps should subclass this class and override the
 * appropriate methods here. The subclassed receiver needs to be published in the app's
 * manifest, with appropriate intent filters to mark which callbacks the receiver is interested
 * in. An app can have multiple receivers as long as they listen for disjoint set of callbacks.
 * For the manifest definitions, it must be protected by the
 * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission to ensure only
 * the system can trigger these callbacks.
 *
 * <p>The callback methods happen on the main thread of the process.  Thus long running
 * operations must be done on another thread.  Note that because a receiver
 * is done once returning from its receive function, such long-running operations
 * should probably be done in a {@link Service}.
 *
 * @see DevicePolicyManager#setDelegatedScopes
 * @see DeviceAdminReceiver
 */
public class DelegatedAdminReceiver extends BroadcastReceiver {
    private static final String TAG = "DelegatedAdminReceiver";

    /**
     * Allows this receiver to select the alias for a private key and certificate pair for
     * authentication.  If this method returns null, the default {@link android.app.Activity} will
     * be shown that lets the user pick a private key and certificate pair.
     *
     * <p> This callback is only applicable if the delegated app has
     * {@link DevicePolicyManager#DELEGATION_CERT_SELECTION} capability. Additionally, it must
     * declare an intent fitler for {@link #ACTION_CHOOSE_PRIVATE_KEY_ALIAS} in the receiver's
     * manifest in order to receive this callback.
     *
     * @param context The running context as per {@link #onReceive}.
     * @param intent The received intent as per {@link #onReceive}.
     * @param uid The uid asking for the private key and certificate pair.
     * @param uri The URI to authenticate, may be null.
     * @param alias The alias preselected by the client, or null.
     * @return The private key alias to return and grant access to.
     * @see KeyChain#choosePrivateKeyAlias
     */
    public String onChoosePrivateKeyAlias(Context context, Intent intent, int uid, Uri uri,
            String alias) {
        return null;
    }

    /**
     * Called each time a new batch of network logs can be retrieved. This callback method will only
     * ever be called when network logging is enabled. The logs can only be retrieved while network
     * logging is enabled.
     *
     * <p>If a secondary user or profile is created, this callback won't be received until all users
     * become affiliated again (even if network logging is enabled). It will also no longer be
     * possible to retrieve the network logs batch with the most recent {@code batchToken} provided
     * by this callback. See {@link DevicePolicyManager#setAffiliationIds}.
     *
     * <p> This callback is only applicable if the delegated app has
     * {@link DevicePolicyManager#DELEGATION_NETWORK_LOGGING} capability. Additionally, it must
     * declare an intent fitler for {@link #ACTION_NETWORK_LOGS_AVAILABLE} in the receiver's
     * manifest in order to receive this callback.
     *
     * @param context The running context as per {@link #onReceive}.
     * @param intent The received intent as per {@link #onReceive}.
     * @param batchToken The token representing the current batch of network logs.
     * @param networkLogsCount The total count of events in the current batch of network logs.
     * @see DevicePolicyManager#retrieveNetworkLogs
     */
    public void onNetworkLogsAvailable(Context context, Intent intent, long batchToken,
            int networkLogsCount) {
    }

    /**
     * Intercept delegated device administrator broadcasts. Implementations should not override
     * this method; implement the convenience callbacks for each action instead.
     */
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (ACTION_CHOOSE_PRIVATE_KEY_ALIAS.equals(action)) {
            int uid = intent.getIntExtra(EXTRA_CHOOSE_PRIVATE_KEY_SENDER_UID, -1);
            Uri uri = intent.getParcelableExtra(EXTRA_CHOOSE_PRIVATE_KEY_URI);
            String alias = intent.getStringExtra(EXTRA_CHOOSE_PRIVATE_KEY_ALIAS);
            String chosenAlias = onChoosePrivateKeyAlias(context, intent, uid, uri, alias);
            setResultData(chosenAlias);
        } else if (ACTION_NETWORK_LOGS_AVAILABLE.equals(action)) {
            long batchToken = intent.getLongExtra(EXTRA_NETWORK_LOGS_TOKEN, -1);
            int networkLogsCount = intent.getIntExtra(EXTRA_NETWORK_LOGS_COUNT, 0);
            onNetworkLogsAvailable(context, intent, batchToken, networkLogsCount);
        } else {
            Log.w(TAG, "Unhandled broadcast: " + action);
        }
    }
}
+6 −2
Original line number Diff line number Diff line
@@ -297,7 +297,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
    /**
     * Broadcast action: notify that a new batch of network logs is ready to be collected.
     * @see DeviceAdminReceiver#onNetworkLogsAvailable
     * @hide
     * @see DelegatedAdminReceiver#onNetworkLogsAvailable
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    @BroadcastBehavior(explicitOnly = true)
@@ -426,7 +426,11 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
     */
    public static final int BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE = 1;

    /** @hide */
    /**
     * Broadcast action: notify that some app is attempting to choose a KeyChain key.
     * @see DeviceAdminReceiver#onChoosePrivateKeyAlias
     * @see DelegatedAdminReceiver#onChoosePrivateKeyAlias
     */
    public static final String ACTION_CHOOSE_PRIVATE_KEY_ALIAS =
            "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS";

+50 −11
Original line number Diff line number Diff line
@@ -1485,11 +1485,45 @@ public class DevicePolicyManager {

    /**
     * Delegation of management of uninstalled packages. This scope grants access to the
     * {@code #setKeepUninstalledPackages} and {@code #getKeepUninstalledPackages} APIs.
     * {@link #setKeepUninstalledPackages} and {@link #getKeepUninstalledPackages} APIs.
     */
    public static final String DELEGATION_KEEP_UNINSTALLED_PACKAGES =
            "delegation-keep-uninstalled-packages";

    /**
     * Grants access to {@link #setNetworkLoggingEnabled}, {@link #isNetworkLoggingEnabled} and
     * {@link #retrieveNetworkLogs}. Once granted the delegated app will start receiving
     * DelegatedAdminReceiver.onNetworkLogsAvailable() callback, and Device owner will no longer
     * receive the DeviceAdminReceiver.onNetworkLogsAvailable() callback.
     * There can be at most one app that has this delegation.
     * If another app already had delegated network logging access,
     * it will lose the delegation when a new app is delegated.
     *
     * <p> Can only be granted by Device Owner.
     */
    public static final String DELEGATION_NETWORK_LOGGING = "delegation-network-logging";

    /**
     * Grants access to selection of KeyChain certificates on behalf of requesting apps.
     * Once granted the app will start receiving
     * DelegatedAdminReceiver.onChoosePrivateKeyAlias. The caller (PO/DO) will
     * no longer receive {@link DeviceAdminReceiver#onChoosePrivateKeyAlias()}.
     * There can be at most one app that has this delegation.
     * If another app already had delegated certificate selection access,
     * it will lose the delegation when a new app is delegated.
     *
     * <p> Can be granted by Device Owner or Profile Owner.
     */
    public static final String DELEGATION_CERT_SELECTION = "delegation-cert-selection";


    /**
     * Delegation of silent APK installation via {@link android.content.pm.PackageInstaller} APIs.
     *
     * <p> Can only be delegated by Device Owner.
     */
    public static final String DELEGATION_PACKAGE_INSTALLATION = "delegation-package-installation";

    /**
     * No management for current user in-effect. This is the default.
     * @hide
@@ -9148,7 +9182,8 @@ public class DevicePolicyManager {
    }

    /**
     * Called by a device owner to control the network logging feature.
     * Called by a device owner or delegated app with {@link #DELEGATION_NETWORK_LOGGING} to
     * control the network logging feature.
     *
     * <p> Network logs contain DNS lookup and connect() library call events. The following library
     *     functions are recorded while network logging is active:
@@ -9185,16 +9220,17 @@ public class DevicePolicyManager {
     * all users to become affiliated. Therefore it's recommended that affiliation ids are set for
     * new users as soon as possible after provisioning via {@link #setAffiliationIds}.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
     *        {@code null} if called by a delegated app.
     * @param enabled whether network logging should be enabled or not.
     * @throws SecurityException if {@code admin} is not a device owner.
     * @see #setAffiliationIds
     * @see #retrieveNetworkLogs
     */
    public void setNetworkLoggingEnabled(@NonNull ComponentName admin, boolean enabled) {
    public void setNetworkLoggingEnabled(@Nullable ComponentName admin, boolean enabled) {
        throwIfParentInstance("setNetworkLoggingEnabled");
        try {
            mService.setNetworkLoggingEnabled(admin, enabled);
            mService.setNetworkLoggingEnabled(admin, mContext.getPackageName(), enabled);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
@@ -9204,7 +9240,8 @@ public class DevicePolicyManager {
     * Return whether network logging is enabled by a device owner.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Can only
     * be {@code null} if the caller has MANAGE_USERS permission.
     * be {@code null} if the caller is a delegated app with {@link #DELEGATION_NETWORK_LOGGING}
     * or has MANAGE_USERS permission.
     * @return {@code true} if network logging is enabled by device owner, {@code false} otherwise.
     * @throws SecurityException if {@code admin} is not a device owner and caller has
     * no MANAGE_USERS permission
@@ -9212,14 +9249,15 @@ public class DevicePolicyManager {
    public boolean isNetworkLoggingEnabled(@Nullable ComponentName admin) {
        throwIfParentInstance("isNetworkLoggingEnabled");
        try {
            return mService.isNetworkLoggingEnabled(admin);
            return mService.isNetworkLoggingEnabled(admin, mContext.getPackageName());
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
    }

    /**
     * Called by device owner to retrieve the most recent batch of network logging events.
     * Called by device owner or delegated app with {@link #DELEGATION_NETWORK_LOGGING} to retrieve
     * the most recent batch of network logging events.
     * A device owner has to provide a batchToken provided as part of
     * {@link DeviceAdminReceiver#onNetworkLogsAvailable} callback. If the token doesn't match the
     * token of the most recent available batch of logs, {@code null} will be returned.
@@ -9238,7 +9276,8 @@ public class DevicePolicyManager {
     * by {@link DeviceAdminReceiver#onNetworkLogsAvailable}. See
     * {@link DevicePolicyManager#setAffiliationIds}.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
     *        {@code null} if called by a delegated app.
     * @param batchToken A token of the batch to retrieve
     * @return A new batch of network logs which is a list of {@link NetworkEvent}. Returns
     *        {@code null} if the batch represented by batchToken is no longer available or if
@@ -9248,11 +9287,11 @@ public class DevicePolicyManager {
     * @see #setAffiliationIds
     * @see DeviceAdminReceiver#onNetworkLogsAvailable
     */
    public @Nullable List<NetworkEvent> retrieveNetworkLogs(@NonNull ComponentName admin,
    public @Nullable List<NetworkEvent> retrieveNetworkLogs(@Nullable ComponentName admin,
            long batchToken) {
        throwIfParentInstance("retrieveNetworkLogs");
        try {
            return mService.retrieveNetworkLogs(admin, batchToken);
            return mService.retrieveNetworkLogs(admin, mContext.getPackageName(), batchToken);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
+3 −3
Original line number Diff line number Diff line
@@ -366,9 +366,9 @@ interface IDevicePolicyManager {
    void setBackupServiceEnabled(in ComponentName admin, boolean enabled);
    boolean isBackupServiceEnabled(in ComponentName admin);

    void setNetworkLoggingEnabled(in ComponentName admin, boolean enabled);
    boolean isNetworkLoggingEnabled(in ComponentName admin);
    List<NetworkEvent> retrieveNetworkLogs(in ComponentName admin, long batchToken);
    void setNetworkLoggingEnabled(in ComponentName admin, in String packageName, boolean enabled);
    boolean isNetworkLoggingEnabled(in ComponentName admin, in String packageName);
    List<NetworkEvent> retrieveNetworkLogs(in ComponentName admin, in String packageName, long batchToken);

    boolean bindDeviceAdminServiceAsUser(in ComponentName admin,
        IApplicationThread caller, IBinder token, in Intent service,
Loading