Modify permissions for files in Things Manager
[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  * <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.mediator.ip;
24
25 import java.io.BufferedReader;
26 import java.io.FileReader;
27 import java.io.IOException;
28 import java.lang.reflect.Method;
29 import java.net.InetAddress;
30 import java.util.ArrayList;
31
32 import android.content.Context;
33 import android.net.wifi.WifiConfiguration;
34 import android.net.wifi.WifiManager;
35 import android.os.Handler;
36 import android.util.Log;
37
38 import org.iotivity.service.easysetup.mediator.EnrolleeInfo;
39 import org.iotivity.service.easysetup.mediator.EnrolleeOnBoardingInfo;
40 import org.iotivity.service.easysetup.mediator.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 synchronized 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) 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 SSID, security and channel details as part of WifiConfiguration
182      * @return {@code true} if the operation succeeds, {@code false} otherwise
183      */
184     public boolean setWifiApEnabled(WifiConfiguration wifiConfig,
185                                     boolean enabled) {
186         try {
187             appNotification.clear();
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   Interface called when the scan method finishes
256      * @param reachableTimeout Reachable Timeout in miliseconds
257      */
258     public void getClientList(IOnBoardingStatus finishListener, final int reachableTimeout) {
259         this.finishListener = finishListener;
260         Runnable runnable = new Runnable() {
261             public void run() {
262                 Log.i(TAG, "Scanning enrolling device in the network" );
263
264                 BufferedReader bufferedReader = null;
265
266                 try {
267                     // Note : This is a reference implementation for getting the list of Enrollee's
268                     // connected to the Soft AP.
269                     // There is no Android API for getting list of connected devices to the Soft AP.
270                     // The connected device information is fetched from Arp cache for Soft AP and
271                     // it is maintained in the file "/proc/net/arp"
272                     bufferedReader = new BufferedReader(new FileReader("/proc/net/arp"));
273                     String line;
274
275                     while ((line = bufferedReader.readLine()) != null) {
276                         //ARP entries are splitted using Regex for getting the IP and MAC Address
277                         // info
278                         String[] arpEntry = line.split(" +");
279
280                         if ((arpEntry != null) && (arpEntry.length >= 4)) {
281                             String ipAddr = arpEntry[0];
282                             String macAddr = arpEntry[3];
283
284
285                             // Checking if the string is matching MAC Address is matching the
286                             // standard MAC address format.
287                             // If the entry is not matching with MAC address format,
288                             // it will continue
289                             if (CheckIfValidMacAddress(macAddr)) {
290                                 boolean isReachable = InetAddress.getByName(
291                                         ipAddr).isReachable(
292                                         reachableTimeout);
293
294                                 Log.i("exec statement", ipAddr);
295                                 Log.i("Return Value", " " + isReachable);
296
297                                 // Checking if the app notification entries are available in the
298                                 // list for the current device
299                                 // API returns true is there is a notification to the application.
300                                 // API returns false if there is no entry or if device is
301                                 // already notified
302                                 if (CheckForDeviceEntryAndNotify(ipAddr, macAddr, isReachable)) {
303                                     break;
304                                 }
305                             }
306                         }
307                     }
308                 } catch (Exception e) {
309                     Log.e(this.getClass().toString(), e.toString());
310                 } finally {
311                     try {
312                         bufferedReader.close();
313                     } catch (IOException e) {
314                         Log.e(this.getClass().toString(), e.getMessage());
315                     }
316                 }
317             }
318         };
319
320         Thread mythread = new Thread(runnable);
321         mythread.start();
322     }
323
324     void NotifyApplication(final EnrolleeInfo result) {
325         // Get a handler that can be used to post to the main thread
326 /*
327         Handler mainHandler = new Handler(context.getMainLooper());
328         Runnable myRunnable = new Runnable() {
329             @Override
330             public void run() {
331                 finishListener.deviceOnBoardingStatus(result);
332             }
333         };
334         mainHandler.post(myRunnable);
335 */
336         Log.i(TAG, "Scanning is finished with result, IP : " +  result.getIpAddr() + "Notifying to Application");
337         finishListener.deviceOnBoardingStatus(result);
338
339     }
340 }