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