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