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

Commit 3294ddb2 authored by Antoan Angelov's avatar Antoan Angelov Committed by Android (Google) Code Review
Browse files

Merge "Transform NFC Intent to provisioning Intent"

parents e39c36c1 bb419eee
Loading
Loading
Loading
Loading

core/api/system-current.txt

100644 → 100755
+2 −0
Original line number Original line Diff line number Diff line
@@ -924,6 +924,7 @@ package android.app.admin {
  }
  }
  public class DevicePolicyManager {
  public class DevicePolicyManager {
    method @Nullable public android.content.Intent createProvisioningIntentFromNfcIntent(@NonNull android.content.Intent);
    method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
    method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
@@ -972,6 +973,7 @@ package android.app.admin {
    field public static final int FLAG_SUPPORTED_MODES_PERSONALLY_OWNED = 2; // 0x2
    field public static final int FLAG_SUPPORTED_MODES_PERSONALLY_OWNED = 2; // 0x2
    field public static final int PROVISIONING_TRIGGER_CLOUD_ENROLLMENT = 1; // 0x1
    field public static final int PROVISIONING_TRIGGER_CLOUD_ENROLLMENT = 1; // 0x1
    field public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4; // 0x4
    field public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4; // 0x4
    field public static final int PROVISIONING_TRIGGER_NFC = 5; // 0x5
    field @Deprecated public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; // 0x3
    field @Deprecated public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; // 0x3
    field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
    field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
    field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0
    field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0
+49 −1
Original line number Original line Diff line number Diff line
@@ -56,6 +56,7 @@ import android.graphics.Bitmap;
import android.net.PrivateDnsConnectivityChecker;
import android.net.PrivateDnsConnectivityChecker;
import android.net.ProxyInfo;
import android.net.ProxyInfo;
import android.net.Uri;
import android.net.Uri;
import android.nfc.NfcAdapter;
import android.os.Build;
import android.os.Build;
import android.os.Bundle;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor;
@@ -1216,7 +1217,8 @@ public class DevicePolicyManager {
            PROVISIONING_TRIGGER_CLOUD_ENROLLMENT,
            PROVISIONING_TRIGGER_CLOUD_ENROLLMENT,
            PROVISIONING_TRIGGER_QR_CODE,
            PROVISIONING_TRIGGER_QR_CODE,
            PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER,
            PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER,
            PROVISIONING_TRIGGER_MANAGED_ACCOUNT
            PROVISIONING_TRIGGER_MANAGED_ACCOUNT,
            PROVISIONING_TRIGGER_NFC
    })
    })
    @Retention(RetentionPolicy.SOURCE)
    @Retention(RetentionPolicy.SOURCE)
    public @interface ProvisioningTrigger {}
    public @interface ProvisioningTrigger {}
