1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.test.util.wifistrengthscanner;
18 
19 import android.app.Activity;
20 import android.app.Instrumentation;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.net.wifi.ScanResult;
26 import android.net.wifi.WifiInfo;
27 import android.net.wifi.WifiManager;
28 import android.os.Bundle;
29 import android.os.Environment;
30 import android.os.ParcelFileDescriptor;
31 import android.util.Log;
32 
33 import java.io.FileInputStream;
34 import java.io.IOException;
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.util.concurrent.CountDownLatch;
38 import java.util.concurrent.TimeUnit;
39 
40 public class WifiStrengthScannerInstrumentation extends Instrumentation {
41     private static final String TAG = WifiStrengthScannerInstrumentation.class.getCanonicalName();
42     private final static String SD_CARD_PATH =
43             Environment.getExternalStorageDirectory().getAbsolutePath() + "/";
44     private final int NUMBER_OF_WIFI_LEVELS = 101;
45     private final int INVALID_RSSI = -127;
46     private Bundle mArguments;
47     private CountDownLatch mLatch;
48     private boolean scanReceived;
49 
50     @Override
onCreate(Bundle arguments)51     public void onCreate(Bundle arguments) {
52         super.onCreate(arguments);
53         mArguments = arguments;
54         start();
55     }
56 
57     @Override
onStart()58     public void onStart() {
59         super.onStart();
60         try {
61             mLatch = new CountDownLatch(1);
62             getContext().registerReceiver(new WifiScanReceiver(),
63                     new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
64             WifiManager wifiManager =
65                     (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
66             scanReceived = false;
67             wifiManager.startScan();
68             mLatch.await(10000, TimeUnit.MILLISECONDS);
69 
70             if (!scanReceived) {
71                 sendFailureStatus("no_scan_received");
72                 finish(Activity.RESULT_CANCELED, new Bundle());
73                 return;
74             }
75 
76             WifiInfo wifiInfo = wifiManager.getConnectionInfo();
77             Bundle bundle = new Bundle();
78             bundle.putString("suplicant_state", wifiInfo.getSupplicantState().name());
79 
80             String bssid = wifiInfo.getBSSID();
81             bundle.putString("bssid", bssid);
82             // zero counts as a level, so the max level is one less that the number of levels.
83             bundle.putInt("wifi_max_level", NUMBER_OF_WIFI_LEVELS - 1);
84 
85             bundle.putInt("wifi_info_wifi_level",
86                     WifiManager.calculateSignalLevel(wifiInfo.getRssi(), NUMBER_OF_WIFI_LEVELS));
87             bundle.putInt("wifi_info_rssi", wifiInfo.getRssi());
88             bundle.putInt("wifi_info_frequency", wifiInfo.getFrequency());
89 
90             ScanResult result = getScanResult(wifiManager, bssid);
91             if (result != null) {
92                 bundle.putInt("scan_result_wifi_level", wifiManager.calculateSignalLevel(result
93                         .level, NUMBER_OF_WIFI_LEVELS));
94                 bundle.putInt("scan_result_rssi", result.level);
95                 bundle.putInt("scan_result_frequency", result.frequency);
96             }
97 
98             int dumpsysRssi = getRssiFromDumpsys(bssid);
99             bundle.putInt("dumpsys_rssi", dumpsysRssi);
100             bundle.putInt("dumpsys_wifi_level",
101                     WifiManager.calculateSignalLevel(dumpsysRssi, NUMBER_OF_WIFI_LEVELS));
102             sendStatus(Activity.RESULT_OK, bundle);
103             finish(Activity.RESULT_OK, new Bundle());
104         } catch (IOException e) {
105             Log.e(TAG, Log.getStackTraceString(e));
106             sendFailureStatus("io_exception");
107             finish(Activity.RESULT_CANCELED, new Bundle());
108         } catch (InterruptedException e) {
109             Log.e(TAG, Log.getStackTraceString(e));
110             sendFailureStatus("interrupted_exception");
111             finish(Activity.RESULT_CANCELED, new Bundle());
112         }
113     }
114 
getScanResult(WifiManager wifiManager, String bssid)115     private ScanResult getScanResult(WifiManager wifiManager, String bssid) {
116         List<ScanResult> scanResults = wifiManager.getScanResults();
117         for (ScanResult scanResult : scanResults) {
118             if (scanResult.BSSID.equals(bssid)) {
119                 return scanResult;
120             }
121         }
122 
123         return null;
124     }
125 
sendFailureStatus(String update)126     private void sendFailureStatus(String update) {
127         Bundle result = new Bundle();
128         result.putString("wifi_strength_scanner_failure", update);
129         sendStatus(Activity.RESULT_CANCELED, result);
130     }
131 
getRssiFromDumpsys(String bssid)132     private Integer getRssiFromDumpsys(String bssid) throws IOException, InterruptedException {
133         List<String> dumpsysLines = getDumpsysWifiLastScanResults();
134 
135         for (int i = 2; i < dumpsysLines.size(); i++) {
136             String line = dumpsysLines.get(i);
137             if (line != null && line.contains(bssid)) {
138                 String[] tokens = line.trim().split("\\s\\s+");
139                 return Integer.parseInt(tokens[2]);
140             }
141         }
142 
143         return INVALID_RSSI;
144     }
145 
getDumpsysWifiLastScanResults()146     private List<String> getDumpsysWifiLastScanResults() throws IOException, InterruptedException {
147         String dumpsysWifi = executeCommand("dumpsys wifi");
148         String[] lines = dumpsysWifi.split("\n");
149         List<String> scanResults = new ArrayList<>();
150 
151         boolean scansStarted = false;
152         for (String line : lines) {
153             if (line.startsWith("Latest scan results:")) {
154                 scansStarted = true;
155             }
156 
157             if (scansStarted) {
158                 if ("".equals(line.trim())) {
159                     break;
160                 }
161 
162                 scanResults.add(line);
163             }
164         }
165 
166         return scanResults;
167     }
168 
executeCommand(String cmd)169     private String executeCommand(String cmd) throws IOException, InterruptedException {
170         StringBuilder result = new StringBuilder();
171         try {
172             ParcelFileDescriptor pfd = getUiAutomation().executeShellCommand(cmd);
173             byte[] buf = new byte[1024];
174             int bytesRead;
175             FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
176             while ((bytesRead = fis.read(buf)) != -1) {
177                 result.append(new String(buf, 0, bytesRead));
178             }
179             fis.close();
180         } catch (IOException e) {
181             throw new IOException(String.format("Fails to execute command: %s ", cmd), e);
182         }
183 
184         return result.toString();
185     }
186 
187     private class WifiScanReceiver extends BroadcastReceiver {
188         @Override
onReceive(Context context, Intent intent)189         public void onReceive(Context context, Intent intent) {
190             Log.d(TAG, "scan results received.");
191             scanReceived = true;
192             mLatch.countDown();
193         }
194     }
195 }
196