summaryrefslogtreecommitdiff
path: root/core/java/android
diff options
context:
space:
mode:
authorPablo Soëtard <soetard@google.com>2021-06-25 18:10:21 +0000
committerAntoan Angelov <arangelov@google.com>2021-07-13 18:16:27 +0000
commitbb419eee4a401bab9eb36377274a48d25f751fd8 (patch)
tree4920b7025269f24defbc20de13d6302ac270cd4b /core/java/android
parent835c8181a7cb7a3a4f2291c8c70268b81364177c (diff)
Transform NFC Intent to provisioning Intent
Generates a provisioning intent with the extra data from a nfc intent. Test: CTS tests are included in the other CL in this topic Bug: 192228539 Change-Id: I00ed374191bc3a48dcb336ed056395be59c445f5
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java50
-rw-r--r--core/java/android/app/admin/ProvisioningIntentHelper.java178
2 files changed, 227 insertions, 1 deletions
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7e5b5a677efe..2c38688fe685 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -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,12 +1314,25 @@ 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);
+ }
}
diff --git a/core/java/android/app/admin/ProvisioningIntentHelper.java b/core/java/android/app/admin/ProvisioningIntentHelper.java
new file mode 100644
index 000000000000..fbad90c30d7e
--- /dev/null
+++ b/core/java/android/app/admin/ProvisioningIntentHelper.java
@@ -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;
+ }
+}