summaryrefslogtreecommitdiffstats
path: root/wifi/java/android/net/wifi/RttManager.java
blob: 50fd2609945abdc8c291696d80029bd4b90df4cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
package android.net.wifi;

import android.annotation.SystemApi;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;

import com.android.internal.util.AsyncChannel;

import java.util.concurrent.CountDownLatch;

/** @hide */
@SystemApi
public class RttManager {

    private static final boolean DBG = true;
    private static final String TAG = "RttManager";

    public static final int RTT_TYPE_UNSPECIFIED    = 0;
    public static final int RTT_TYPE_ONE_SIDED      = 1;
    public static final int RTT_TYPE_11_V           = 2;
    public static final int RTT_TYPE_11_MC          = 4;

    public static final int RTT_PEER_TYPE_UNSPECIFIED    = 0;
    public static final int RTT_PEER_TYPE_AP             = 1;
    public static final int RTT_PEER_TYPE_STA            = 2;       /* requires NAN */

    public static final int RTT_CHANNEL_WIDTH_20      = 0;
    public static final int RTT_CHANNEL_WIDTH_40      = 1;
    public static final int RTT_CHANNEL_WIDTH_80      = 2;
    public static final int RTT_CHANNEL_WIDTH_160     = 3;
    public static final int RTT_CHANNEL_WIDTH_80P80   = 4;
    public static final int RTT_CHANNEL_WIDTH_5       = 5;
    public static final int RTT_CHANNEL_WIDTH_10      = 6;
    public static final int RTT_CHANNEL_WIDTH_UNSPECIFIED = -1;

    public static final int RTT_STATUS_SUCCESS                  = 0;
    public static final int RTT_STATUS_FAILURE                  = 1;
    public static final int RTT_STATUS_FAIL_NO_RSP              = 2;
    public static final int RTT_STATUS_FAIL_REJECTED            = 3;
    public static final int RTT_STATUS_FAIL_NOT_SCHEDULED_YET   = 4;
    public static final int RTT_STATUS_FAIL_TM_TIMEOUT          = 5;
    public static final int RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL  = 6;
    public static final int RTT_STATUS_FAIL_NO_CAPABILITY       = 7;
    public static final int RTT_STATUS_ABORTED                  = 8;

    public static final int REASON_UNSPECIFIED              = -1;
    public static final int REASON_INVALID_LISTENER         = -2;
    public static final int REASON_INVALID_REQUEST          = -3;

    public class Capabilities {
        int supportedType;
        int supportedPeerType;
    }

    public Capabilities getCapabilities() {
        return new Capabilities();
    }

    /** specifies parameters for RTT request */
    public static class RttParams {

        /** type of device being ranged; one of RTT_PEER_TYPE_AP or RTT_PEER_TYPE_STA */
        public int deviceType;

        /** type of RTT being sought; one of RTT_TYPE_ONE_SIDED
         *  RTT_TYPE_11_V or RTT_TYPE_11_MC or RTT_TYPE_UNSPECIFIED */
        public int requestType;

        /** mac address of the device being ranged */
        public String bssid;

        /** channel frequency that the device is on; optional */
        public int frequency;

        /** optional channel width. wider channels result in better accuracy,
         *  but they take longer time, and even get aborted may times; use
         *  RTT_CHANNEL_WIDTH_UNSPECIFIED if not specifying */
        public int channelWidth;

        /** number of samples to be taken */
        public int num_samples;

        /** number of retries if a sample fails */
        public int num_retries;
    }

    /** specifies RTT results */
    public static class RttResult {
        /** mac address of the device being ranged */
        public String bssid;

        /** status of the request */
        public int status;

        /** timestamp of completion, in microsecond since boot */
        public long ts;

        /** average RSSI observed */
        public int rssi;

        /** RSSI spread (i.e. max - min) */
        public int rssi_spread;

        /** average transmit rate */
        public int tx_rate;

        /** average round trip time in nano second */
        public long rtt_ns;

        /** standard deviation observed in round trip time */
        public long rtt_sd_ns;

        /** spread (i.e. max - min) round trip time */
        public long rtt_spread_ns;

        /** average distance in centimeter, computed based on rtt_ns */
        public long distance_cm;

        /** standard deviation observed in distance */
        public long distance_sd_cm;

        /** spread (i.e. max - min) distance */
        public long distance_spread_cm;
    }

    public static interface RttListener {
        public void onSuccess(RttResult results[]);
        public void onFailure(int reason, String description);
        public void onAborted();
    }

    public void startRanging(RttParams params[], RttListener listener) {
        validateChannel();
        sAsyncChannel.sendMessage(CMD_OP_START_RANGING, 0, removeListener(listener), params);
    }

    public void stopRanging(RttListener listener) {
        validateChannel();
        sAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
    }

