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.content.Context;
22*90c8c64dSAndroid Build Coastguard Worker import android.net.wifi.WifiInfo;
23*90c8c64dSAndroid Build Coastguard Worker import android.net.wifi.WifiManager;
24*90c8c64dSAndroid Build Coastguard Worker import android.os.Environment;
25*90c8c64dSAndroid Build Coastguard Worker import android.support.v4.content.AsyncTaskLoader;
26*90c8c64dSAndroid Build Coastguard Worker 
27*90c8c64dSAndroid Build Coastguard Worker import com.example.android.common.logger.Log;
28*90c8c64dSAndroid Build Coastguard Worker 
29*90c8c64dSAndroid Build Coastguard Worker import java.io.BufferedReader;
30*90c8c64dSAndroid Build Coastguard Worker import java.io.File;
31*90c8c64dSAndroid Build Coastguard Worker import java.io.FileReader;
32*90c8c64dSAndroid Build Coastguard Worker import java.io.IOException;
33*90c8c64dSAndroid Build Coastguard Worker import java.io.StringWriter;
34*90c8c64dSAndroid Build Coastguard Worker import java.util.HashMap;
35*90c8c64dSAndroid Build Coastguard Worker import java.util.HashSet;
36*90c8c64dSAndroid Build Coastguard Worker import java.util.Map;
37*90c8c64dSAndroid Build Coastguard Worker import java.util.Properties;
38*90c8c64dSAndroid Build Coastguard Worker import java.util.Set;
39*90c8c64dSAndroid Build Coastguard Worker import java.util.TimeZone;
40*90c8c64dSAndroid Build Coastguard Worker 
41*90c8c64dSAndroid Build Coastguard Worker /**
42*90c8c64dSAndroid Build Coastguard Worker  * Loads default values for NFC provisioning.
43*90c8c64dSAndroid Build Coastguard Worker  * <p/>
44*90c8c64dSAndroid Build Coastguard Worker  * This loader first tries to load values from a config file in SD card. Then it fills in missing
45*90c8c64dSAndroid Build Coastguard Worker  * values using constants and settings on the programming device.
46*90c8c64dSAndroid Build Coastguard Worker  */
47*90c8c64dSAndroid Build Coastguard Worker public class ProvisioningValuesLoader extends AsyncTaskLoader<Map<String, String>> {
48*90c8c64dSAndroid Build Coastguard Worker 
49*90c8c64dSAndroid Build Coastguard Worker     private static final String FILENAME = "nfcprovisioning.txt";
50*90c8c64dSAndroid Build Coastguard Worker     private static final String TAG = "LoadProvisioningValuesTask";
51*90c8c64dSAndroid Build Coastguard Worker 
52*90c8c64dSAndroid Build Coastguard Worker     private Map<String, String> mValues;
53*90c8c64dSAndroid Build Coastguard Worker 
ProvisioningValuesLoader(Context context)54*90c8c64dSAndroid Build Coastguard Worker     public ProvisioningValuesLoader(Context context) {
55*90c8c64dSAndroid Build Coastguard Worker         super(context);
56*90c8c64dSAndroid Build Coastguard Worker     }
57*90c8c64dSAndroid Build Coastguard Worker 
58*90c8c64dSAndroid Build Coastguard Worker     @Override
loadInBackground()59*90c8c64dSAndroid Build Coastguard Worker     public Map<String, String> loadInBackground() {
60*90c8c64dSAndroid Build Coastguard Worker         HashMap<String, String> values = new HashMap<>();
61*90c8c64dSAndroid Build Coastguard Worker         loadFromDisk(values);
62*90c8c64dSAndroid Build Coastguard Worker         gatherAdminExtras(values);
63*90c8c64dSAndroid Build Coastguard Worker         loadSystemValues(values);
64*90c8c64dSAndroid Build Coastguard Worker         return values;
65*90c8c64dSAndroid Build Coastguard Worker     }
66*90c8c64dSAndroid Build Coastguard Worker 
67*90c8c64dSAndroid Build Coastguard Worker     @Override
deliverResult(Map<String, String> values)68*90c8c64dSAndroid Build Coastguard Worker     public void deliverResult(Map<String, String> values) {
69*90c8c64dSAndroid Build Coastguard Worker         if (isReset()) {
70*90c8c64dSAndroid Build Coastguard Worker             return;
71*90c8c64dSAndroid Build Coastguard Worker         }
72*90c8c64dSAndroid Build Coastguard Worker         mValues = values;
73*90c8c64dSAndroid Build Coastguard Worker         super.deliverResult(values);
74*90c8c64dSAndroid Build Coastguard Worker     }
75*90c8c64dSAndroid Build Coastguard Worker 
76*90c8c64dSAndroid Build Coastguard Worker     @Override
onStartLoading()77*90c8c64dSAndroid Build Coastguard Worker     protected void onStartLoading() {
78*90c8c64dSAndroid Build Coastguard Worker         if (mValues != null) {
79*90c8c64dSAndroid Build Coastguard Worker             deliverResult(mValues);
80*90c8c64dSAndroid Build Coastguard Worker         }
81*90c8c64dSAndroid Build Coastguard Worker         if (takeContentChanged() || mValues == null) {
82*90c8c64dSAndroid Build Coastguard Worker             forceLoad();
83*90c8c64dSAndroid Build Coastguard Worker         }
84*90c8c64dSAndroid Build Coastguard Worker     }
85*90c8c64dSAndroid Build Coastguard Worker 
86*90c8c64dSAndroid Build Coastguard Worker     @Override
onStopLoading()87*90c8c64dSAndroid Build Coastguard Worker     protected void onStopLoading() {
88*90c8c64dSAndroid Build Coastguard Worker         cancelLoad();
89*90c8c64dSAndroid Build Coastguard Worker     }
90*90c8c64dSAndroid Build Coastguard Worker 
91*90c8c64dSAndroid Build Coastguard Worker     @Override
onReset()92*90c8c64dSAndroid Build Coastguard Worker     protected void onReset() {
93*90c8c64dSAndroid Build Coastguard Worker         super.onReset();
94*90c8c64dSAndroid Build Coastguard Worker         onStopLoading();
95*90c8c64dSAndroid Build Coastguard Worker         mValues = null;
96*90c8c64dSAndroid Build Coastguard Worker     }
97*90c8c64dSAndroid Build Coastguard Worker 
loadFromDisk(HashMap<String, String> values)98*90c8c64dSAndroid Build Coastguard Worker     private void loadFromDisk(HashMap<String, String> values) {
99*90c8c64dSAndroid Build Coastguard Worker         File directory = Environment.getExternalStorageDirectory();
100*90c8c64dSAndroid Build Coastguard Worker         File file = new File(directory, FILENAME);
101*90c8c64dSAndroid Build Coastguard Worker         if (!file.exists()) {
102*90c8c64dSAndroid Build Coastguard Worker             return;
103*90c8c64dSAndroid Build Coastguard Worker         }
104*90c8c64dSAndroid Build Coastguard Worker         Log.d(TAG, "Loading the config file...");
105*90c8c64dSAndroid Build Coastguard Worker         try {
106*90c8c64dSAndroid Build Coastguard Worker             loadFromFile(values, file);
107*90c8c64dSAndroid Build Coastguard Worker         } catch (IOException e) {
108*90c8c64dSAndroid Build Coastguard Worker             e.printStackTrace();
109*90c8c64dSAndroid Build Coastguard Worker             Log.e(TAG, "Error loading data from " + file, e);
110*90c8c64dSAndroid Build Coastguard Worker         }
111*90c8c64dSAndroid Build Coastguard Worker     }
112*90c8c64dSAndroid Build Coastguard Worker 
loadFromFile(HashMap<String, String> values, File file)113*90c8c64dSAndroid Build Coastguard Worker     private void loadFromFile(HashMap<String, String> values, File file) throws IOException {
114*90c8c64dSAndroid Build Coastguard Worker         BufferedReader reader = null;
115*90c8c64dSAndroid Build Coastguard Worker         try {
116*90c8c64dSAndroid Build Coastguard Worker             reader = new BufferedReader(new FileReader(file));
117*90c8c64dSAndroid Build Coastguard Worker             String line;
118*90c8c64dSAndroid Build Coastguard Worker             while (null != (line = reader.readLine())) {
119*90c8c64dSAndroid Build Coastguard Worker                 if (line.startsWith("#")) {
120*90c8c64dSAndroid Build Coastguard Worker                     continue;
121*90c8c64dSAndroid Build Coastguard Worker                 }
122*90c8c64dSAndroid Build Coastguard Worker                 int position = line.indexOf("=");
123*90c8c64dSAndroid Build Coastguard Worker                 if (position < 0) { // Not found
124*90c8c64dSAndroid Build Coastguard Worker                     continue;
125*90c8c64dSAndroid Build Coastguard Worker                 }
126*90c8c64dSAndroid Build Coastguard Worker                 String key = line.substring(0, position);
127*90c8c64dSAndroid Build Coastguard Worker                 String value = line.substring(position + 1);
128*90c8c64dSAndroid Build Coastguard Worker                 values.put(key, value);
129*90c8c64dSAndroid Build Coastguard Worker                 Log.d(TAG, key + "=" + value);
130*90c8c64dSAndroid Build Coastguard Worker             }
131*90c8c64dSAndroid Build Coastguard Worker         } finally {
132*90c8c64dSAndroid Build Coastguard Worker             if (reader != null) {
133*90c8c64dSAndroid Build Coastguard Worker                 reader.close();
134*90c8c64dSAndroid Build Coastguard Worker             }
135*90c8c64dSAndroid Build Coastguard Worker         }
136*90c8c64dSAndroid Build Coastguard Worker     }
137*90c8c64dSAndroid Build Coastguard Worker 
gatherAdminExtras(HashMap<String, String> values)138*90c8c64dSAndroid Build Coastguard Worker     private void gatherAdminExtras(HashMap<String, String> values) {
139*90c8c64dSAndroid Build Coastguard Worker         HashMap<String, String> newMap = new HashMap<String, String>();
140*90c8c64dSAndroid Build Coastguard Worker         Properties props = new Properties();
141*90c8c64dSAndroid Build Coastguard Worker         Set<String>keys = new HashSet(values.keySet());
142*90c8c64dSAndroid Build Coastguard Worker         for (String key : keys) {
143*90c8c64dSAndroid Build Coastguard Worker             if (key.startsWith("android.app.extra")) {
144*90c8c64dSAndroid Build Coastguard Worker                 continue;
145*90c8c64dSAndroid Build Coastguard Worker             }
146*90c8c64dSAndroid Build Coastguard Worker             props.put(key, values.get(key));
147*90c8c64dSAndroid Build Coastguard Worker             values.remove(key);
148*90c8c64dSAndroid Build Coastguard Worker         }
149*90c8c64dSAndroid Build Coastguard Worker         StringWriter sw = new StringWriter();
150*90c8c64dSAndroid Build Coastguard Worker         try{
151*90c8c64dSAndroid Build Coastguard Worker             props.store(sw, "admin extras bundle");
152*90c8c64dSAndroid Build Coastguard Worker             values.put(DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE,
153*90c8c64dSAndroid Build Coastguard Worker                     sw.toString());
154*90c8c64dSAndroid Build Coastguard Worker             Log.d(TAG, "Admin extras bundle=" + values.get(
155*90c8c64dSAndroid Build Coastguard Worker                     DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE));
156*90c8c64dSAndroid Build Coastguard Worker         } catch (IOException e) {
157*90c8c64dSAndroid Build Coastguard Worker             Log.e(TAG, "Unable to build admin extras bundle");
158*90c8c64dSAndroid Build Coastguard Worker         }
159*90c8c64dSAndroid Build Coastguard Worker     }
160*90c8c64dSAndroid Build Coastguard Worker 
loadSystemValues(HashMap<String, String> values)161*90c8c64dSAndroid Build Coastguard Worker     private void loadSystemValues(HashMap<String, String> values) {
162*90c8c64dSAndroid Build Coastguard Worker         Context context = getContext();
163*90c8c64dSAndroid Build Coastguard Worker         putIfMissing(values, DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,
164*90c8c64dSAndroid Build Coastguard Worker                 "com.example.android.deviceowner");
165*90c8c64dSAndroid Build Coastguard Worker         putIfMissing(values, DevicePolicyManager.EXTRA_PROVISIONING_LOCALE,
166*90c8c64dSAndroid Build Coastguard Worker                 context.getResources().getConfiguration().locale.toString());
167*90c8c64dSAndroid Build Coastguard Worker         putIfMissing(values, DevicePolicyManager.EXTRA_PROVISIONING_TIME_ZONE,
168*90c8c64dSAndroid Build Coastguard Worker                 TimeZone.getDefault().getID());
169*90c8c64dSAndroid Build Coastguard Worker         if (!values.containsKey(DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SSID)) {
170*90c8c64dSAndroid Build Coastguard Worker             WifiManager wifiManager = (WifiManager) context
171*90c8c64dSAndroid Build Coastguard Worker                     .getSystemService(Activity.WIFI_SERVICE);
172*90c8c64dSAndroid Build Coastguard Worker             WifiInfo info = wifiManager.getConnectionInfo();
173*90c8c64dSAndroid Build Coastguard Worker             values.put(DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SSID, trimSsid(info.getSSID()));
174*90c8c64dSAndroid Build Coastguard Worker         }
175*90c8c64dSAndroid Build Coastguard Worker     }
176*90c8c64dSAndroid Build Coastguard Worker 
177*90c8c64dSAndroid Build Coastguard Worker     /**
178*90c8c64dSAndroid Build Coastguard Worker      * {@link WifiInfo#getSSID} returns the WiFi SSID surrounded by double quotation marks. This
179*90c8c64dSAndroid Build Coastguard Worker      * method removes them if wifiSsid contains them.
180*90c8c64dSAndroid Build Coastguard Worker      */
trimSsid(String wifiSsid)181*90c8c64dSAndroid Build Coastguard Worker     private static String trimSsid(String wifiSsid) {
182*90c8c64dSAndroid Build Coastguard Worker         int head = wifiSsid.startsWith("\"") ? 1 : 0;
183*90c8c64dSAndroid Build Coastguard Worker         int tail = wifiSsid.endsWith("\"") ? 1 : 0;
184*90c8c64dSAndroid Build Coastguard Worker         return wifiSsid.substring(head, wifiSsid.length() - tail);
185*90c8c64dSAndroid Build Coastguard Worker     }
186*90c8c64dSAndroid Build Coastguard Worker 
putIfMissing(HashMap<Key, Value> map, Key key, Value value)187*90c8c64dSAndroid Build Coastguard Worker     private static <Key, Value> void putIfMissing(HashMap<Key, Value> map, Key key, Value value) {
188*90c8c64dSAndroid Build Coastguard Worker         if (!map.containsKey(key)) {
189*90c8c64dSAndroid Build Coastguard Worker             map.put(key, value);
190*90c8c64dSAndroid Build Coastguard Worker         }
191*90c8c64dSAndroid Build Coastguard Worker     }
192*90c8c64dSAndroid Build Coastguard Worker 
193*90c8c64dSAndroid Build Coastguard Worker }
194