Refactoring of EasySetup Android implementation
[platform/upstream/iotivity.git] / service / easy-setup / sdk / mediator / android / EasySetupCore / src / main / java / org / iotivity / service / easysetup / mediator / ip / WiFiSoftAPManager.java
1 /******************************************************************
2  *
3  * Copyright 2015 Samsung Electronics All Rights Reserved.
4  *
5  *
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  ******************************************************************/
20 package org.iotivity.service.easysetup.mediator.ip;
21
22 import java.io.BufferedReader;
23 import java.io.FileReader;
24 import java.io.IOException;
25 import java.lang.reflect.Method;
26 import java.net.InetAddress;
27 import java.util.ArrayList;
28
29 import android.content.Context;
30 import android.net.wifi.WifiConfiguration;
31 import android.net.wifi.WifiManager;
32 import android.os.Handler;
33 import android.util.Log;
34
35 import org.iotivity.service.easysetup.mediator.EnrolleeInfo;
36 import org.iotivity.service.easysetup.mediator.EnrolleeOnBoardingInfo;
37 import org.iotivity.service.easysetup.mediator.IOnBoardingStatus;
38
39 /**
40  * WiFiSoftAPManager provides wrapper methods for accessing Android Soft Access Point functionality.
41  * This is a convenient class, providing Android "WiFiManager" kind of equivalent class for Soft AP.
42  * <p>
43  * Note: Android doesn't provide public APIs for Soft Access Point feature access.
44  * This class provides only reference implementation to use the Soft AP and it uses Java reflection
45  * for access Soft Access point features.
46  * </p>
47  */
48 public class WiFiSoftAPManager {
49     private final WifiManager                mWifiManager;
50     private Context                          context;
51     static ArrayList<EnrolleeOnBoardingInfo> appNotification =
52                                                     new ArrayList<EnrolleeOnBoardingInfo>();
53     IOnBoardingStatus finishListener  = null;
54
55     public enum WIFI_AP_STATE {
56         WIFI_AP_STATE_DISABLING,
57         WIFI_AP_STATE_DISABLED,
58         WIFI_AP_STATE_ENABLING,
59         WIFI_AP_STATE_ENABLED,
60         WIFI_AP_STATE_FAILED
61     }
62
63     public WiFiSoftAPManager(Context context) {
64         this.context = context;
65         mWifiManager = (WifiManager) this.context
66                 .getSystemService(Context.WIFI_SERVICE);
67     }
68
69     /*
70     * Utility API to check the validity of the MAC Address read from the ARP cache
71     */
72     private boolean CheckIfValidMacAddress(String macAddr)
73     {
74         if(macAddr.matches("..:..:..:..:..:.."))
75         {
76             return true;
77         }
78         else {
79             return false;
80         }
81     }
82
83     /*
84     * The API is used for checking the device entry in the list maintained for the device
85     * notifications.
86     * If device entry is not found in the list, app is notified.
87     * If the device entry is found in the device, as application is already notified it will
88     * continue
89     */
90     private synchronized boolean CheckForDeviceEntryAndNotify(String ipAddr,
91                                                  String macAddr, boolean isReachable)
92     {
93         final EnrolleeInfo result = new EnrolleeInfo();
94         boolean deviceAddedToList = false;
95
96         if (appNotification.size() > 0) {
97             for (EnrolleeOnBoardingInfo ipDeviceOnBoardingNotification : appNotification) {
98                 EnrolleeOnBoardingInfo ipEnrolleeDevice = (EnrolleeOnBoardingInfo)ipDeviceOnBoardingNotification;
99                 boolean macAddressComparison = ipEnrolleeDevice.getHWAddr().equalsIgnoreCase(
100                                 macAddr) ? true : false;
101
102                 if (macAddressComparison) {
103                     deviceAddedToList = true;
104
105                     if (ipDeviceOnBoardingNotification
106                             .isAdditionNotified()
107                             && isReachable) {
108                         continue;
109                     } else if (ipDeviceOnBoardingNotification
110                             .isRemovalNotified()
111                             && !isReachable) {
112                         continue;
113                     } else {
114                         result.setIpAddr(ipAddr);
115                         result.setHWAddr(macAddr);
116                         result.setReachable(isReachable);
117
118                         appNotification
119                                 .remove(ipDeviceOnBoardingNotification);
120                         if (isReachable) {
121                             appNotification
122                                     .add(new EnrolleeOnBoardingInfo(ipAddr, macAddr, "", isReachable,
123                                             false, true));
124                         } else {
125                             appNotification
126                                     .add(new EnrolleeOnBoardingInfo(ipAddr, macAddr, "", isReachable,
127                                             true, false));
128                         }
129
130                         NotifyApplication(result);
131                         return true;
132                     }
133                 }
134             }
135             if (!deviceAddedToList) {
136                 if (isReachable) {
137                     appNotification
138                             .add(new EnrolleeOnBoardingInfo(ipAddr, macAddr, "", isReachable, false,
139                                     true));
140                 } else {
141                     appNotification
142                             .add(new EnrolleeOnBoardingInfo(ipAddr, macAddr, "", isReachable, true,
143                                     false));
144                 }
145
146                 result.setIpAddr(ipAddr);
147                 result.setHWAddr(macAddr);
148                 result.setReachable(isReachable);
149
150                 NotifyApplication(result);
151                 return true;
152             }
153         } else {
154             if (isReachable) {
155                 appNotification
156                         .add(new EnrolleeOnBoardingInfo(ipAddr, macAddr, "", isReachable, false,
157                                 true));
158             } else {
159                 appNotification
160                         .add(new EnrolleeOnBoardingInfo(ipAddr, macAddr, "", isReachable, true,
161                                 false));
162             }
163
164             result.setIpAddr(ipAddr);
165             result.setHWAddr(macAddr);
166             result.setReachable(isReachable);
167
168             NotifyApplication(result);
169             return true;
170         }
171         return false;
172     }
173
174     /**
175      * Start WiFi Soft AccessPoint mode with the specified configuration.
176      * If the Soft AP is already running, this API call will update the new configuration.
177      * <p>
178      * Note: Starting Wi-Fi Soft Access Point will disable the Wi-Fi normal operation.
179      * </p>
180      *
181      * @param wifiConfig
182      *            SSID, security and channel details as part of WifiConfiguration
183      * @return {@code true} if the operation succeeds, {@code false} otherwise
184      */
185     public boolean setWifiApEnabled(WifiConfiguration wifiConfig,
186             boolean enabled) {
187         try {
188             // Stopping Wi-Fi mode
189             if (enabled) {
190                 mWifiManager.setWifiEnabled(false);
191             }
192
193             Method method = mWifiManager.getClass().getMethod(
194                     "setWifiApEnabled", WifiConfiguration.class, boolean.class);
195             return (Boolean) method.invoke(mWifiManager, wifiConfig, enabled);
196         } catch (Exception e) {
197             Log.e(this.getClass().toString(), "", e);
198             return false;
199         }
200     }
201
202     /**
203      * Fetch the current state of the Wi-Fi Soft AP
204      *
205      * @return {@link WIFI_AP_STATE}
206      */
207     public WIFI_AP_STATE getWifiApState() {
208         try {
209             Method method = mWifiManager.getClass().getMethod("getWifiApState");
210
211             int currentWiFiState = ((Integer) method.invoke(mWifiManager));
212
213             return WIFI_AP_STATE.class.getEnumConstants()[currentWiFiState];
214         } catch (Exception e) {
215             Log.e(this.getClass().toString(), "", e);
216             return WIFI_AP_STATE.WIFI_AP_STATE_FAILED;
217         }
218     }
219
220     /**
221      * Fetch the current Wi-Fi AP Configuration.
222      *
223      * @return AP details in {@link WifiConfiguration}
224      */
225     public WifiConfiguration getWifiApConfiguration() {
226         try {
227             Method method = mWifiManager.getClass().getMethod(
228                     "getWifiApConfiguration");
229             return (WifiConfiguration) method.invoke(mWifiManager);
230         } catch (Exception e) {
231             Log.e(this.getClass().toString(), "", e);
232             return null;
233         }
234     }
235
236     /**
237      * Set/Update the Wi-Fi AP Configuration.
238      *
239      * @return {@code true} if the operation succeeds, {@code false} otherwise
240      */
241     public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
242         try {
243             Method method = mWifiManager.getClass().getMethod(
244                     "setWifiApConfiguration", WifiConfiguration.class);
245             return (Boolean) method.invoke(mWifiManager, wifiConfig);
246         } catch (Exception e) {
247             Log.e(this.getClass().toString(), "", e);
248             return false;
249         }
250     }
251
252     /**
253      * Gets a list of the Soft AP clients connected to the Wi-Fi Soft Access point
254      *
255      * @param finishListener
256      *            Interface called when the scan method finishes
257      * @param reachableTimeout
258      *            Reachable Timeout in miliseconds
259      */
260     public void getClientList(IOnBoardingStatus finishListener, final int reachableTimeout) {
261         this.finishListener = finishListener;
262         Runnable runnable = new Runnable() {
263             public void run() {
264
265                 BufferedReader bufferedReader = null;
266
267                 try {
268                     // Note : This is a reference implementation for getting the list of Enrollee's
269                     // connected to the Soft AP.
270                     // There is no Android API for getting list of connected devices to the Soft AP.
271                     // The connected device information is fetched from Arp cache for Soft AP and
272                     // it is maintained in the file "/proc/net/arp"
273                     bufferedReader = new BufferedReader(new FileReader("/proc/net/arp"));
274                     String line;
275
276                     while ((line = bufferedReader.readLine()) != null) {
277                         //ARP entries are splitted using Regex for getting the IP and MAC Address
278                         // info
279                         String[] arpEntry = line.split(" +");
280
281                         if ((arpEntry != null) && (arpEntry.length >= 4)) {
282                             String ipAddr = arpEntry[0];
283                             String macAddr = arpEntry[3];
284
285
286                             // Checking if the string is matching MAC Address is matching the
287                             // standard MAC address format.
288                             // If the entry is not matching with MAC address format,
289                             // it will continue
290                             if (CheckIfValidMacAddress(macAddr)) {
291                                 boolean isReachable = InetAddress.getByName(
292                                         ipAddr).isReachable(
293                                         reachableTimeout);
294
295                                 Log.i("exec statement", ipAddr);
296                                 Log.i("Return Value", " " + isReachable);
297
298                                 // Checking if the app notification entries are available in the
299                                 // list for the current device
300                                 // API returns true is there is a notification to the application.
301                                 // API returns false if there is no entry or if device is
302                                 // already notified
303                                 if(CheckForDeviceEntryAndNotify(ipAddr, macAddr, isReachable))
304                                 {
305                                     break;
306                                 }
307                             }
308                         }
309                     }
310                 } catch (Exception e) {
311                     Log.e(this.getClass().toString(), e.toString());
312                 } finally {
313                     try {
314                         bufferedReader.close();
315                     } catch (IOException e) {
316                         Log.e(this.getClass().toString(), e.getMessage());
317                     }
318                 }
319             }
320         };
321
322         Thread mythread = new Thread(runnable);
323         mythread.start();
324     }
325
326     void NotifyApplication(final EnrolleeInfo result) {
327         // Get a handler that can be used to post to the main thread
328         Handler mainHandler = new Handler(context.getMainLooper());
329         Runnable myRunnable = new Runnable() {
330             @Override
331             public void run() {
332                 finishListener.deviceOnBoardingStatus(result);
333             }
334         };
335         mainHandler.post(myRunnable);
336     }
337 }