diff options
| author | Pablo Soëtard <soetard@google.com> | 2021-06-25 18:10:21 +0000 |
|---|---|---|
| committer | Antoan Angelov <arangelov@google.com> | 2021-07-13 18:16:27 +0000 |
| commit | bb419eee4a401bab9eb36377274a48d25f751fd8 (patch) | |
| tree | 4920b7025269f24defbc20de13d6302ac270cd4b /core/java/android | |
| parent | 835c8181a7cb7a3a4f2291c8c70268b81364177c (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.java | 50 | ||||
| -rw-r--r-- | core/java/android/app/admin/ProvisioningIntentHelper.java | 178 |
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; + } +} |