    /* private methods */
    public static final int CMD_OP_START_RANGING        = 0;
    public static final int CMD_OP_STOP_RANGING         = 1;
    public static final int CMD_OP_FAILED               = 2;
    public static final int CMD_OP_SUCCEEDED            = 3;
    public static final int CMD_OP_ABORTED              = 4;

    private Context mContext;
    private IRttManager mService;

    private static final int INVALID_KEY = 0;
    private static int sListenerKey = 1;

    private static final SparseArray sListenerMap = new SparseArray();
    private static final Object sListenerMapLock = new Object();

    private static AsyncChannel sAsyncChannel;
    private static CountDownLatch sConnected;

    private static final Object sThreadRefLock = new Object();
    private static int sThreadRefCount;
    private static HandlerThread sHandlerThread;

    /**
     * Create a new WifiScanner instance.
     * Applications will almost always want to use
     * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
     * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
     * @param context the application context
     * @param service the Binder interface
     * @hide
     */

    public RttManager(Context context, IRttManager service) {
        mContext = context;
        mService = service;
        init();
    }

    private void init() {
        synchronized (sThreadRefLock) {
            if (++sThreadRefCount == 1) {
                Messenger messenger = null;
                try {
                    messenger = mService.getMessenger();
                } catch (RemoteException e) {
                    /* do nothing */
                } catch (SecurityException e) {
                    /* do nothing */
                }

                if (messenger == null) {
                    sAsyncChannel = null;
                    return;
                }

                sHandlerThread = new HandlerThread("WifiScanner");
                sAsyncChannel = new AsyncChannel();
                sConnected = new CountDownLatch(1);

                sHandlerThread.start();
                Handler handler = new ServiceHandler(sHandlerThread.getLooper());
                sAsyncChannel.connect(mContext, handler, messenger);
                try {
                    sConnected.await();
                } catch (InterruptedException e) {
                    Log.e(TAG, "interrupted wait at init");
                }
            }
        }
    }

    private void validateChannel() {
        if (sAsyncChannel == null) throw new IllegalStateException(
                "No permission to access and change wifi or a bad initialization");
    }

    private static int putListener(Object listener) {
        if (listener == null) return INVALID_KEY;
        int key;
        synchronized (sListenerMapLock) {
            do {
                key = sListenerKey++;
            } while (key == INVALID_KEY);
            sListenerMap.put(key, listener);
        }
        return key;
    }

    private static Object getListener(int key) {
        if (key == INVALID_KEY) return null;
        synchronized (sListenerMapLock) {
            Object listener = sListenerMap.get(key);
            return listener;
        }
    }

    private static int getListenerKey(Object listener) {
        if (listener == null) return INVALID_KEY;
        synchronized (sListenerMapLock) {
            int index = sListenerMap.indexOfValue(listener);
            if (index == -1) {
                return INVALID_KEY;
            } else {
                return sListenerMap.keyAt(index);
            }
        }
    }

    private static Object removeListener(int key) {
        if (key == INVALID_KEY) return null;
        synchronized (sListenerMapLock) {
            Object listener = sListenerMap.get(key);
            sListenerMap.remove(key);
            return listener;
        }
    }

    private static int removeListener(Object listener) {
        int key = getListenerKey(listener);
        if (key == INVALID_KEY) return key;
        synchronized (sListenerMapLock) {
            sListenerMap.remove(key);
            return key;
        }
    }

    private static class ServiceHandler extends Handler {
        ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
                        sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
                    } else {
                        Log.e(TAG, "Failed to set up channel connection");
                        // This will cause all further async API calls on the WifiManager
                        // to fail and throw an exception
                        sAsyncChannel = null;
                    }
                    sConnected.countDown();
                    return;
                case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
                    return;
                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
                    Log.e(TAG, "Channel connection lost");
                    // This will cause all further async API calls on the WifiManager
                    // to fail and throw an exception
                    sAsyncChannel = null;
                    getLooper().quit();
                    return;
            }

            Object listener = getListener(msg.arg2);
            if (listener == null) {
                if (DBG) Log.d(TAG, "invalid listener key = " + msg.arg2);
                return;
            } else {
                if (DBG) Log.d(TAG, "listener key = " + msg.arg2);
            }

            switch (msg.what) {
                /* ActionListeners grouped together */
                case CMD_OP_SUCCEEDED :
                    ((RttListener) listener).onSuccess((RttResult[])msg.obj);
                    break;
                case CMD_OP_FAILED :
                    ((RttListener) listener).onFailure(msg.arg1, (String)msg.obj);
                    removeListener(msg.arg2);
                    break;
                case CMD_OP_ABORTED :
                    ((RttListener) listener).onAborted();
                    removeListener(msg.arg2);
                    break;
                default:
                    if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
                    return;
            }
        }
    }

}