1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.nfcprovisioning; 18 19 import android.app.Activity; 20 import android.app.admin.DevicePolicyManager; 21 import android.nfc.NdefMessage; 22 import android.nfc.NdefRecord; 23 import android.nfc.NfcAdapter; 24 import android.nfc.NfcEvent; 25 import android.os.Bundle; 26 import android.support.annotation.Nullable; 27 import android.support.v4.app.Fragment; 28 import android.support.v4.app.LoaderManager; 29 import android.support.v4.content.Loader; 30 import android.text.TextUtils; 31 import android.view.LayoutInflater; 32 import android.view.View; 33 import android.view.ViewGroup; 34 import android.widget.EditText; 35 36 import java.io.ByteArrayOutputStream; 37 import java.io.IOException; 38 import java.util.Map; 39 import java.util.Properties; 40 41 /** 42 * Provides UI and logic for NFC provisioning. 43 * <p> 44 * This fragment creates an intent, which sends parameters to a second device via an Nfc bump. If 45 * the second device is factory reset, this will start provisioning the second device to set it up 46 * as an owned device. 47 * </p> 48 */ 49 public class NfcProvisioningFragment extends Fragment implements 50 NfcAdapter.CreateNdefMessageCallback, 51 TextWatcherWrapper.OnTextChangedListener, 52 LoaderManager.LoaderCallbacks<Map<String, String>> { 53 54 private static final int LOADER_PROVISIONING_VALUES = 1; 55 56 // View references 57 private EditText mEditPackageName; 58 private EditText mEditLocale; 59 private EditText mEditTimezone; 60 private EditText mEditWifiSsid; 61 private EditText mEditWifiSecurityType; 62 private EditText mEditWifiPassword; 63 64 // Values to be set via NFC bump 65 private Map<String, String> mProvisioningValues; 66 67 @Override onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)68 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 69 @Nullable Bundle savedInstanceState) { 70 return inflater.inflate(R.layout.fragment_nfc_provisioning, container, false); 71 } 72 73 @Override onViewCreated(View view, @Nullable Bundle savedInstanceState)74 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 75 // Retrieve view references 76 mEditPackageName = (EditText) view.findViewById(R.id.package_name); 77 mEditLocale = (EditText) view.findViewById(R.id.locale); 78 mEditTimezone = (EditText) view.findViewById(R.id.timezone); 79 mEditWifiSsid = (EditText) view.findViewById(R.id.wifi_ssid); 80 mEditWifiSecurityType = (EditText) view.findViewById(R.id.wifi_security_type); 81 mEditWifiPassword = (EditText) view.findViewById(R.id.wifi_password); 82 // Bind event handlers 83 mEditPackageName.addTextChangedListener(new TextWatcherWrapper(R.id.package_name, this)); 84 mEditLocale.addTextChangedListener(new TextWatcherWrapper(R.id.locale, this)); 85 mEditTimezone.addTextChangedListener(new TextWatcherWrapper(R.id.timezone, this)); 86 mEditWifiSsid.addTextChangedListener(new TextWatcherWrapper(R.id.wifi_ssid, this)); 87 mEditWifiSecurityType.addTextChangedListener( 88 new TextWatcherWrapper(R.id.wifi_security_type, this)); 89 mEditWifiPassword.addTextChangedListener(new TextWatcherWrapper(R.id.wifi_password, this)); 90 } 91 92 @Override onStart()93 public void onStart() { 94 super.onStart(); 95 Activity activity = getActivity(); 96 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(activity); 97 if (adapter != null) { 98 adapter.setNdefPushMessageCallback(this, activity); 99 } 100 getLoaderManager().initLoader(LOADER_PROVISIONING_VALUES, null, this); 101 } 102 103 @Override createNdefMessage(NfcEvent event)104 public NdefMessage createNdefMessage(NfcEvent event) { 105 if (mProvisioningValues == null) { 106 return null; 107 } 108 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 109 Properties properties = new Properties(); 110 // Store all the values into the Properties object 111 for (Map.Entry<String, String> e : mProvisioningValues.entrySet()) { 112 if (!TextUtils.isEmpty(e.getValue())) { 113 String value; 114 if (e.getKey().equals(DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SSID)) { 115 // Make sure to surround SSID with double quotes 116 value = e.getValue(); 117 if (!value.startsWith("\"") || !value.endsWith("\"")) { 118 value = "\"" + value + "\""; 119 } 120 } else { 121 value = e.getValue(); 122 } 123 properties.put(e.getKey(), value); 124 } 125 } 126 // Make sure to put local time in the properties. This is necessary on some devices to 127 // reliably download the device owner APK from an HTTPS connection. 128 if (!properties.contains(DevicePolicyManager.EXTRA_PROVISIONING_LOCAL_TIME)) { 129 properties.put(DevicePolicyManager.EXTRA_PROVISIONING_LOCAL_TIME, 130 String.valueOf(System.currentTimeMillis())); 131 } 132 try { 133 properties.store(stream, getString(R.string.nfc_comment)); 134 NdefRecord record = NdefRecord.createMime( 135 DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC, stream.toByteArray()); 136 return new NdefMessage(new NdefRecord[]{record}); 137 } catch (IOException e) { 138 e.printStackTrace(); 139 } 140 return null; 141 } 142 143 @Override onTextChanged(int id, String s)144 public void onTextChanged(int id, String s) { 145 if (mProvisioningValues == null) { 146 return; 147 } 148 switch (id) { 149 case R.id.package_name: 150 mProvisioningValues.put( 151 DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME, s); 152 break; 153 case R.id.locale: 154 mProvisioningValues.put(DevicePolicyManager.EXTRA_PROVISIONING_LOCALE, s); 155 break; 156 case R.id.timezone: 157 mProvisioningValues.put(DevicePolicyManager.EXTRA_PROVISIONING_TIME_ZONE, s); 158 break; 159 case R.id.wifi_ssid: 160 mProvisioningValues.put(DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SSID, s); 161 break; 162 case R.id.wifi_security_type: 163 mProvisioningValues.put( 164 DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SECURITY_TYPE, s); 165 break; 166 case R.id.wifi_password: 167 mProvisioningValues.put(DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PASSWORD, s); 168 break; 169 } 170 } 171 172 @Override onCreateLoader(int id, Bundle args)173 public Loader<Map<String, String>> onCreateLoader(int id, Bundle args) { 174 if (id == LOADER_PROVISIONING_VALUES) { 175 return new ProvisioningValuesLoader(getActivity()); 176 } 177 return null; 178 } 179 180 @Override onLoadFinished(Loader<Map<String, String>> loader, Map<String, String> values)181 public void onLoadFinished(Loader<Map<String, String>> loader, Map<String, String> values) { 182 if (loader.getId() == LOADER_PROVISIONING_VALUES) { 183 mProvisioningValues = values; 184 mEditPackageName.setText(values.get( 185 DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME)); 186 mEditLocale.setText(values.get(DevicePolicyManager.EXTRA_PROVISIONING_LOCALE)); 187 mEditTimezone.setText(values.get(DevicePolicyManager.EXTRA_PROVISIONING_TIME_ZONE)); 188 mEditWifiSsid.setText(values.get(DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SSID)); 189 mEditWifiSecurityType.setText(values.get( 190 DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SECURITY_TYPE)); 191 mEditWifiPassword.setText(null); 192 } 193 } 194 195 @Override onLoaderReset(Loader<Map<String, String>> loader)196 public void onLoaderReset(Loader<Map<String, String>> loader) { 197 // Do nothing 198 } 199 200 } 201