RttService.java revision f95649f33db0a328cb4c0bb5e10c7075e6c828f8
1package com.android.server.wifi;
2
3import android.content.BroadcastReceiver;
4import android.content.Context;
5import android.content.Intent;
6import android.content.IntentFilter;
7import android.net.wifi.RttManager;
8import android.net.wifi.WifiManager;
9import android.os.Bundle;
10import android.os.Handler;
11import android.os.HandlerThread;
12import android.os.Looper;
13import android.os.Message;
14import android.os.Messenger;
15import android.os.Parcel;
16import android.os.RemoteException;
17import android.util.Log;
18import android.net.wifi.IRttManager;
19import android.util.Slog;
20
21import com.android.internal.util.AsyncChannel;
22import com.android.internal.util.Protocol;
23import com.android.internal.util.StateMachine;
24import com.android.internal.util.State;
25import com.android.server.SystemService;
26
27import java.util.HashMap;
28import java.util.Iterator;
29import java.util.LinkedList;
30import java.util.Queue;
31
32class RttService extends SystemService {
33
34    public static final boolean DBG = true;
35
36    class RttServiceImpl extends IRttManager.Stub {
37
38        @Override
39        public Messenger getMessenger() {
40            return new Messenger(mClientHandler);
41        }
42
43        private class ClientHandler extends Handler {
44
45            ClientHandler(android.os.Looper looper) {
46                super(looper);
47            }
48
49            @Override
50            public void handleMessage(Message msg) {
51
52                if (DBG) Log.d(TAG, "ClientHandler got" + msg);
53
54                switch (msg.what) {
55
56                    case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
57                        if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
58                            AsyncChannel c = (AsyncChannel) msg.obj;
59                            if (DBG) Slog.d(TAG, "New client listening to asynchronous messages: " +
60                                    msg.replyTo);
61                            ClientInfo cInfo = new ClientInfo(c, msg.replyTo);
62                            mClients.put(msg.replyTo, cInfo);
63                        } else {
64                            Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
65                        }
66                        return;
67                    case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
68                        if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
69                            Slog.e(TAG, "Send failed, client connection lost");
70                        } else {
71                            if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
72                        }
73                        if (DBG) Slog.d(TAG, "closing client " + msg.replyTo);
74                        ClientInfo ci = mClients.remove(msg.replyTo);
75                        if (ci != null) ci.cleanup();
76                        return;
77                    case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
78                        AsyncChannel ac = new AsyncChannel();
79                        ac.connect(mContext, this, msg.replyTo);
80                        return;
81                }
82
83                ClientInfo ci = mClients.get(msg.replyTo);
84                if (ci == null) {
85                    Slog.e(TAG, "Could not find client info for message " + msg.replyTo);
86                    replyFailed(msg, RttManager.REASON_INVALID_LISTENER, "Could not find listener");
87                    return;
88                }
89
90                int validCommands[] = {
91                        RttManager.CMD_OP_START_RANGING,
92                        RttManager.CMD_OP_STOP_RANGING
93                        };
94
95                for(int cmd : validCommands) {
96                    if (cmd == msg.what) {
97                        mStateMachine.sendMessage(Message.obtain(msg));
98                        return;
99                    }
100                }
101
102                replyFailed(msg, RttManager.REASON_INVALID_REQUEST, "Invalid request");
103            }
104        }
105
106        private Context mContext;
107        private RttStateMachine mStateMachine;
108        private ClientHandler mClientHandler;
109
110        RttServiceImpl() { }
111
112        RttServiceImpl(Context context) {
113            mContext = context;
114        }
115
116        public void startService(Context context) {
117            mContext = context;
118
119            HandlerThread thread = new HandlerThread("WifiRttService");
120            thread.start();
121
122            mClientHandler = new ClientHandler(thread.getLooper());
123            mStateMachine = new RttStateMachine(thread.getLooper());
124
125            mContext.registerReceiver(
126                    new BroadcastReceiver() {
127                        @Override
128                        public void onReceive(Context context, Intent intent) {
129                            int state = intent.getIntExtra(
130                                    WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_DISABLED);
131                            if (DBG) Log.d(TAG, "SCAN_AVAILABLE : " + state);
132                            if (state == WifiManager.WIFI_STATE_ENABLED) {
133                                mStateMachine.sendMessage(CMD_DRIVER_LOADED);
134                            } else if (state == WifiManager.WIFI_STATE_DISABLED) {
135                                mStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
136                            }
137                        }
138                    }, new IntentFilter(WifiManager.WIFI_SCAN_AVAILABLE));
139
140            mStateMachine.start();
141        }
142
143        private class RttRequest {
144            Integer key;
145            ClientInfo ci;
146            RttManager.RttParams[] params;
147
148            @Override
149            public String toString() {
150                String str = getClass().getName() + "@" + Integer.toHexString(hashCode());
151                if(this.key != null) {
152                    return str + " key: " + this.key;
153                } else {
154                    return str + " key: " + " , null";
155                }
156            }
157        }
158
159        private class ClientInfo {
160            private final AsyncChannel mChannel;
161            private final Messenger mMessenger;
162            HashMap<Integer, RttRequest> mRequests = new HashMap<Integer,
163                    RttRequest>();
164
165            ClientInfo(AsyncChannel c, Messenger m) {
166                mChannel = c;
167                mMessenger = m;
168            }
169
170            boolean addRttRequest(int key, RttManager.ParcelableRttParams parcelableParams) {
171                if (parcelableParams == null) {
172                    return false;
173                }
174
175                RttManager.RttParams params[] = parcelableParams.mParams;
176
177                RttRequest request = new RttRequest();
178                request.key = key;
179                request.ci = this;
180                request.params = params;
181                mRequests.put(key, request);
182                mRequestQueue.add(request);
183                return true;
184            }
185
186            void removeRttRequest(int key) {
187                mRequests.remove(key);
188            }
189
190            void reportResult(RttRequest request, RttManager.RttResult[] results) {
191                RttManager.ParcelableRttResults parcelableResults =
192                        new RttManager.ParcelableRttResults(results);
193
194                mChannel.sendMessage(RttManager.CMD_OP_SUCCEEDED,
195                        0, request.key, parcelableResults);
196                mRequests.remove(request.key);
197            }
198
199            void reportFailed(RttRequest request, int reason, String description) {
200                reportFailed(request.key, reason, description);
201            }
202
203            void reportFailed(int key, int reason, String description) {
204                Bundle bundle = new Bundle();
205                bundle.putString(RttManager.DESCRIPTION_KEY, description);
206                mChannel.sendMessage(RttManager.CMD_OP_FAILED, key, reason, bundle);
207                mRequests.remove(key);
208            }
209
210            void reportAborted(int key) {
211                mChannel.sendMessage(RttManager.CMD_OP_ABORTED, 0, key);
212                //All Queued RTT request will be cleaned
213                cleanup();
214            }
215
216            void cleanup() {
217                mRequests.clear();
218                mRequestQueue.clear();
219            }
220        }
221
222        private Queue<RttRequest> mRequestQueue = new LinkedList<RttRequest>();
223        private HashMap<Messenger, ClientInfo> mClients = new HashMap<Messenger, ClientInfo>(4);
224
225        private static final int BASE = Protocol.BASE_WIFI_RTT_SERVICE;
226
227        private static final int CMD_DRIVER_LOADED                       = BASE + 0;
228        private static final int CMD_DRIVER_UNLOADED                     = BASE + 1;
229        private static final int CMD_ISSUE_NEXT_REQUEST                  = BASE + 2;
230        private static final int CMD_RTT_RESPONSE                        = BASE + 3;
231
232        class RttStateMachine extends StateMachine {
233
234            DefaultState mDefaultState = new DefaultState();
235            EnabledState mEnabledState = new EnabledState();
236            RequestPendingState mRequestPendingState = new RequestPendingState();
237
238            RttStateMachine(Looper looper) {
239                super("RttStateMachine", looper);
240
241                addState(mDefaultState);
242                addState(mEnabledState);
243                    addState(mRequestPendingState, mEnabledState);
244
245                setInitialState(mDefaultState);
246            }
247
248            class DefaultState extends State {
249                @Override
250                public boolean processMessage(Message msg) {
251                    if (DBG) Log.d(TAG, "DefaultState got" + msg);
252                    switch (msg.what) {
253                        case CMD_DRIVER_LOADED:
254                            transitionTo(mEnabledState);
255                            break;
256                        case CMD_ISSUE_NEXT_REQUEST:
257                            deferMessage(msg);
258                            break;
259                        case RttManager.CMD_OP_START_RANGING:
260                            replyFailed(msg, RttManager.REASON_NOT_AVAILABLE, "Try later");
261                            break;
262                        case RttManager.CMD_OP_STOP_RANGING:
263                            return HANDLED;
264                        default:
265                            return NOT_HANDLED;
266                    }
267                    return HANDLED;
268                }
269            }
270
271            class EnabledState extends State {
272                @Override
273                public boolean processMessage(Message msg) {
274                    if (DBG) Log.d(TAG, "EnabledState got" + msg);
275                    ClientInfo ci = mClients.get(msg.replyTo);
276
277                    switch (msg.what) {
278                        case CMD_DRIVER_UNLOADED:
279                            transitionTo(mDefaultState);
280                            break;
281                        case CMD_ISSUE_NEXT_REQUEST:
282                            deferMessage(msg);
283                            transitionTo(mRequestPendingState);
284                            break;
285                        case RttManager.CMD_OP_START_RANGING: {
286                                RttManager.ParcelableRttParams params =
287                                        (RttManager.ParcelableRttParams)msg.obj;
288                                if (params == null) {
289                                    replyFailed(msg,
290                                            RttManager.REASON_INVALID_REQUEST, "No params");
291                                } else if (ci.addRttRequest(msg.arg2, params) == false) {
292                                    replyFailed(msg,
293                                            RttManager.REASON_INVALID_REQUEST, "Unspecified");
294                                } else {
295                                    sendMessage(CMD_ISSUE_NEXT_REQUEST);
296                                }
297                            }
298                            break;
299                        case RttManager.CMD_OP_STOP_RANGING:
300                            for (Iterator<RttRequest> it = mRequestQueue.iterator();
301                                    it.hasNext(); ) {
302                                RttRequest request = it.next();
303                                if (request.key == msg.arg2) {
304                                    if (DBG) Log.d(TAG, "Cancelling not-yet-scheduled RTT");
305                                    mRequestQueue.remove(request);
306                                    request.ci.reportAborted(request.key);
307                                    break;
308                                }
309                            }
310                            break;
311                        default:
312                            return NOT_HANDLED;
313                    }
314                    return HANDLED;
315                }
316            }
317
318            class RequestPendingState extends State {
319                RttRequest mOutstandingRequest;
320                @Override
321                public boolean processMessage(Message msg) {
322                    if (DBG) Log.d(TAG, "RequestPendingState got" + msg);
323                    switch (msg.what) {
324                        case CMD_DRIVER_UNLOADED:
325                            if (mOutstandingRequest != null) {
326                                WifiNative.cancelRtt(mOutstandingRequest.params);
327                                if (DBG) Log.d(TAG, "abort key: " + mOutstandingRequest.key);
328                                mOutstandingRequest.ci.reportAborted(mOutstandingRequest.key);
329                                mOutstandingRequest = null;
330                            }
331                            transitionTo(mDefaultState);
332                            break;
333                        case CMD_ISSUE_NEXT_REQUEST:
334                            if (mOutstandingRequest == null) {
335                                mOutstandingRequest = issueNextRequest();
336                                if (mOutstandingRequest == null) {
337                                    transitionTo(mEnabledState);
338                                }
339                                if(mOutstandingRequest != null) {
340                                    if (DBG) Log.d(TAG, "new mOutstandingRequest.key is: " + mOutstandingRequest.key);
341                                } else {
342                                    if (DBG) Log.d(TAG, "CMD_ISSUE_NEXT_REQUEST: mOutstandingRequest =null ");
343                                }
344                            } else {
345                                /* just wait; we'll issue next request after
346                                 * current one is finished */
347                                 if (DBG) Log.d(TAG, "Current mOutstandingRequest.key is: " +
348                                         mOutstandingRequest.key);
349                                 if (DBG) Log.d(TAG, "Ignoring CMD_ISSUE_NEXT_REQUEST");
350                            }
351                            break;
352                        case CMD_RTT_RESPONSE:
353                            if (DBG) Log.d(TAG, "Received an RTT response from: " + msg.arg2);
354                            mOutstandingRequest.ci.reportResult(
355                                    mOutstandingRequest, (RttManager.RttResult[])msg.obj);
356                            mOutstandingRequest = null;
357                            sendMessage(CMD_ISSUE_NEXT_REQUEST);
358                            break;
359                        case RttManager.CMD_OP_STOP_RANGING:
360                            if (mOutstandingRequest != null
361                                    && msg.arg2 == mOutstandingRequest.key) {
362                                if (DBG) Log.d(TAG, "Cancelling ongoing RTT of: " + msg.arg2);
363                                WifiNative.cancelRtt(mOutstandingRequest.params);
364                                mOutstandingRequest.ci.reportAborted(mOutstandingRequest.key);
365                                mOutstandingRequest = null;
366                                sendMessage(CMD_ISSUE_NEXT_REQUEST);
367                            } else {
368                                /* Let EnabledState handle this */
369                                return NOT_HANDLED;
370                            }
371                            break;
372                        default:
373                            return NOT_HANDLED;
374                    }
375                    return HANDLED;
376                }
377            }
378        }
379
380        void replySucceeded(Message msg, Object obj) {
381            if (msg.replyTo != null) {
382                Message reply = Message.obtain();
383                reply.what = RttManager.CMD_OP_SUCCEEDED;
384                reply.arg2 = msg.arg2;
385                reply.obj = obj;
386                try {
387                    msg.replyTo.send(reply);
388                } catch (RemoteException e) {
389                    // There's not much we can do if reply can't be sent!
390                }
391            } else {
392                // locally generated message; doesn't need a reply!
393            }
394        }
395
396        void replyFailed(Message msg, int reason, String description) {
397            Message reply = Message.obtain();
398            reply.what = RttManager.CMD_OP_FAILED;
399            reply.arg1 = reason;
400            reply.arg2 = msg.arg2;
401
402            Bundle bundle = new Bundle();
403            bundle.putString(RttManager.DESCRIPTION_KEY, description);
404            reply.obj = bundle;
405
406            try {
407                msg.replyTo.send(reply);
408            } catch (RemoteException e) {
409                // There's not much we can do if reply can't be sent!
410            }
411        }
412
413        private WifiNative.RttEventHandler mEventHandler = new WifiNative.RttEventHandler() {
414            @Override
415            public void onRttResults(RttManager.RttResult[] result) {
416                mStateMachine.sendMessage(CMD_RTT_RESPONSE, result);
417            }
418        };
419
420        RttRequest issueNextRequest() {
421            RttRequest request = null;
422            while (mRequestQueue.isEmpty() == false) {
423                request = mRequestQueue.remove();
424                if(request !=  null) {
425                    if (WifiNative.requestRtt(request.params, mEventHandler)) {
426                        if (DBG) Log.d(TAG, "Issued next RTT request with key: " + request.key);
427                        return request;
428                    } else {
429                        Log.e(TAG, "Fail to issue key at native layer");
430                        request.ci.reportFailed(request,
431                                RttManager.REASON_UNSPECIFIED, "Failed to start");
432                    }
433                }
434            }
435
436            /* all requests exhausted */
437            if (DBG) Log.d(TAG, "No more requests left");
438            return null;
439        }
440        @Override
441        public RttManager.RttCapabilities getRttCapabilities() {
442            return WifiNative.getRttCapabilities();
443        }
444    }
445
446    private static final String TAG = "RttService";
447    RttServiceImpl mImpl;
448
449    public RttService(Context context) {
450        super(context);
451        Log.i(TAG, "Creating " + Context.WIFI_RTT_SERVICE);
452    }
453
454    @Override
455    public void onStart() {
456        mImpl = new RttServiceImpl(getContext());
457
458        Log.i(TAG, "Starting " + Context.WIFI_RTT_SERVICE);
459        publishBinderService(Context.WIFI_RTT_SERVICE, mImpl);
460    }
461
462    @Override
463    public void onBootPhase(int phase) {
464        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
465            Log.i(TAG, "Registering " + Context.WIFI_RTT_SERVICE);
466            if (mImpl == null) {
467                mImpl = new RttServiceImpl(getContext());
468            }
469            mImpl.startService(getContext());
470        }
471    }
472
473
474}
475