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
149        private class ClientInfo {
150            private final AsyncChannel mChannel;
151            private final Messenger mMessenger;
152            HashMap<Integer, RttRequest> mRequests = new HashMap<Integer,
153                    RttRequest>();
154
155            ClientInfo(AsyncChannel c, Messenger m) {
156                mChannel = c;
157                mMessenger = m;
158            }
159
160            boolean addRttRequest(int key, RttManager.ParcelableRttParams parcelableParams) {
161                if (parcelableParams == null) {
162                    return false;
163                }
164
165                RttManager.RttParams params[] = parcelableParams.mParams;
166
167                RttRequest request = new RttRequest();
168                request.key = key;
169                request.ci = this;
170                request.params = params;
171                mRequests.put(key, request);
172                mRequestQueue.add(request);
173                return true;
174            }
175
176            void removeRttRequest(int key) {
177                mRequests.remove(key);
178            }
179
180            void reportResult(RttRequest request, RttManager.RttResult[] results) {
181                RttManager.ParcelableRttResults parcelableResults =
182                        new RttManager.ParcelableRttResults(results);
183
184                mChannel.sendMessage(RttManager.CMD_OP_SUCCEEDED,
185                        0, request.key, parcelableResults);
186                mRequests.remove(request.key);
187            }
188
189            void reportFailed(RttRequest request, int reason, String description) {
190                reportFailed(request.key, reason, description);
191            }
192
193            void reportFailed(int key, int reason, String description) {
194                Bundle bundle = new Bundle();
195                bundle.putString(RttManager.DESCRIPTION_KEY, description);
196                mChannel.sendMessage(RttManager.CMD_OP_FAILED, key, reason, bundle);
197                mRequests.remove(key);
198            }
199
200            void reportAborted(int key) {
201                mChannel.sendMessage(RttManager.CMD_OP_ABORTED, key);
202                mRequests.remove(key);
203            }
204
205            void cleanup() {
206                mRequests.clear();
207            }
208        }
209
210        private Queue<RttRequest> mRequestQueue = new LinkedList<RttRequest>();
211        private HashMap<Messenger, ClientInfo> mClients = new HashMap<Messenger, ClientInfo>(4);
212
213        private static final int BASE = Protocol.BASE_WIFI_RTT_SERVICE;
214
215        private static final int CMD_DRIVER_LOADED                       = BASE + 0;
216        private static final int CMD_DRIVER_UNLOADED                     = BASE + 1;
217        private static final int CMD_ISSUE_NEXT_REQUEST                  = BASE + 2;
218        private static final int CMD_RTT_RESPONSE                        = BASE + 3;
219
220        class RttStateMachine extends StateMachine {
221
222            DefaultState mDefaultState = new DefaultState();
223            EnabledState mEnabledState = new EnabledState();
224            RequestPendingState mRequestPendingState = new RequestPendingState();
225
226            RttStateMachine(Looper looper) {
227                super("RttStateMachine", looper);
228
229                addState(mDefaultState);
230                addState(mEnabledState);
231                    addState(mRequestPendingState, mEnabledState);
232
233                setInitialState(mDefaultState);
234            }
235
236            class DefaultState extends State {
237                @Override
238                public boolean processMessage(Message msg) {
239                    if (DBG) Log.d(TAG, "DefaultState got" + msg);
240                    switch (msg.what) {
241                        case CMD_DRIVER_LOADED:
242                            transitionTo(mEnabledState);
243                            break;
244                        case CMD_ISSUE_NEXT_REQUEST:
245                            deferMessage(msg);
246                            break;
247                        case RttManager.CMD_OP_START_RANGING:
248                            replyFailed(msg, RttManager.REASON_NOT_AVAILABLE, "Try later");
249                            break;
250                        case RttManager.CMD_OP_STOP_RANGING:
251                            return HANDLED;
252                        default:
253                            return NOT_HANDLED;
254                    }
255                    return HANDLED;
256                }
257            }
258
259            class EnabledState extends State {
260                @Override
261                public boolean processMessage(Message msg) {
262                    if (DBG) Log.d(TAG, "EnabledState got" + msg);
263                    ClientInfo ci = mClients.get(msg.replyTo);
264
265                    switch (msg.what) {
266                        case CMD_DRIVER_UNLOADED:
267                            transitionTo(mDefaultState);
268                            break;
269                        case CMD_ISSUE_NEXT_REQUEST:
270                            deferMessage(msg);
271                            transitionTo(mRequestPendingState);
272                            break;
273                        case RttManager.CMD_OP_START_RANGING: {
274                                RttManager.ParcelableRttParams params =
275                                        (RttManager.ParcelableRttParams)msg.obj;
276                                if (params == null) {
277                                    replyFailed(msg,
278                                            RttManager.REASON_INVALID_REQUEST, "No params");
279                                } else if (ci.addRttRequest(msg.arg2, params) == false) {
280                                    replyFailed(msg,
281                                            RttManager.REASON_INVALID_REQUEST, "Unspecified");
282                                } else {
283                                    sendMessage(CMD_ISSUE_NEXT_REQUEST);
284                                }
285                            }
286                            break;
287                        case RttManager.CMD_OP_STOP_RANGING:
288                            for (Iterator<RttRequest> it = mRequestQueue.iterator();
289                                    it.hasNext(); ) {
290                                RttRequest request = it.next();
291                                if (request.key == msg.arg2) {
292                                    if (DBG) Log.d(TAG, "Cancelling not-yet-scheduled RTT");
293                                    mRequestQueue.remove(request);
294                                    request.ci.reportAborted(request.key);
295                                    break;
296                                }
297                            }
298                            break;
299                        default:
300                            return NOT_HANDLED;
301                    }
302                    return HANDLED;
303                }
304            }
305
306            class RequestPendingState extends State {
307                RttRequest mOutstandingRequest;
308                @Override
309                public boolean processMessage(Message msg) {
310                    if (DBG) Log.d(TAG, "RequestPendingState got" + msg);
311                    switch (msg.what) {
312                        case CMD_DRIVER_UNLOADED:
313                            if (mOutstandingRequest != null) {
314                                WifiNative.cancelRtt(mOutstandingRequest.params);
315                                mOutstandingRequest.ci.reportAborted(mOutstandingRequest.key);
316                                mOutstandingRequest = null;
317                            }
318                            transitionTo(mDefaultState);
319                            break;
320                        case CMD_ISSUE_NEXT_REQUEST:
321                            if (mOutstandingRequest == null) {
322                                mOutstandingRequest = issueNextRequest();
323                                if (mOutstandingRequest == null) {
324                                    transitionTo(mEnabledState);
325                                }
326                            } else {
327                                /* just wait; we'll issue next request after
328                                 * current one is finished */
329                                if (DBG) Log.d(TAG, "Ignoring CMD_ISSUE_NEXT_REQUEST");
330                            }
331                            break;
332                        case CMD_RTT_RESPONSE:
333                            if (DBG) Log.d(TAG, "Received an RTT response");
334                            mOutstandingRequest.ci.reportResult(
335                                    mOutstandingRequest, (RttManager.RttResult[])msg.obj);
336                            mOutstandingRequest = null;
337                            sendMessage(CMD_ISSUE_NEXT_REQUEST);
338                            break;
339                        case RttManager.CMD_OP_STOP_RANGING:
340                            if (mOutstandingRequest != null
341                                    && msg.arg2 == mOutstandingRequest.key) {
342                                if (DBG) Log.d(TAG, "Cancelling ongoing RTT");
343                                WifiNative.cancelRtt(mOutstandingRequest.params);
344                                mOutstandingRequest.ci.reportAborted(mOutstandingRequest.key);
345                                mOutstandingRequest = null;
346                                sendMessage(CMD_ISSUE_NEXT_REQUEST);
347                            } else {
348                                /* Let EnabledState handle this */
349                                return NOT_HANDLED;
350                            }
351                            break;
352                        default:
353                            return NOT_HANDLED;
354                    }
355                    return HANDLED;
356                }
357            }
358        }
359
360        void replySucceeded(Message msg, Object obj) {
361            if (msg.replyTo != null) {
362                Message reply = Message.obtain();
363                reply.what = RttManager.CMD_OP_SUCCEEDED;
364                reply.arg2 = msg.arg2;
365                reply.obj = obj;
366                try {
367                    msg.replyTo.send(reply);
368                } catch (RemoteException e) {
369                    // There's not much we can do if reply can't be sent!
370                }
371            } else {
372                // locally generated message; doesn't need a reply!
373            }
374        }
375
376        void replyFailed(Message msg, int reason, String description) {
377            Message reply = Message.obtain();
378            reply.what = RttManager.CMD_OP_FAILED;
379            reply.arg1 = reason;
380            reply.arg2 = msg.arg2;
381
382            Bundle bundle = new Bundle();
383            bundle.putString(RttManager.DESCRIPTION_KEY, description);
384            reply.obj = bundle;
385
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        }
392
393        private WifiNative.RttEventHandler mEventHandler = new WifiNative.RttEventHandler() {
394            @Override
395            public void onRttResults(RttManager.RttResult[] result) {
396                mStateMachine.sendMessage(CMD_RTT_RESPONSE, result);
397            }
398        };
399
400        RttRequest issueNextRequest() {
401            RttRequest request = null;
402            while (mRequestQueue.isEmpty() == false) {
403                request = mRequestQueue.remove();
404                if (WifiNative.requestRtt(request.params, mEventHandler)) {
405                    if (DBG) Log.d(TAG, "Issued next RTT request");
406                    return request;
407                } else {
408                    request.ci.reportFailed(request,
409                            RttManager.REASON_UNSPECIFIED, "Failed to start");
410                }
411            }
412
413            /* all requests exhausted */
414            if (DBG) Log.d(TAG, "No more requests left");
415            return null;
416        }
417    }
418
419    private static final String TAG = "RttService";
420    RttServiceImpl mImpl;
421
422    public RttService(Context context) {
423        super(context);
424        Log.i(TAG, "Creating " + Context.WIFI_RTT_SERVICE);
425    }
426
427    @Override
428    public void onStart() {
429        mImpl = new RttServiceImpl(getContext());
430
431        Log.i(TAG, "Starting " + Context.WIFI_RTT_SERVICE);
432        publishBinderService(Context.WIFI_RTT_SERVICE, mImpl);
433    }
434
435    @Override
436    public void onBootPhase(int phase) {
437        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
438            Log.i(TAG, "Registering " + Context.WIFI_RTT_SERVICE);
439            if (mImpl == null) {
440                mImpl = new RttServiceImpl(getContext());
441            }
442            mImpl.startService(getContext());
443        }
444    }
445}
446