RttService.java revision 202fdf96e1e0191be37a916d97116df35fe3cbfc
1package com.android.server.wifi;
2
3import android.Manifest;
4import android.content.BroadcastReceiver;
5import android.content.Context;
6import android.content.Intent;
7import android.content.IntentFilter;
8import android.net.wifi.IRttManager;
9import android.net.wifi.RttManager;
10import android.net.wifi.RttManager.ResponderConfig;
11import android.net.wifi.WifiManager;
12import android.os.Bundle;
13import android.os.Handler;
14import android.os.HandlerThread;
15import android.os.Looper;
16import android.os.Message;
17import android.os.Messenger;
18import android.os.RemoteException;
19import android.util.Log;
20import android.util.Slog;
21
22import com.android.internal.util.AsyncChannel;
23import com.android.internal.util.Protocol;
24import com.android.internal.util.State;
25import com.android.internal.util.StateMachine;
26import com.android.server.SystemService;
27
28import java.util.HashMap;
29import java.util.HashSet;
30import java.util.Iterator;
31import java.util.LinkedList;
32import java.util.Queue;
33import java.util.Set;
34
35public final class RttService extends SystemService {
36
37    public static final boolean DBG = true;
38
39    static class RttServiceImpl extends IRttManager.Stub {
40
41        @Override
42        public Messenger getMessenger() {
43            return new Messenger(mClientHandler);
44        }
45
46        private class ClientHandler extends Handler {
47
48            ClientHandler(android.os.Looper looper) {
49                super(looper);
50            }
51
52            @Override
53            public void handleMessage(Message msg) {
54
55                if (DBG) {
56                    Log.d(TAG, "ClientHandler got" + msg + " what = " + getDescription(msg.what));
57                }
58
59                switch (msg.what) {
60
61                    case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
62                        if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
63                            Slog.e(TAG, "Send failed, client connection lost");
64                        } else {
65                            if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
66                        }
67                        if (DBG) Slog.d(TAG, "closing client " + msg.replyTo);
68                        ClientInfo ci = mClients.remove(msg.replyTo);
69                        if (ci != null) ci.cleanup();
70                        return;
71                    case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
72                        AsyncChannel ac = new AsyncChannel();
73                        ac.connected(mContext, this, msg.replyTo);
74                        ClientInfo client = new ClientInfo(ac, msg.replyTo);
75                        mClients.put(msg.replyTo, client);
76                        ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
77                                AsyncChannel.STATUS_SUCCESSFUL);
78                        return;
79                }
80
81                ClientInfo ci = mClients.get(msg.replyTo);
82                if (ci == null) {
83                    Slog.e(TAG, "Could not find client info for message " + msg.replyTo);
84                    replyFailed(msg, RttManager.REASON_INVALID_LISTENER, "Could not find listener");
85                    return;
86                }
87
88                final int validCommands[] = {
89                        RttManager.CMD_OP_START_RANGING,
90                        RttManager.CMD_OP_STOP_RANGING,
91                        RttManager.CMD_OP_ENABLE_RESPONDER,
92                        RttManager.CMD_OP_DISABLE_RESPONDER,
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            private String getDescription(int what) {
106                switch(what) {
107                    case RttManager.CMD_OP_ENABLE_RESPONDER:
108                        return "CMD_OP_ENABLE_RESPONDER";
109                    case RttManager.CMD_OP_DISABLE_RESPONDER:
110                        return "CMD_OP_DISABLE_RESPONDER";
111                    default:
112                        return "CMD_UNKNOWN";
113                }
114            }
115        }
116
117        private final WifiNative mWifiNative;
118        private final Context mContext;
119        private final Looper mLooper;
120        private RttStateMachine mStateMachine;
121        private ClientHandler mClientHandler;
122
123        RttServiceImpl(Context context, Looper looper) {
124            mContext = context;
125            mWifiNative = WifiNative.getWlanNativeInterface();
126            mLooper = looper;
127        }
128
129        public void startService() {
130            mClientHandler = new ClientHandler(mLooper);
131            mStateMachine = new RttStateMachine(mLooper);
132
133            mContext.registerReceiver(
134                    new BroadcastReceiver() {
135                        @Override
136                        public void onReceive(Context context, Intent intent) {
137                            int state = intent.getIntExtra(
138                                    WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_DISABLED);
139                            if (DBG) Log.d(TAG, "SCAN_AVAILABLE : " + state);
140                            if (state == WifiManager.WIFI_STATE_ENABLED) {
141                                mStateMachine.sendMessage(CMD_DRIVER_LOADED);
142                            } else if (state == WifiManager.WIFI_STATE_DISABLED) {
143                                mStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
144                            }
145                        }
146                    }, new IntentFilter(WifiManager.WIFI_SCAN_AVAILABLE));
147
148            mStateMachine.start();
149        }
150
151        private class RttRequest {
152            Integer key;
153            ClientInfo ci;
154            RttManager.RttParams[] params;
155
156            @Override
157            public String toString() {
158                String str = getClass().getName() + "@" + Integer.toHexString(hashCode());
159                if(this.key != null) {
160                    return str + " key: " + this.key;
161                } else {
162                    return str + " key: " + " , null";
163                }
164            }
165        }
166
167        private class ClientInfo {
168            private final AsyncChannel mChannel;
169            private final Messenger mMessenger;
170            HashMap<Integer, RttRequest> mRequests = new HashMap<Integer,
171                    RttRequest>();
172            // Client keys of all outstanding responders.
173            Set<Integer> mResponderRequests = new HashSet<>();
174
175            ClientInfo(AsyncChannel c, Messenger m) {
176                mChannel = c;
177                mMessenger = m;
178            }
179
180            void addResponderRequest(int key) {
181                mResponderRequests.add(key);
182            }
183
184            void removeResponderRequest(int key) {
185                mResponderRequests.remove(key);
186            }
187
188            boolean addRttRequest(int key, RttManager.ParcelableRttParams parcelableParams) {
189                if (parcelableParams == null) {
190                    return false;
191                }
192
193                RttManager.RttParams params[] = parcelableParams.mParams;
194
195                RttRequest request = new RttRequest();
196                request.key = key;
197                request.ci = this;
198                request.params = params;
199                mRequests.put(key, request);
200                mRequestQueue.add(request);
201                return true;
202            }
203
204            void removeRttRequest(int key) {
205                mRequests.remove(key);
206            }
207
208            void reportResponderEnableSucceed(int key, ResponderConfig config) {
209                mChannel.sendMessage(RttManager.CMD_OP_ENALBE_RESPONDER_SUCCEEDED, 0, key, config);
210            }
211
212            void reportResponderEnableFailed(int key) {
213                mChannel.sendMessage(RttManager.CMD_OP_ENALBE_RESPONDER_FAILED, 0, key);
214                mResponderRequests.remove(key);
215            }
216
217            void reportResult(RttRequest request, RttManager.RttResult[] results) {
218                RttManager.ParcelableRttResults parcelableResults =
219                        new RttManager.ParcelableRttResults(results);
220
221                mChannel.sendMessage(RttManager.CMD_OP_SUCCEEDED,
222                        0, request.key, parcelableResults);
223                mRequests.remove(request.key);
224            }
225
226            void reportFailed(RttRequest request, int reason, String description) {
227                reportFailed(request.key, reason, description);
228            }
229
230            void reportFailed(int key, int reason, String description) {
231                Bundle bundle = new Bundle();
232                bundle.putString(RttManager.DESCRIPTION_KEY, description);
233                mChannel.sendMessage(RttManager.CMD_OP_FAILED, key, reason, bundle);
234                mRequests.remove(key);
235            }
236
237            void reportAborted(int key) {
238                mChannel.sendMessage(RttManager.CMD_OP_ABORTED, 0, key);
239                //All Queued RTT request will be cleaned
240                cleanup();
241            }
242
243            void cleanup() {
244                mRequests.clear();
245                mRequestQueue.clear();
246            }
247        }
248
249        private Queue<RttRequest> mRequestQueue = new LinkedList<RttRequest>();
250        private HashMap<Messenger, ClientInfo> mClients = new HashMap<Messenger, ClientInfo>(4);
251
252        private static final int BASE = Protocol.BASE_WIFI_RTT_SERVICE;
253
254        private static final int CMD_DRIVER_LOADED                       = BASE + 0;
255        private static final int CMD_DRIVER_UNLOADED                     = BASE + 1;
256        private static final int CMD_ISSUE_NEXT_REQUEST                  = BASE + 2;
257        private static final int CMD_RTT_RESPONSE                        = BASE + 3;
258
259        // Maximum duration for responder role.
260        private static final int MAX_RESPONDER_DURATION_SECONDS = 60 * 10;
261
262        class RttStateMachine extends StateMachine {
263
264            DefaultState mDefaultState = new DefaultState();
265            EnabledState mEnabledState = new EnabledState();
266            InitiatorEnabledState mInitiatorEnabledState = new InitiatorEnabledState();
267            ResponderEnabledState mResponderEnabledState = new ResponderEnabledState();
268            ResponderConfig mResponderConfig;
269
270            RttStateMachine(Looper looper) {
271                super("RttStateMachine", looper);
272
273                // CHECKSTYLE:OFF IndentationCheck
274                addState(mDefaultState);
275                addState(mEnabledState);
276                    addState(mInitiatorEnabledState, mEnabledState);
277                    addState(mResponderEnabledState, mEnabledState);
278                // CHECKSTYLE:ON IndentationCheck
279
280                setInitialState(mDefaultState);
281            }
282
283            class DefaultState extends State {
284                @Override
285                public boolean processMessage(Message msg) {
286                    if (DBG) Log.d(TAG, "DefaultState got" + msg);
287                    switch (msg.what) {
288                        case CMD_DRIVER_LOADED:
289                            transitionTo(mEnabledState);
290                            break;
291                        case CMD_ISSUE_NEXT_REQUEST:
292                            deferMessage(msg);
293                            break;
294                        case RttManager.CMD_OP_START_RANGING:
295                            replyFailed(msg, RttManager.REASON_NOT_AVAILABLE, "Try later");
296                            break;
297                        case RttManager.CMD_OP_STOP_RANGING:
298                            return HANDLED;
299                        case RttManager.CMD_OP_ENABLE_RESPONDER:
300                            replyFailed(msg, RttManager.REASON_NOT_AVAILABLE,
301                                    "Wifi not enabled");
302                            break;
303                        case RttManager.CMD_OP_DISABLE_RESPONDER:
304                            return HANDLED;
305                        default:
306                            return NOT_HANDLED;
307                    }
308                    return HANDLED;
309                }
310            }
311
312            class EnabledState extends State {
313                @Override
314                public boolean processMessage(Message msg) {
315                    if (DBG) Log.d(TAG, "EnabledState got" + msg);
316                    ClientInfo ci = mClients.get(msg.replyTo);
317
318                    switch (msg.what) {
319                        case CMD_DRIVER_UNLOADED:
320                            transitionTo(mDefaultState);
321                            break;
322                        case CMD_ISSUE_NEXT_REQUEST:
323                            deferMessage(msg);
324                            transitionTo(mInitiatorEnabledState);
325                            break;
326                        case RttManager.CMD_OP_START_RANGING: {
327                            //check permission
328                            if(DBG) Log.d(TAG, "UID is: " + msg.sendingUid);
329                            if (!enforcePermissionCheck(msg)) {
330                                break;
331                            }
332
333                            RttManager.ParcelableRttParams params =
334                                    (RttManager.ParcelableRttParams)msg.obj;
335                            if (params == null || params.mParams == null
336                                    || params.mParams.length == 0) {
337                                replyFailed(msg,
338                                        RttManager.REASON_INVALID_REQUEST, "No params");
339                            } else if (ci.addRttRequest(msg.arg2, params) == false) {
340                                replyFailed(msg,
341                                        RttManager.REASON_INVALID_REQUEST, "Unspecified");
342                            } else {
343                                sendMessage(CMD_ISSUE_NEXT_REQUEST);
344                            }
345                        }
346                            break;
347                        case RttManager.CMD_OP_STOP_RANGING:
348                            if(!enforcePermissionCheck(msg)) {
349                                break;
350                            }
351
352                            for (Iterator<RttRequest> it = mRequestQueue.iterator();
353                                    it.hasNext(); ) {
354                                RttRequest request = it.next();
355                                if (request.key == msg.arg2) {
356                                    if (DBG) Log.d(TAG, "Cancelling not-yet-scheduled RTT");
357                                    mRequestQueue.remove(request);
358                                    request.ci.reportAborted(request.key);
359                                    break;
360                                }
361                            }
362                            break;
363                        case RttManager.CMD_OP_ENABLE_RESPONDER:
364                            if (!enforcePermissionCheck(msg)) {
365                                break;
366                            }
367                            int key = msg.arg2;
368                            mResponderConfig =
369                                    mWifiNative.enableRttResponder(MAX_RESPONDER_DURATION_SECONDS);
370                            if (DBG) Log.d(TAG, "mWifiNative.enableRttResponder called");
371
372                            if (mResponderConfig != null) {
373                                // TODO: remove once mac address is added when enabling responder.
374                                mResponderConfig.macAddress = mWifiNative.getMacAddress();
375                                ci.addResponderRequest(key);
376                                ci.reportResponderEnableSucceed(key, mResponderConfig);
377                                transitionTo(mResponderEnabledState);
378                            } else {
379                                Log.e(TAG, "enable responder failed");
380                                ci.reportResponderEnableFailed(key);
381                            }
382                            break;
383                        case RttManager.CMD_OP_DISABLE_RESPONDER:
384                            if (!enforcePermissionCheck(msg)) {
385                                break;
386                            }
387                        default:
388                            return NOT_HANDLED;
389                    }
390                    return HANDLED;
391                }
392            }
393
394            class InitiatorEnabledState extends State {
395                RttRequest mOutstandingRequest;
396                @Override
397                public boolean processMessage(Message msg) {
398                    if (DBG) Log.d(TAG, "RequestPendingState got" + msg);
399                    switch (msg.what) {
400                        case CMD_DRIVER_UNLOADED:
401                            if (mOutstandingRequest != null) {
402                                mWifiNative.cancelRtt(mOutstandingRequest.params);
403                                if (DBG) Log.d(TAG, "abort key: " + mOutstandingRequest.key);
404                                mOutstandingRequest.ci.reportAborted(mOutstandingRequest.key);
405                                mOutstandingRequest = null;
406                            }
407                            transitionTo(mDefaultState);
408                            break;
409                        case CMD_ISSUE_NEXT_REQUEST:
410                            if (mOutstandingRequest == null) {
411                                mOutstandingRequest = issueNextRequest();
412                                if (mOutstandingRequest == null) {
413                                    transitionTo(mEnabledState);
414                                }
415                                if(mOutstandingRequest != null) {
416                                    if (DBG) Log.d(TAG, "new mOutstandingRequest.key is: " +
417                                            mOutstandingRequest.key);
418                                } else {
419                                    if (DBG) Log.d(TAG,
420                                            "CMD_ISSUE_NEXT_REQUEST: mOutstandingRequest =null ");
421                                }
422                            } else {
423                                /* just wait; we'll issue next request after
424                                 * current one is finished */
425                                 if (DBG) Log.d(TAG, "Current mOutstandingRequest.key is: " +
426                                         mOutstandingRequest.key);
427                                 if (DBG) Log.d(TAG, "Ignoring CMD_ISSUE_NEXT_REQUEST");
428                            }
429                            break;
430                        case CMD_RTT_RESPONSE:
431                            if (DBG) Log.d(TAG, "Received an RTT response from: " + msg.arg2);
432                            mOutstandingRequest.ci.reportResult(
433                                    mOutstandingRequest, (RttManager.RttResult[])msg.obj);
434                            mOutstandingRequest = null;
435                            sendMessage(CMD_ISSUE_NEXT_REQUEST);
436                            break;
437                        case RttManager.CMD_OP_STOP_RANGING:
438                            if(!enforcePermissionCheck(msg)) {
439                                break;
440                            }
441
442                            if (mOutstandingRequest != null
443                                    && msg.arg2 == mOutstandingRequest.key) {
444                                if (DBG) Log.d(TAG, "Cancelling ongoing RTT of: " + msg.arg2);
445                                mWifiNative.cancelRtt(mOutstandingRequest.params);
446                                mOutstandingRequest.ci.reportAborted(mOutstandingRequest.key);
447                                mOutstandingRequest = null;
448                                sendMessage(CMD_ISSUE_NEXT_REQUEST);
449                            } else {
450                                /* Let EnabledState handle this */
451                                return NOT_HANDLED;
452                            }
453                            break;
454                        default:
455                            return NOT_HANDLED;
456                    }
457                    return HANDLED;
458                }
459            }
460
461            // Check if there are still outstanding responder requests from any client.
462            private boolean hasOutstandingReponderRequests() {
463                for (ClientInfo client : mClients.values()) {
464                    if (!client.mResponderRequests.isEmpty()) {
465                        return true;
466                    }
467                }
468                return false;
469            }
470
471            /**
472             * Representing an outstanding RTT responder state.
473             */
474            class ResponderEnabledState extends State {
475                @Override
476                public boolean processMessage(Message msg) {
477                    if (DBG) Log.d(TAG, "ResponderEnabledState got " + msg);
478                    ClientInfo ci = mClients.get(msg.replyTo);
479                    int key = msg.arg2;
480                    switch(msg.what) {
481                        case RttManager.CMD_OP_ENABLE_RESPONDER:
482                            // Responder already enabled, simply return the responder config.
483                            ci.addResponderRequest(key);
484                            ci.reportResponderEnableSucceed(key, mResponderConfig);
485                            return HANDLED;
486                        case RttManager.CMD_OP_DISABLE_RESPONDER:
487                            ci.removeResponderRequest(key);
488                            // Only disable responder when there are no outstanding clients.
489                            if (!hasOutstandingReponderRequests()) {
490                                if (!mWifiNative.disableRttResponder()) {
491                                    Log.e(TAG, "disable responder failed");
492                                }
493                                if (DBG) Log.d(TAG, "mWifiNative.disableRttResponder called");
494                                transitionTo(mEnabledState);
495                            }
496                            return HANDLED;
497                        case RttManager.CMD_OP_START_RANGING:
498                        case RttManager.CMD_OP_STOP_RANGING:  // fall through
499                            // Concurrent initiator and responder role is not supported.
500                            replyFailed(msg,
501                                    RttManager.REASON_INITIATOR_NOT_ALLOWED_WHEN_RESPONDER_ON,
502                                    "Initiator not allowed when responder is turned on");
503                            return HANDLED;
504                        default:
505                            return NOT_HANDLED;
506                    }
507                }
508            }
509        }
510
511        void replySucceeded(Message msg, Object obj) {
512            if (msg.replyTo != null) {
513                Message reply = Message.obtain();
514                reply.what = RttManager.CMD_OP_SUCCEEDED;
515                reply.arg2 = msg.arg2;
516                reply.obj = obj;
517                try {
518                    msg.replyTo.send(reply);
519                } catch (RemoteException e) {
520                    // There's not much we can do if reply can't be sent!
521                }
522            } else {
523                // locally generated message; doesn't need a reply!
524            }
525        }
526
527        void replyFailed(Message msg, int reason, String description) {
528            Message reply = Message.obtain();
529            reply.what = RttManager.CMD_OP_FAILED;
530            reply.arg1 = reason;
531            reply.arg2 = msg.arg2;
532
533            Bundle bundle = new Bundle();
534            bundle.putString(RttManager.DESCRIPTION_KEY, description);
535            reply.obj = bundle;
536
537            try {
538                msg.replyTo.send(reply);
539            } catch (RemoteException e) {
540                // There's not much we can do if reply can't be sent!
541            }
542        }
543
544        boolean enforcePermissionCheck(Message msg) {
545            try {
546                mContext.enforcePermission(Manifest.permission.LOCATION_HARDWARE,
547                         -1, msg.sendingUid, "LocationRTT");
548            } catch (SecurityException e) {
549                Log.e(TAG, "UID: " + msg.sendingUid + " has no LOCATION_HARDWARE Permission");
550                replyFailed(msg,RttManager.REASON_PERMISSION_DENIED, "No params");
551                return false;
552            }
553            return true;
554        }
555
556        private WifiNative.RttEventHandler mEventHandler = new WifiNative.RttEventHandler() {
557            @Override
558            public void onRttResults(RttManager.RttResult[] result) {
559                mStateMachine.sendMessage(CMD_RTT_RESPONSE, result);
560            }
561        };
562
563        RttRequest issueNextRequest() {
564            RttRequest request = null;
565            while (mRequestQueue.isEmpty() == false) {
566                request = mRequestQueue.remove();
567                if(request !=  null) {
568                    if (mWifiNative.requestRtt(request.params, mEventHandler)) {
569                        if (DBG) Log.d(TAG, "Issued next RTT request with key: " + request.key);
570                        return request;
571                    } else {
572                        Log.e(TAG, "Fail to issue key at native layer");
573                        request.ci.reportFailed(request,
574                                RttManager.REASON_UNSPECIFIED, "Failed to start");
575                    }
576                }
577            }
578
579            /* all requests exhausted */
580            if (DBG) Log.d(TAG, "No more requests left");
581            return null;
582        }
583        @Override
584        public RttManager.RttCapabilities getRttCapabilities() {
585            return mWifiNative.getRttCapabilities();
586        }
587    }
588
589    private static final String TAG = "RttService";
590    RttServiceImpl mImpl;
591    private final HandlerThread mHandlerThread;
592
593    public RttService(Context context) {
594        super(context);
595        mHandlerThread = new HandlerThread("WifiRttService");
596        mHandlerThread.start();
597        Log.i(TAG, "Creating " + Context.WIFI_RTT_SERVICE);
598    }
599
600    @Override
601    public void onStart() {
602        mImpl = new RttServiceImpl(getContext(), mHandlerThread.getLooper());
603
604        Log.i(TAG, "Starting " + Context.WIFI_RTT_SERVICE);
605        publishBinderService(Context.WIFI_RTT_SERVICE, mImpl);
606    }
607
608    @Override
609    public void onBootPhase(int phase) {
610        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
611            Log.i(TAG, "Registering " + Context.WIFI_RTT_SERVICE);
612            if (mImpl == null) {
613                mImpl = new RttServiceImpl(getContext(), mHandlerThread.getLooper());
614            }
615            mImpl.startService();
616        }
617    }
618
619
620}
621