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