@@ -1254,6 +1256,7 @@ public class DevicePolicyManager {
     * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
     * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
     * @see #PROVISIONING_TRIGGER_QR_CODE
     * @see #PROVISIONING_TRIGGER_QR_CODE
     * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
     * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
     * @see #PROVISIONING_TRIGGER_NFC
     * @hide
     * @hide
     */
     */
    @SystemApi
    @SystemApi
@@ -1265,6 +1268,7 @@ public class DevicePolicyManager {
     * @see #PROVISIONING_TRIGGER_QR_CODE
     * @see #PROVISIONING_TRIGGER_QR_CODE
     * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
     * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
     * @see #PROVISIONING_TRIGGER_UNSPECIFIED
     * @see #PROVISIONING_TRIGGER_UNSPECIFIED
     * @see #PROVISIONING_TRIGGER_NFC
     * @hide
     * @hide
     */
     */
    @SystemApi
    @SystemApi
@@ -1276,6 +1280,7 @@ public class DevicePolicyManager {
     * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
     * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
     * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
     * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
     * @see #PROVISIONING_TRIGGER_UNSPECIFIED
     * @see #PROVISIONING_TRIGGER_UNSPECIFIED
     * @see #PROVISIONING_TRIGGER_NFC
     * @hide
     * @hide
     */
     */
    @SystemApi
    @SystemApi
@@ -1295,6 +1300,7 @@ public class DevicePolicyManager {
     * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
     * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
     * @see #PROVISIONING_TRIGGER_QR_CODE
     * @see #PROVISIONING_TRIGGER_QR_CODE
     * @see #PROVISIONING_TRIGGER_UNSPECIFIED
     * @see #PROVISIONING_TRIGGER_UNSPECIFIED
     * @see #PROVISIONING_TRIGGER_NFC
     * @hide
     * @hide
     */
     */
    @SystemApi
    @SystemApi
@@ -1308,11 +1314,24 @@ public class DevicePolicyManager {
     * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
     * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
     * @see #PROVISIONING_TRIGGER_QR_CODE
     * @see #PROVISIONING_TRIGGER_QR_CODE
     * @see #PROVISIONING_TRIGGER_UNSPECIFIED
     * @see #PROVISIONING_TRIGGER_UNSPECIFIED
     * @see #PROVISIONING_TRIGGER_NFC
     * @hide
     * @hide
     */
     */
    @SystemApi
    @SystemApi
    public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4;
    public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4;
    /**
     * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning is
     * triggered by tapping an NFC tag.
     * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
     * @see #PROVISIONING_TRIGGER_QR_CODE
     * @see #PROVISIONING_TRIGGER_UNSPECIFIED
     * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
     * @hide
     */
    @SystemApi
    public static final int PROVISIONING_TRIGGER_NFC = 5;
    /**
    /**
     * Flag for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning is
     * Flag for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning is
     * organization-owned.
     * organization-owned.
@@ -14009,4 +14028,33 @@ public class DevicePolicyManager {
            throw e.rethrowFromSystemServer();
            throw e.rethrowFromSystemServer();
        }
        }
    }
    }
    /**
     * Creates a {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent
     * from the provided {@code nfcIntent}.
     *
     * <p>Prerequisites to create the provisioning intent:
     *
     * <ul>
     * <li>{@code nfcIntent}'s action is {@link NfcAdapter#ACTION_NDEF_DISCOVERED}</li>
     * <li>{@code nfcIntent}'s NFC properties contain either
     * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} or
     * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} </li>
     * </ul>
     *
     * This method returns {@code null} if the prerequisites are not met or if an error occurs
     * when reading the NFC properties.
     *
     * @param nfcIntent the nfc intent generated from scanning a NFC tag
     * @return a {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent with
     * intent extras as read by {@code nfcIntent}'s NFC properties or {@code null} if the
     * prerequisites are not met or if an error occurs when reading the NFC properties.
     *
     * @hide
     */
    @Nullable
    @SystemApi
    public Intent createProvisioningIntentFromNfcIntent(@NonNull Intent nfcIntent) {
        return ProvisioningIntentHelper.createProvisioningIntentFromNfcIntent(nfcIntent);
    }
}
}
+178 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 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.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TRIGGER;
import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC;
import static android.app.admin.DevicePolicyManager.PROVISIONING_TRIGGER_NFC;
import static android.nfc.NfcAdapter.EXTRA_NDEF_MESSAGES;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Intent;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;

import java.io.IOException;
import java.io.StringReader;
import java.util.Enumeration;
import java.util.Properties;

/**
 * Utility class that provides functionality to create provisioning intents from nfc intents.
 */
final class ProvisioningIntentHelper {

    private static final String TAG = "ProvisioningIntentHelper";

    /**
     * This class is never instantiated
     */
    private ProvisioningIntentHelper() { }

    @Nullable
    public static Intent createProvisioningIntentFromNfcIntent(@NonNull Intent nfcIntent) {
        requireNonNull(nfcIntent);

        if (!NfcAdapter.ACTION_NDEF_DISCOVERED.equals(nfcIntent.getAction())) {
            Log.e(TAG, "Wrong Nfc action: " + nfcIntent.getAction());
            return null;
        }

        NdefRecord firstRecord = getFirstNdefRecord(nfcIntent);

        if (firstRecord != null) {
            return createProvisioningIntentFromNdefRecord(firstRecord);
        }

        return null;
    }


    private static Intent createProvisioningIntentFromNdefRecord(NdefRecord firstRecord) {
        requireNonNull(firstRecord);

        Properties properties = loadPropertiesFromPayload(firstRecord.getPayload());

        if (properties == null) {
            Log.e(TAG, "Failed to load NdefRecord properties.");
            return null;
        }

        Bundle bundle = createBundleFromProperties(properties);

        if (!containsRequiredProvisioningExtras(bundle)) {
            Log.e(TAG, "Bundle does not contain the required provisioning extras.");
            return null;
        }

        return createProvisioningIntentFromBundle(bundle);
    }

    private static Properties loadPropertiesFromPayload(byte[] payload) {
        Properties properties = new Properties();

        try {
            properties.load(new StringReader(new String(payload, UTF_8)));
        } catch (IOException e) {
            Log.e(TAG, "NFC Intent properties loading failed.");
            return null;
        }

        return properties;
    }

    private static Bundle createBundleFromProperties(Properties properties) {
        Enumeration propertyNames = properties.propertyNames();
        Bundle bundle = new Bundle();

        while (propertyNames.hasMoreElements()) {
            String propertyName = (String) propertyNames.nextElement();
            addPropertyToBundle(propertyName, properties, bundle);
        }
        return bundle;
    }

    private static void addPropertyToBundle(
            String propertyName, Properties properties, Bundle bundle) {
        if(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME.equals(propertyName)) {
            ComponentName componentName = ComponentName.unflattenFromString(
                    properties.getProperty(propertyName));
            bundle.putParcelable(propertyName, componentName);
        }
        else {
            bundle.putString(propertyName, properties.getProperty(propertyName));
        }
    }

    private static Intent createProvisioningIntentFromBundle(Bundle bundle) {
        requireNonNull(bundle);

        Intent provisioningIntent = new Intent(ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE);

        provisioningIntent.putExtras(bundle);

        provisioningIntent.putExtra(EXTRA_PROVISIONING_TRIGGER, PROVISIONING_TRIGGER_NFC);

        return provisioningIntent;
    }

    private static boolean containsRequiredProvisioningExtras(Bundle bundle) {
        return bundle.containsKey(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME) ||
                bundle.containsKey(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME);
    }

    /**
     * Returns the first {@link NdefRecord} found with a recognized MIME-type
     */
    private static NdefRecord getFirstNdefRecord(Intent nfcIntent) {
        Parcelable[] ndefMessages = nfcIntent.getParcelableArrayExtra(EXTRA_NDEF_MESSAGES);
        if (ndefMessages == null) {
            Log.i(TAG, "No EXTRA_NDEF_MESSAGES from nfcIntent");
            return null;
        }

        for (Parcelable rawMsg : ndefMessages) {
            NdefMessage msg = (NdefMessage) rawMsg;
            for (NdefRecord record : msg.getRecords()) {
                String mimeType = new String(record.getType(), UTF_8);

                // Only one first message with NFC_MIME_TYPE is used.
                if (MIME_TYPE_PROVISIONING_NFC.equals(mimeType)) {
                    return record;
                }

                // Assume only first record of message is used.
                break;
            }
        }

        Log.i(TAG, "No compatible records found on nfcIntent");
        return null;
    }
}