Merge "Transform NFC Intent to provisioning Intent"

This commit is contained in:
Antoan Angelov
2021-07-16 11:39:24 +00:00
committed by Android (Google) Code Review
3 changed files with 229 additions and 1 deletions

2
core/api/system-current.txt Normal file → Executable file
View File

@@ -924,6 +924,7 @@ package android.app.admin {
}
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 @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();
@@ -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 PROVISIONING_TRIGGER_CLOUD_ENROLLMENT = 1; // 0x1
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 public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0

View File

@@ -56,6 +56,7 @@ import android.graphics.Bitmap;
import android.net.PrivateDnsConnectivityChecker;
import android.net.ProxyInfo;
import android.net.Uri;
import android.nfc.NfcAdapter;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
@@ -1216,7 +1217,8 @@ public class DevicePolicyManager {
PROVISIONING_TRIGGER_CLOUD_ENROLLMENT,
PROVISIONING_TRIGGER_QR_CODE,
PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER,
PROVISIONING_TRIGGER_MANAGED_ACCOUNT
PROVISIONING_TRIGGER_MANAGED_ACCOUNT,
PROVISIONING_TRIGGER_NFC
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProvisioningTrigger {}
@@ -1254,6 +1256,7 @@ public class DevicePolicyManager {
* @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
* @see #PROVISIONING_TRIGGER_QR_CODE
* @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
* @see #PROVISIONING_TRIGGER_NFC
* @hide
*/
@SystemApi
@@ -1265,6 +1268,7 @@ public class DevicePolicyManager {
* @see #PROVISIONING_TRIGGER_QR_CODE
* @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
* @see #PROVISIONING_TRIGGER_UNSPECIFIED
* @see #PROVISIONING_TRIGGER_NFC
* @hide
*/
@SystemApi
@@ -1276,6 +1280,7 @@ public class DevicePolicyManager {
* @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
* @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
* @see #PROVISIONING_TRIGGER_UNSPECIFIED
* @see #PROVISIONING_TRIGGER_NFC
* @hide
*/
@SystemApi
@@ -1295,6 +1300,7 @@ public class DevicePolicyManager {
* @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
* @see #PROVISIONING_TRIGGER_QR_CODE
* @see #PROVISIONING_TRIGGER_UNSPECIFIED
* @see #PROVISIONING_TRIGGER_NFC
* @hide
*/
@SystemApi
@@ -1308,11 +1314,24 @@ public class DevicePolicyManager {
* @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
* @see #PROVISIONING_TRIGGER_QR_CODE
* @see #PROVISIONING_TRIGGER_UNSPECIFIED
* @see #PROVISIONING_TRIGGER_NFC
* @hide
*/
@SystemApi
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
* organization-owned.
@@ -14009,4 +14028,33 @@ public class DevicePolicyManager {
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);
}
}

View File

@@ -0,0 +1,178 @@
/*
* 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;
}
}