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
17package com.android.server.wifi.nan;
18
19import android.net.wifi.nan.ConfigRequest;
20import android.net.wifi.nan.IWifiNanEventListener;
21import android.net.wifi.nan.IWifiNanSessionListener;
22import android.net.wifi.nan.PublishData;
23import android.net.wifi.nan.PublishSettings;
24import android.net.wifi.nan.SubscribeData;
25import android.net.wifi.nan.SubscribeSettings;
26import android.os.Bundle;
27import android.os.Handler;
28import android.os.Looper;
29import android.os.Message;
30import android.util.Log;
31import android.util.SparseArray;
32
33import libcore.util.HexEncoding;
34
35import java.io.FileDescriptor;
36import java.io.PrintWriter;
37import java.util.ArrayList;
38import java.util.List;
39
40public class WifiNanStateManager {
41    private static final String TAG = "WifiNanStateManager";
42    private static final boolean DBG = false;
43    private static final boolean VDBG = false; // STOPSHIP if true
44
45    private static WifiNanStateManager sNanStateManagerSingleton;
46
47    private static final int MESSAGE_CONNECT = 0;
48    private static final int MESSAGE_DISCONNECT = 1;
49    private static final int MESSAGE_REQUEST_CONFIG = 4;
50    private static final int MESSAGE_CREATE_SESSION = 5;
51    private static final int MESSAGE_DESTROY_SESSION = 6;
52    private static final int MESSAGE_PUBLISH = 7;
53    private static final int MESSAGE_SUBSCRIBE = 8;
54    private static final int MESSAGE_SEND_MESSAGE = 9;
55    private static final int MESSAGE_STOP_SESSION = 10;
56    private static final int MESSAGE_ON_CONFIG_COMPLETED = 11;
57    private static final int MESSAGE_ON_CONFIG_FAILED = 12;
58    private static final int MESSAGE_ON_NAN_DOWN = 13;
59    private static final int MESSAGE_ON_INTERFACE_CHANGE = 14;
60    private static final int MESSAGE_ON_CLUSTER_CHANGE = 15;
61    private static final int MESSAGE_ON_PUBLISH_SUCCESS = 16;
62    private static final int MESSAGE_ON_PUBLISH_FAIL = 17;
63    private static final int MESSAGE_ON_PUBLISH_TERMINATED = 18;
64    private static final int MESSAGE_ON_SUBSCRIBE_SUCCESS = 19;
65    private static final int MESSAGE_ON_SUBSCRIBE_FAIL = 20;
66    private static final int MESSAGE_ON_SUBSCRIBE_TERMINATED = 21;
67    private static final int MESSAGE_ON_MESSAGE_SEND_SUCCESS = 22;
68    private static final int MESSAGE_ON_MESSAGE_SEND_FAIL = 23;
69    private static final int MESSAGE_ON_UNKNOWN_TRANSACTION = 24;
70    private static final int MESSAGE_ON_MATCH = 25;
71    private static final int MESSAGE_ON_MESSAGE_RECEIVED = 26;
72    private static final int MESSAGE_ON_CAPABILITIES_UPDATED = 27;
73
74    private static final String MESSAGE_BUNDLE_KEY_SESSION_ID = "session_id";
75    private static final String MESSAGE_BUNDLE_KEY_EVENTS = "events";
76    private static final String MESSAGE_BUNDLE_KEY_PUBLISH_DATA = "publish_data";
77    private static final String MESSAGE_BUNDLE_KEY_PUBLISH_SETTINGS = "publish_settings";
78    private static final String MESSAGE_BUNDLE_KEY_SUBSCRIBE_DATA = "subscribe_data";
79    private static final String MESSAGE_BUNDLE_KEY_SUBSCRIBE_SETTINGS = "subscribe_settings";
80    private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message";
81    private static final String MESSAGE_BUNDLE_KEY_MESSAGE_PEER_ID = "message_peer_id";
82    private static final String MESSAGE_BUNDLE_KEY_MESSAGE_ID = "message_id";
83    private static final String MESSAGE_BUNDLE_KEY_RESPONSE_TYPE = "response_type";
84    private static final String MESSAGE_BUNDLE_KEY_SSI_LENGTH = "ssi_length";
85    private static final String MESSAGE_BUNDLE_KEY_SSI_DATA = "ssi_data";
86    private static final String MESSAGE_BUNDLE_KEY_FILTER_LENGTH = "filter_length";
87    private static final String MESSAGE_BUNDLE_KEY_FILTER_DATA = "filter_data";
88    private static final String MESSAGE_BUNDLE_KEY_MAC_ADDRESS = "mac_address";
89    private static final String MESSAGE_BUNDLE_KEY_MESSAGE_DATA = "message_data";
90    private static final String MESSAGE_BUNDLE_KEY_MESSAGE_LENGTH = "message_length";
91
92    private WifiNanNative.Capabilities mCapabilities;
93
94    private WifiNanStateHandler mHandler;
95
96    // no synchronization necessary: only access through Handler
97    private final SparseArray<WifiNanClientState> mClients = new SparseArray<>();
98    private final SparseArray<TransactionInfoBase> mPendingResponses = new SparseArray<>();
99    private short mNextTransactionId = 1;
100
101    private WifiNanStateManager() {
102        // EMPTY: singleton pattern
103    }
104
105    public static WifiNanStateManager getInstance() {
106        if (sNanStateManagerSingleton == null) {
107            sNanStateManagerSingleton = new WifiNanStateManager();
108        }
109
110        return sNanStateManagerSingleton;
111    }
112
113    public void start(Looper looper) {
114        Log.i(TAG, "start()");
115
116        mHandler = new WifiNanStateHandler(looper);
117    }
118
119    public void connect(int uid, IWifiNanEventListener listener, int events) {
120        Message msg = mHandler.obtainMessage(MESSAGE_CONNECT);
121        msg.arg1 = uid;
122        msg.arg2 = events;
123        msg.obj = listener;
124        mHandler.sendMessage(msg);
125    }
126
127    public void disconnect(int uid) {
128        Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT);
129        msg.arg1 = uid;
130        mHandler.sendMessage(msg);
131    }
132
133    public void requestConfig(int uid, ConfigRequest configRequest) {
134        Message msg = mHandler.obtainMessage(MESSAGE_REQUEST_CONFIG);
135        msg.arg1 = uid;
136        msg.obj = configRequest;
137        mHandler.sendMessage(msg);
138    }
139
140    public void stopSession(int uid, int sessionId) {
141        Message msg = mHandler.obtainMessage(MESSAGE_STOP_SESSION);
142        msg.arg1 = uid;
143        msg.arg2 = sessionId;
144        mHandler.sendMessage(msg);
145    }
146
147    public void destroySession(int uid, int sessionId) {
148        Message msg = mHandler.obtainMessage(MESSAGE_DESTROY_SESSION);
149        msg.arg1 = uid;
150        msg.arg2 = sessionId;
151        mHandler.sendMessage(msg);
152    }
153
154    public void createSession(int uid, int sessionId, IWifiNanSessionListener listener,
155            int events) {
156        Bundle data = new Bundle();
157        data.putInt(MESSAGE_BUNDLE_KEY_EVENTS, events);
158
159        Message msg = mHandler.obtainMessage(MESSAGE_CREATE_SESSION);
160        msg.setData(data);
161        msg.arg1 = uid;
162        msg.arg2 = sessionId;
163        msg.obj = listener;
164        mHandler.sendMessage(msg);
165    }
166
167    public void publish(int uid, int sessionId, PublishData publishData,
168            PublishSettings publishSettings) {
169        Bundle data = new Bundle();
170        data.putParcelable(MESSAGE_BUNDLE_KEY_PUBLISH_DATA, publishData);
171        data.putParcelable(MESSAGE_BUNDLE_KEY_PUBLISH_SETTINGS, publishSettings);
172
173        Message msg = mHandler.obtainMessage(MESSAGE_PUBLISH);
174        msg.setData(data);
175        msg.arg1 = uid;
176        msg.arg2 = sessionId;
177        mHandler.sendMessage(msg);
178    }
179
180    public void subscribe(int uid, int sessionId, SubscribeData subscribeData,
181            SubscribeSettings subscribeSettings) {
182        Bundle data = new Bundle();
183        data.putParcelable(MESSAGE_BUNDLE_KEY_SUBSCRIBE_DATA, subscribeData);
184        data.putParcelable(MESSAGE_BUNDLE_KEY_SUBSCRIBE_SETTINGS, subscribeSettings);
185
186        Message msg = mHandler.obtainMessage(MESSAGE_SUBSCRIBE);
187        msg.setData(data);
188        msg.arg1 = uid;
189        msg.arg2 = sessionId;
190        mHandler.sendMessage(msg);
191    }
192
193    public void sendMessage(int uid, int sessionId, int peerId, byte[] message, int messageLength,
194            int messageId) {
195        Bundle data = new Bundle();
196        data.putInt(MESSAGE_BUNDLE_KEY_SESSION_ID, sessionId);
197        data.putInt(MESSAGE_BUNDLE_KEY_MESSAGE_PEER_ID, peerId);
198        data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, message);
199        data.putInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID, messageId);
200
201        Message msg = mHandler.obtainMessage(MESSAGE_SEND_MESSAGE);
202        msg.arg1 = uid;
203        msg.arg2 = messageLength;
204        msg.setData(data);
205        mHandler.sendMessage(msg);
206    }
207
208    public void onCapabilitiesUpdate(short transactionId, WifiNanNative.Capabilities capabilities) {
209        Message msg = mHandler.obtainMessage(MESSAGE_ON_CAPABILITIES_UPDATED);
210        msg.arg1 = transactionId;
211        msg.obj = capabilities;
212        mHandler.sendMessage(msg);
213    }
214
215    public void onConfigCompleted(short transactionId) {
216        Message msg = mHandler.obtainMessage(MESSAGE_ON_CONFIG_COMPLETED);
217        msg.arg1 = transactionId;
218        mHandler.sendMessage(msg);
219    }
220
221    public void onConfigFailed(short transactionId, int reason) {
222        Message msg = mHandler.obtainMessage(MESSAGE_ON_CONFIG_FAILED);
223        msg.arg1 = transactionId;
224        msg.arg2 = reason;
225        mHandler.sendMessage(msg);
226    }
227
228    public void onPublishSuccess(short transactionId, int publishId) {
229        Message msg = mHandler.obtainMessage(MESSAGE_ON_PUBLISH_SUCCESS);
230        msg.arg1 = transactionId;
231        msg.arg2 = publishId;
232        mHandler.sendMessage(msg);
233    }
234
235    public void onPublishFail(short transactionId, int status) {
236        Message msg = mHandler.obtainMessage(MESSAGE_ON_PUBLISH_FAIL);
237        msg.arg1 = transactionId;
238        msg.arg2 = status;
239        mHandler.sendMessage(msg);
240    }
241
242    public void onMessageSendSuccess(short transactionId) {
243        Message msg = mHandler.obtainMessage(MESSAGE_ON_MESSAGE_SEND_SUCCESS);
244        msg.arg1 = transactionId;
245        mHandler.sendMessage(msg);
246    }
247
248    public void onMessageSendFail(short transactionId, int status) {
249        Message msg = mHandler.obtainMessage(MESSAGE_ON_MESSAGE_SEND_FAIL);
250        msg.arg1 = transactionId;
251        msg.arg2 = status;
252        mHandler.sendMessage(msg);
253    }
254
255    public void onSubscribeSuccess(short transactionId, int subscribeId) {
256        Message msg = mHandler.obtainMessage(MESSAGE_ON_SUBSCRIBE_SUCCESS);
257        msg.arg1 = transactionId;
258        msg.arg2 = subscribeId;
259        mHandler.sendMessage(msg);
260    }
261
262    public void onSubscribeFail(short transactionId, int status) {
263        Message msg = mHandler.obtainMessage(MESSAGE_ON_SUBSCRIBE_FAIL);
264        msg.arg1 = transactionId;
265        msg.arg2 = status;
266        mHandler.sendMessage(msg);
267    }
268
269    public void onUnknownTransaction(int responseType, short transactionId, int status) {
270        Message msg = mHandler.obtainMessage(MESSAGE_ON_UNKNOWN_TRANSACTION);
271        Bundle data = new Bundle();
272        data.putInt(MESSAGE_BUNDLE_KEY_RESPONSE_TYPE, responseType);
273        msg.setData(data);
274        msg.arg1 = transactionId;
275        msg.arg2 = status;
276        mHandler.sendMessage(msg);
277    }
278
279    public void onInterfaceAddressChange(byte[] mac) {
280        Message msg = mHandler.obtainMessage(MESSAGE_ON_INTERFACE_CHANGE);
281        msg.obj = mac;
282        mHandler.sendMessage(msg);
283    }
284
285    public void onClusterChange(int flag, byte[] clusterId) {
286        Message msg = mHandler.obtainMessage(MESSAGE_ON_CLUSTER_CHANGE);
287        msg.arg1 = flag;
288        msg.obj = clusterId;
289        mHandler.sendMessage(msg);
290    }
291
292    public void onMatch(int pubSubId, int requestorInstanceId, byte[] peerMac,
293            byte[] serviceSpecificInfo, int serviceSpecificInfoLength, byte[] matchFilter,
294            int matchFilterLength) {
295        Message msg = mHandler.obtainMessage(MESSAGE_ON_MATCH);
296        msg.arg1 = pubSubId;
297        msg.arg2 = requestorInstanceId;
298        Bundle data = new Bundle();
299        data.putByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS, peerMac);
300        data.putByteArray(MESSAGE_BUNDLE_KEY_SSI_DATA, serviceSpecificInfo);
301        data.putInt(MESSAGE_BUNDLE_KEY_SSI_LENGTH, serviceSpecificInfoLength);
302        data.putByteArray(MESSAGE_BUNDLE_KEY_FILTER_DATA, matchFilter);
303        data.putInt(MESSAGE_BUNDLE_KEY_FILTER_LENGTH, matchFilterLength);
304        msg.setData(data);
305        mHandler.sendMessage(msg);
306    }
307
308    public void onPublishTerminated(int publishId, int status) {
309        Message msg = mHandler.obtainMessage(MESSAGE_ON_PUBLISH_TERMINATED);
310        msg.arg1 = publishId;
311        msg.arg2 = status;
312        mHandler.sendMessage(msg);
313    }
314
315    public void onSubscribeTerminated(int subscribeId, int status) {
316        Message msg = mHandler.obtainMessage(MESSAGE_ON_SUBSCRIBE_TERMINATED);
317        msg.arg1 = subscribeId;
318        msg.arg2 = status;
319        mHandler.sendMessage(msg);
320    }
321
322    public void onMessageReceived(int pubSubId, int requestorInstanceId, byte[] peerMac,
323            byte[] message, int messageLength) {
324        Message msg = mHandler.obtainMessage(MESSAGE_ON_MESSAGE_RECEIVED);
325        msg.arg1 = pubSubId;
326        msg.arg2 = requestorInstanceId;
327        Bundle data = new Bundle();
328        data.putByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS, peerMac);
329        data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE_DATA, message);
330        data.putInt(MESSAGE_BUNDLE_KEY_MESSAGE_LENGTH, messageLength);
331        msg.setData(data);
332        mHandler.sendMessage(msg);
333    }
334
335    public void onNanDown(int reason) {
336        Message msg = mHandler.obtainMessage(MESSAGE_ON_NAN_DOWN);
337        msg.arg1 = reason;
338        mHandler.sendMessage(msg);
339    }
340
341    private class WifiNanStateHandler extends Handler {
342        WifiNanStateHandler(android.os.Looper looper) {
343            super(looper);
344        }
345
346        @Override
347        public void handleMessage(Message msg) {
348            if (DBG) {
349                Log.d(TAG, "Message: " + msg.what);
350            }
351            switch (msg.what) {
352                case MESSAGE_CONNECT: {
353                    if (VDBG) {
354                        Log.d(TAG, "NAN connection request received");
355                    }
356                    connectLocal(msg.arg1, (IWifiNanEventListener) msg.obj, msg.arg2);
357                    break;
358                }
359                case MESSAGE_DISCONNECT: {
360                    if (VDBG) {
361                        Log.d(TAG, "NAN disconnection request received");
362                    }
363                    disconnectLocal(msg.arg1);
364                    break;
365                }
366                case MESSAGE_REQUEST_CONFIG: {
367                    if (VDBG) {
368                        Log.d(TAG, "NAN configuration request received");
369                    }
370                    requestConfigLocal(msg.arg1, (ConfigRequest) msg.obj);
371                    break;
372                }
373                case MESSAGE_CREATE_SESSION: {
374                    if (VDBG) {
375                        Log.d(TAG, "Create session");
376                    }
377                    int events = msg.getData().getInt(MESSAGE_BUNDLE_KEY_EVENTS);
378                    createSessionLocal(msg.arg1, msg.arg2, (IWifiNanSessionListener) msg.obj,
379                            events);
380                    break;
381                }
382                case MESSAGE_DESTROY_SESSION: {
383                    if (VDBG) {
384                        Log.d(TAG, "Destroy session");
385                    }
386                    destroySessionLocal(msg.arg1, msg.arg2);
387                    break;
388                }
389                case MESSAGE_PUBLISH: {
390                    Bundle data = msg.getData();
391                    PublishData publishData = (PublishData) data
392                            .getParcelable(MESSAGE_BUNDLE_KEY_PUBLISH_DATA);
393                    PublishSettings publishSettings = (PublishSettings) data
394                            .getParcelable(MESSAGE_BUNDLE_KEY_PUBLISH_SETTINGS);
395                    if (VDBG) {
396                        Log.d(TAG,
397                                "Publish: data='" + publishData + "', settings=" + publishSettings);
398                    }
399
400                    publishLocal(msg.arg1, msg.arg2, publishData, publishSettings);
401                    break;
402                }
403                case MESSAGE_SUBSCRIBE: {
404                    Bundle data = msg.getData();
405                    SubscribeData subscribeData = (SubscribeData) data
406                            .getParcelable(MESSAGE_BUNDLE_KEY_SUBSCRIBE_DATA);
407                    SubscribeSettings subscribeSettings = (SubscribeSettings) data
408                            .getParcelable(MESSAGE_BUNDLE_KEY_SUBSCRIBE_SETTINGS);
409                    if (VDBG) {
410                        Log.d(TAG, "Subscribe: data='" + subscribeData + "', settings="
411                                + subscribeSettings);
412                    }
413
414                    subscribeLocal(msg.arg1, msg.arg2, subscribeData, subscribeSettings);
415                    break;
416                }
417                case MESSAGE_SEND_MESSAGE: {
418                    Bundle data = msg.getData();
419                    int sessionId = msg.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
420                    int peerId = data.getInt(MESSAGE_BUNDLE_KEY_MESSAGE_PEER_ID);
421                    byte[] message = data.getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE);
422                    int messageId = data.getInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID);
423
424                    if (VDBG) {
425                        Log.d(TAG, "Send Message: message='" + message + "' (ID=" + messageId
426                                + ") to peerId=" + peerId);
427                    }
428
429                    sendFollowonMessageLocal(msg.arg1, sessionId, peerId, message, msg.arg2,
430                            messageId);
431                    break;
432                }
433                case MESSAGE_STOP_SESSION: {
434                    if (VDBG) {
435                        Log.d(TAG, "Stop session");
436                    }
437                    stopSessionLocal(msg.arg1, msg.arg2);
438                    break;
439                }
440                case MESSAGE_ON_CAPABILITIES_UPDATED:
441                    onCapabilitiesUpdatedLocal((short) msg.arg1,
442                            (WifiNanNative.Capabilities) msg.obj);
443                    break;
444                case MESSAGE_ON_CONFIG_COMPLETED:
445                    onConfigCompletedLocal((short) msg.arg1);
446                    break;
447                case MESSAGE_ON_CONFIG_FAILED:
448                    onConfigFailedLocal((short) msg.arg1, msg.arg2);
449                    break;
450                case MESSAGE_ON_NAN_DOWN:
451                    onNanDownLocal(msg.arg1);
452                    break;
453                case MESSAGE_ON_INTERFACE_CHANGE:
454                    onInterfaceAddressChangeLocal((byte[]) msg.obj);
455                    break;
456                case MESSAGE_ON_CLUSTER_CHANGE:
457                    onClusterChangeLocal(msg.arg1, (byte[]) msg.obj);
458                    break;
459                case MESSAGE_ON_PUBLISH_SUCCESS:
460                    onPublishSuccessLocal((short) msg.arg1, msg.arg2);
461                    break;
462                case MESSAGE_ON_PUBLISH_FAIL:
463                    onPublishFailLocal((short) msg.arg1, msg.arg2);
464                    break;
465                case MESSAGE_ON_PUBLISH_TERMINATED:
466                    onPublishTerminatedLocal(msg.arg1, msg.arg2);
467                    break;
468                case MESSAGE_ON_SUBSCRIBE_SUCCESS:
469                    onSubscribeSuccessLocal((short) msg.arg1, msg.arg2);
470                    break;
471                case MESSAGE_ON_SUBSCRIBE_FAIL:
472                    onSubscribeFailLocal((short) msg.arg1, msg.arg2);
473                    break;
474                case MESSAGE_ON_SUBSCRIBE_TERMINATED:
475                    onSubscribeTerminatedLocal(msg.arg1, msg.arg2);
476                    break;
477                case MESSAGE_ON_MESSAGE_SEND_SUCCESS:
478                    onMessageSendSuccessLocal((short) msg.arg1);
479                    break;
480                case MESSAGE_ON_MESSAGE_SEND_FAIL:
481                    onMessageSendFailLocal((short) msg.arg1, msg.arg2);
482                    break;
483                case MESSAGE_ON_UNKNOWN_TRANSACTION:
484                    onUnknownTransactionLocal(
485                            msg.getData().getInt(MESSAGE_BUNDLE_KEY_RESPONSE_TYPE),
486                            (short) msg.arg1, msg.arg2);
487                    break;
488                case MESSAGE_ON_MATCH: {
489                    int pubSubId = msg.arg1;
490                    int requestorInstanceId = msg.arg2;
491                    byte[] peerMac = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS);
492                    byte[] serviceSpecificInfo = msg.getData()
493                            .getByteArray(MESSAGE_BUNDLE_KEY_SSI_DATA);
494                    int serviceSpecificInfoLength = msg.getData()
495                            .getInt(MESSAGE_BUNDLE_KEY_SSI_LENGTH);
496                    byte[] matchFilter = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_FILTER_DATA);
497                    int matchFilterLength = msg.getData().getInt(MESSAGE_BUNDLE_KEY_FILTER_LENGTH);
498                    onMatchLocal(pubSubId, requestorInstanceId, peerMac, serviceSpecificInfo,
499                            serviceSpecificInfoLength, matchFilter, matchFilterLength);
500                    break;
501                }
502                case MESSAGE_ON_MESSAGE_RECEIVED: {
503                    int pubSubId = msg.arg1;
504                    int requestorInstanceId = msg.arg2;
505                    byte[] peerMac = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS);
506                    byte[] message = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE_DATA);
507                    int messageLength = msg.getData().getInt(MESSAGE_BUNDLE_KEY_MESSAGE_LENGTH);
508                    onMessageReceivedLocal(pubSubId, requestorInstanceId, peerMac, message,
509                            messageLength);
510                    break;
511                }
512                default:
513                    Log.e(TAG, "Unknown message code: " + msg.what);
514            }
515        }
516    }
517
518    /*
519     * Transaction management classes & operations
520     */
521
522    // non-synchronized (should be ok as long as only used from NanStateManager,
523    // NanClientState, and NanSessionState)
524    /* package */ short createNextTransactionId() {
525        return mNextTransactionId++;
526    }
527
528    private static class TransactionInfoBase {
529        short mTransactionId;
530    }
531
532    private static class TransactionInfoSession extends TransactionInfoBase {
533        public WifiNanClientState mClient;
534        public WifiNanSessionState mSession;
535    }
536
537    private static class TransactionInfoMessage extends TransactionInfoSession {
538        public int mMessageId;
539    }
540
541    private static class TransactionInfoConfig extends TransactionInfoBase {
542        public ConfigRequest mConfig;
543    }
544
545    private void allocateAndRegisterTransactionId(TransactionInfoBase info) {
546        info.mTransactionId = createNextTransactionId();
547
548        mPendingResponses.put(info.mTransactionId, info);
549    }
550
551    private void fillInTransactionInfoSession(TransactionInfoSession info, int uid,
552            int sessionId) {
553        WifiNanClientState client = mClients.get(uid);
554        if (client == null) {
555            throw new IllegalStateException(
556                    "getAndRegisterTransactionId: no client exists for uid=" + uid);
557        }
558        info.mClient = client;
559
560        WifiNanSessionState session = info.mClient.getSession(sessionId);
561        if (session == null) {
562            throw new IllegalStateException(
563                    "getAndRegisterSessionTransactionId: no session exists for uid=" + uid
564                            + ", sessionId=" + sessionId);
565        }
566        info.mSession = session;
567    }
568
569    private TransactionInfoBase createTransactionInfo() {
570        TransactionInfoBase info = new TransactionInfoBase();
571        allocateAndRegisterTransactionId(info);
572        return info;
573    }
574
575    private TransactionInfoSession createTransactionInfoSession(int uid, int sessionId) {
576        TransactionInfoSession info = new TransactionInfoSession();
577        fillInTransactionInfoSession(info, uid, sessionId);
578        allocateAndRegisterTransactionId(info);
579        return info;
580    }
581
582    private TransactionInfoMessage createTransactionInfoMessage(int uid, int sessionId,
583            int messageId) {
584        TransactionInfoMessage info = new TransactionInfoMessage();
585        fillInTransactionInfoSession(info, uid, sessionId);
586        info.mMessageId = messageId;
587        allocateAndRegisterTransactionId(info);
588        return info;
589    }
590
591    private TransactionInfoConfig createTransactionInfoConfig(ConfigRequest configRequest) {
592        TransactionInfoConfig info = new TransactionInfoConfig();
593        info.mConfig = configRequest;
594        allocateAndRegisterTransactionId(info);
595        return info;
596    }
597
598    private TransactionInfoBase getAndRemovePendingResponseTransactionInfo(short transactionId) {
599        TransactionInfoBase transInfo = mPendingResponses.get(transactionId);
600        if (transInfo != null) {
601            mPendingResponses.remove(transactionId);
602        }
603
604        return transInfo;
605    }
606
607    private WifiNanSessionState getNanSessionStateForPubSubId(int pubSubId) {
608        for (int i = 0; i < mClients.size(); ++i) {
609            WifiNanSessionState session = mClients.valueAt(i)
610                    .getNanSessionStateForPubSubId(pubSubId);
611            if (session != null) {
612                return session;
613            }
614        }
615
616        return null;
617    }
618
619    /*
620     * Actions (calls from API to service)
621     */
622    private void connectLocal(int uid, IWifiNanEventListener listener, int events) {
623        if (VDBG) {
624            Log.v(TAG, "connect(): uid=" + uid + ", listener=" + listener + ", events=" + events);
625        }
626
627        if (mClients.get(uid) != null) {
628            Log.e(TAG, "connect: entry already exists for uid=" + uid);
629            return;
630        }
631
632        WifiNanClientState client = new WifiNanClientState(uid, listener, events);
633        mClients.put(uid, client);
634    }
635
636    private void disconnectLocal(int uid) {
637        if (VDBG) {
638            Log.v(TAG, "disconnect(): uid=" + uid);
639        }
640
641        WifiNanClientState client = mClients.get(uid);
642        mClients.delete(uid);
643
644        if (client == null) {
645            Log.e(TAG, "disconnect: no entry for uid=" + uid);
646            return;
647        }
648
649        List<Integer> toRemove = new ArrayList<>();
650        for (int i = 0; i < mPendingResponses.size(); ++i) {
651            TransactionInfoBase info = mPendingResponses.valueAt(i);
652            if (!(info instanceof TransactionInfoSession)) {
653                continue;
654            }
655            if (((TransactionInfoSession) info).mClient.getUid() == uid) {
656                toRemove.add(i);
657            }
658        }
659        for (Integer id : toRemove) {
660            mPendingResponses.removeAt(id);
661        }
662
663        client.destroy();
664
665        if (mClients.size() == 0) {
666            WifiNanNative.getInstance().disable(createTransactionInfo().mTransactionId);
667            return;
668        }
669
670        ConfigRequest merged = mergeConfigRequests();
671
672        WifiNanNative.getInstance()
673                .enableAndConfigure(createTransactionInfoConfig(merged).mTransactionId, merged);
674    }
675
676    private void requestConfigLocal(int uid, ConfigRequest configRequest) {
677        if (VDBG) {
678            Log.v(TAG, "requestConfig(): uid=" + uid + ", configRequest=" + configRequest);
679        }
680
681        WifiNanClientState client = mClients.get(uid);
682        if (client == null) {
683            Log.e(TAG, "requestConfig: no client exists for uid=" + uid);
684            return;
685        }
686
687        client.setConfigRequest(configRequest);
688
689        ConfigRequest merged = mergeConfigRequests();
690
691        WifiNanNative.getInstance()
692                .enableAndConfigure(createTransactionInfoConfig(merged).mTransactionId, merged);
693    }
694
695    private void createSessionLocal(int uid, int sessionId, IWifiNanSessionListener listener,
696            int events) {
697        if (VDBG) {
698            Log.v(TAG, "createSession(): uid=" + uid + ", sessionId=" + sessionId + ", listener="
699                    + listener + ", events=" + events);
700        }
701
702        WifiNanClientState client = mClients.get(uid);
703        if (client == null) {
704            Log.e(TAG, "createSession: no client exists for uid=" + uid);
705            return;
706        }
707
708        client.createSession(sessionId, listener, events);
709    }
710
711    private void destroySessionLocal(int uid, int sessionId) {
712        if (VDBG) {
713            Log.v(TAG, "destroySession(): uid=" + uid + ", sessionId=" + sessionId);
714        }
715
716        WifiNanClientState client = mClients.get(uid);
717        if (client == null) {
718            Log.e(TAG, "destroySession: no client exists for uid=" + uid);
719            return;
720        }
721
722        List<Integer> toRemove = new ArrayList<>();
723        for (int i = 0; i < mPendingResponses.size(); ++i) {
724            TransactionInfoBase info = mPendingResponses.valueAt(i);
725            if (!(info instanceof TransactionInfoSession)) {
726                continue;
727            }
728            TransactionInfoSession infoSession = (TransactionInfoSession) info;
729            if (infoSession.mClient.getUid() == uid
730                    && infoSession.mSession.getSessionId() == sessionId) {
731                toRemove.add(i);
732            }
733        }
734        for (Integer id : toRemove) {
735            mPendingResponses.removeAt(id);
736        }
737
738        client.destroySession(sessionId);
739    }
740
741    private void publishLocal(int uid, int sessionId, PublishData publishData,
742            PublishSettings publishSettings) {
743        if (VDBG) {
744            Log.v(TAG, "publish(): uid=" + uid + ", sessionId=" + sessionId + ", data="
745                    + publishData + ", settings=" + publishSettings);
746        }
747
748        TransactionInfoSession info = createTransactionInfoSession(uid, sessionId);
749
750        info.mSession.publish(info.mTransactionId, publishData, publishSettings);
751    }
752
753    private void subscribeLocal(int uid, int sessionId, SubscribeData subscribeData,
754            SubscribeSettings subscribeSettings) {
755        if (VDBG) {
756            Log.v(TAG, "subscribe(): uid=" + uid + ", sessionId=" + sessionId + ", data="
757                    + subscribeData + ", settings=" + subscribeSettings);
758        }
759
760        TransactionInfoSession info = createTransactionInfoSession(uid, sessionId);
761
762        info.mSession.subscribe(info.mTransactionId, subscribeData, subscribeSettings);
763    }
764
765    private void sendFollowonMessageLocal(int uid, int sessionId, int peerId, byte[] message,
766            int messageLength, int messageId) {
767        if (VDBG) {
768            Log.v(TAG, "sendMessage(): uid=" + uid + ", sessionId=" + sessionId + ", peerId="
769                    + peerId + ", messageLength=" + messageLength + ", messageId=" + messageId);
770        }
771
772        TransactionInfoMessage info = createTransactionInfoMessage(uid, sessionId, messageId);
773
774        info.mSession.sendMessage(info.mTransactionId, peerId, message, messageLength, messageId);
775    }
776
777    private void stopSessionLocal(int uid, int sessionId) {
778        if (VDBG) {
779            Log.v(TAG, "stopSession(): uid=" + uid + ", sessionId=" + sessionId);
780        }
781
782        TransactionInfoSession info = createTransactionInfoSession(uid, sessionId);
783
784        info.mSession.stop(info.mTransactionId);
785    }
786
787    /*
788     * Callbacks (calls from HAL/Native to service)
789     */
790
791    private void onCapabilitiesUpdatedLocal(short transactionId,
792            WifiNanNative.Capabilities capabilities) {
793        if (VDBG) {
794            Log.v(TAG, "onCapabilitiesUpdatedLocal: transactionId=" + transactionId
795                    + ", capabilites=" + capabilities);
796        }
797
798        mCapabilities = capabilities;
799    }
800
801    private void onConfigCompletedLocal(short transactionId) {
802        if (VDBG) {
803            Log.v(TAG, "onConfigCompleted: transactionId=" + transactionId);
804        }
805
806        TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
807        if (info == null) {
808            Log.e(TAG, "onConfigCompleted: no transaction info for transactionId=" + transactionId);
809            return;
810        }
811        if (!(info instanceof TransactionInfoConfig)) {
812            Log.e(TAG, "onConfigCompleted: invalid info structure stored for transactionId="
813                    + transactionId);
814            return;
815        }
816        TransactionInfoConfig infoConfig = (TransactionInfoConfig) info;
817
818        if (DBG) {
819            Log.d(TAG, "onConfigCompleted: request=" + infoConfig.mConfig);
820        }
821
822        for (int i = 0; i < mClients.size(); ++i) {
823            WifiNanClientState client = mClients.valueAt(i);
824            client.onConfigCompleted(infoConfig.mConfig);
825        }
826    }
827
828    private void onConfigFailedLocal(short transactionId, int reason) {
829        if (VDBG) {
830            Log.v(TAG, "onEnableFailed: transactionId=" + transactionId + ", reason=" + reason);
831        }
832
833        TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
834        if (info == null) {
835            Log.e(TAG, "onConfigFailed: no transaction info for transactionId=" + transactionId);
836            return;
837        }
838        if (!(info instanceof TransactionInfoConfig)) {
839            Log.e(TAG, "onConfigCompleted: invalid info structure stored for transactionId="
840                    + transactionId);
841            return;
842        }
843        TransactionInfoConfig infoConfig = (TransactionInfoConfig) info;
844
845        if (DBG) {
846            Log.d(TAG, "onConfigFailed: request=" + infoConfig.mConfig);
847        }
848
849        for (int i = 0; i < mClients.size(); ++i) {
850            WifiNanClientState client = mClients.valueAt(i);
851            client.onConfigFailed(infoConfig.mConfig, reason);
852        }
853    }
854
855    private void onNanDownLocal(int reason) {
856        if (VDBG) {
857            Log.v(TAG, "onNanDown: reason=" + reason);
858        }
859
860        int interested = 0;
861        for (int i = 0; i < mClients.size(); ++i) {
862            WifiNanClientState client = mClients.valueAt(i);
863            interested += client.onNanDown(reason);
864        }
865
866        if (interested == 0) {
867            Log.e(TAG, "onNanDown: event received but no listeners registered for this event "
868                    + "- should be disabled from fw!");
869        }
870    }
871
872    private void onInterfaceAddressChangeLocal(byte[] mac) {
873        if (VDBG) {
874            Log.v(TAG, "onInterfaceAddressChange: mac=" + String.valueOf(HexEncoding.encode(mac)));
875        }
876
877        int interested = 0;
878        for (int i = 0; i < mClients.size(); ++i) {
879            WifiNanClientState client = mClients.valueAt(i);
880            interested += client.onInterfaceAddressChange(mac);
881        }
882
883        if (interested == 0) {
884            Log.e(TAG, "onInterfaceAddressChange: event received but no listeners registered "
885                    + "for this event - should be disabled from fw!");
886        }
887    }
888
889    private void onClusterChangeLocal(int flag, byte[] clusterId) {
890        if (VDBG) {
891            Log.v(TAG, "onClusterChange: flag=" + flag + ", clusterId="
892                    + String.valueOf(HexEncoding.encode(clusterId)));
893        }
894
895        int interested = 0;
896        for (int i = 0; i < mClients.size(); ++i) {
897            WifiNanClientState client = mClients.valueAt(i);
898            interested += client.onClusterChange(flag, clusterId);
899        }
900
901        if (interested == 0) {
902            Log.e(TAG, "onClusterChange: event received but no listeners registered for this "
903                    + "event - should be disabled from fw!");
904        }
905    }
906
907    private void onPublishSuccessLocal(short transactionId, int publishId) {
908        if (VDBG) {
909            Log.v(TAG, "onPublishSuccess: transactionId=" + transactionId + ", publishId="
910                    + publishId);
911        }
912
913        TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
914        if (info == null) {
915            Log.e(TAG, "onPublishSuccess(): no info registered for transactionId=" + transactionId);
916            return;
917        }
918        if (!(info instanceof TransactionInfoSession)) {
919            Log.e(TAG, "onPublishSuccess: invalid info structure stored for transactionId="
920                    + transactionId);
921            return;
922        }
923        TransactionInfoSession infoSession = (TransactionInfoSession) info;
924
925        infoSession.mSession.onPublishSuccess(publishId);
926    }
927
928    private void onPublishFailLocal(short transactionId, int status) {
929        if (VDBG) {
930            Log.v(TAG, "onPublishFail: transactionId=" + transactionId + ", status=" + status);
931        }
932
933        TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
934        if (info == null) {
935            Log.e(TAG, "onPublishFail(): no info registered for transactionId=" + transactionId);
936            return;
937        }
938        if (!(info instanceof TransactionInfoSession)) {
939            Log.e(TAG, "onPublishFail: invalid info structure stored for transactionId="
940                    + transactionId);
941            return;
942        }
943        TransactionInfoSession infoSession = (TransactionInfoSession) info;
944
945        infoSession.mSession.onPublishFail(status);
946    }
947
948    private void onPublishTerminatedLocal(int publishId, int status) {
949        if (VDBG) {
950            Log.v(TAG, "onPublishTerminated: publishId=" + publishId + ", status=" + status);
951        }
952
953        WifiNanSessionState session = getNanSessionStateForPubSubId(publishId);
954        if (session == null) {
955            Log.e(TAG, "onPublishTerminated: no session found for publishId=" + publishId);
956            return;
957        }
958
959        session.onPublishTerminated(status);
960    }
961
962    private void onSubscribeSuccessLocal(short transactionId, int subscribeId) {
963        if (VDBG) {
964            Log.v(TAG, "onSubscribeSuccess: transactionId=" + transactionId + ", subscribeId="
965                    + subscribeId);
966        }
967
968        TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
969        if (info == null) {
970            Log.e(TAG,
971                    "onSubscribeSuccess(): no info registered for transactionId=" + transactionId);
972            return;
973        }
974        if (!(info instanceof TransactionInfoSession)) {
975            Log.e(TAG, "onSubscribeSuccess: invalid info structure stored for transactionId="
976                    + transactionId);
977            return;
978        }
979        TransactionInfoSession infoSession = (TransactionInfoSession) info;
980
981        infoSession.mSession.onSubscribeSuccess(subscribeId);
982    }
983
984    private void onSubscribeFailLocal(short transactionId, int status) {
985        if (VDBG) {
986            Log.v(TAG, "onSubscribeFail: transactionId=" + transactionId + ", status=" + status);
987        }
988
989        TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
990        if (info == null) {
991            Log.e(TAG, "onSubscribeFail(): no info registered for transactionId=" + transactionId);
992            return;
993        }
994        if (!(info instanceof TransactionInfoSession)) {
995            Log.e(TAG, "onSubscribeFail: invalid info structure stored for transactionId="
996                    + transactionId);
997            return;
998        }
999        TransactionInfoSession infoSession = (TransactionInfoSession) info;
1000
1001        infoSession.mSession.onSubscribeFail(status);
1002    }
1003
1004    private void onSubscribeTerminatedLocal(int subscribeId, int status) {
1005        if (VDBG) {
1006            Log.v(TAG, "onPublishTerminated: subscribeId=" + subscribeId + ", status=" + status);
1007        }
1008
1009        WifiNanSessionState session = getNanSessionStateForPubSubId(subscribeId);
1010        if (session == null) {
1011            Log.e(TAG, "onSubscribeTerminated: no session found for subscribeId=" + subscribeId);
1012            return;
1013        }
1014
1015        session.onSubscribeTerminated(status);
1016    }
1017
1018    private void onMessageSendSuccessLocal(short transactionId) {
1019        if (VDBG) {
1020            Log.v(TAG, "onMessageSendSuccess: transactionId=" + transactionId);
1021        }
1022
1023        TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
1024        if (info == null) {
1025            Log.e(TAG, "onMessageSendSuccess(): no info registered for transactionId="
1026                    + transactionId);
1027            return;
1028        }
1029        if (!(info instanceof TransactionInfoMessage)) {
1030            Log.e(TAG, "onMessageSendSuccess: invalid info structure stored for transactionId="
1031                    + transactionId);
1032            return;
1033        }
1034        TransactionInfoMessage infoMessage = (TransactionInfoMessage) info;
1035
1036        infoMessage.mSession.onMessageSendSuccess(infoMessage.mMessageId);
1037    }
1038
1039    private void onMessageSendFailLocal(short transactionId, int status) {
1040        if (VDBG) {
1041            Log.v(TAG, "onMessageSendFail: transactionId=" + transactionId + ", status=" + status);
1042        }
1043
1044        TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
1045        if (info == null) {
1046            Log.e(TAG,
1047                    "onMessageSendFail(): no info registered for transactionId=" + transactionId);
1048            return;
1049        }
1050        if (!(info instanceof TransactionInfoMessage)) {
1051            Log.e(TAG, "onMessageSendFail: invalid info structure stored for transactionId="
1052                    + transactionId);
1053            return;
1054        }
1055        TransactionInfoMessage infoMessage = (TransactionInfoMessage) info;
1056
1057        infoMessage.mSession.onMessageSendFail(infoMessage.mMessageId, status);
1058    }
1059
1060    private void onUnknownTransactionLocal(int responseType, short transactionId, int status) {
1061        Log.e(TAG, "onUnknownTransaction: responseType=" + responseType + ", transactionId="
1062                + transactionId + ", status=" + status);
1063
1064        TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
1065        if (info == null) {
1066            Log.e(TAG, "onUnknownTransaction(): no info registered for transactionId="
1067                    + transactionId);
1068        }
1069    }
1070
1071    private void onMatchLocal(int pubSubId, int requestorInstanceId, byte[] peerMac,
1072            byte[] serviceSpecificInfo, int serviceSpecificInfoLength, byte[] matchFilter,
1073            int matchFilterLength) {
1074        if (VDBG) {
1075            Log.v(TAG, "onMatch: pubSubId=" + pubSubId + ", requestorInstanceId="
1076                    + requestorInstanceId + ", peerMac="
1077                    + String.valueOf(HexEncoding.encode(peerMac)) + ", serviceSpecificInfoLength="
1078                    + serviceSpecificInfoLength + ", serviceSpecificInfo=" + serviceSpecificInfo
1079                    + ", matchFilterLength=" + matchFilterLength + ", matchFilter=" + matchFilter);
1080        }
1081
1082        WifiNanSessionState session = getNanSessionStateForPubSubId(pubSubId);
1083        if (session == null) {
1084            Log.e(TAG, "onMatch: no session found for pubSubId=" + pubSubId);
1085            return;
1086        }
1087
1088        session.onMatch(requestorInstanceId, peerMac, serviceSpecificInfo,
1089                serviceSpecificInfoLength, matchFilter, matchFilterLength);
1090    }
1091
1092    private void onMessageReceivedLocal(int pubSubId, int requestorInstanceId, byte[] peerMac,
1093            byte[] message, int messageLength) {
1094        if (VDBG) {
1095            Log.v(TAG,
1096                    "onMessageReceived: pubSubId=" + pubSubId + ", requestorInstanceId="
1097                            + requestorInstanceId + ", peerMac="
1098                            + String.valueOf(HexEncoding.encode(peerMac)) + ", messageLength="
1099                            + messageLength);
1100        }
1101
1102        WifiNanSessionState session = getNanSessionStateForPubSubId(pubSubId);
1103        if (session == null) {
1104            Log.e(TAG, "onMessageReceived: no session found for pubSubId=" + pubSubId);
1105            return;
1106        }
1107
1108        session.onMessageReceived(requestorInstanceId, peerMac, message, messageLength);
1109    }
1110
1111    private ConfigRequest mergeConfigRequests() {
1112        if (VDBG) {
1113            Log.v(TAG, "mergeConfigRequests(): mClients=[" + mClients + "]");
1114        }
1115
1116        if (mClients.size() == 0) {
1117            Log.e(TAG, "mergeConfigRequests: invalid state - called with 0 clients registered!");
1118            return null;
1119        }
1120
1121        if (mClients.size() == 1) {
1122            return mClients.valueAt(0).getConfigRequest();
1123        }
1124
1125        // TODO: continue working on merge algorithm:
1126        // - if any request 5g: enable
1127        // - maximal master preference
1128        // - cluster range covering all requests: assume that [0,max] is a
1129        // non-request
1130        boolean support5gBand = false;
1131        int masterPreference = 0;
1132        boolean clusterIdValid = false;
1133        int clusterLow = 0;
1134        int clusterHigh = ConfigRequest.CLUSTER_ID_MAX;
1135        for (int i = 0; i < mClients.size(); ++i) {
1136            ConfigRequest cr = mClients.valueAt(i).getConfigRequest();
1137
1138            if (cr.mSupport5gBand) {
1139                support5gBand = true;
1140            }
1141
1142            masterPreference = Math.max(masterPreference, cr.mMasterPreference);
1143
1144            if (cr.mClusterLow != 0 || cr.mClusterHigh != ConfigRequest.CLUSTER_ID_MAX) {
1145                if (!clusterIdValid) {
1146                    clusterLow = cr.mClusterLow;
1147                    clusterHigh = cr.mClusterHigh;
1148                } else {
1149                    clusterLow = Math.min(clusterLow, cr.mClusterLow);
1150                    clusterHigh = Math.max(clusterHigh, cr.mClusterHigh);
1151                }
1152                clusterIdValid = true;
1153            }
1154        }
1155        ConfigRequest.Builder builder = new ConfigRequest.Builder();
1156        builder.setSupport5gBand(support5gBand).setMasterPreference(masterPreference)
1157                .setClusterLow(clusterLow).setClusterHigh(clusterHigh);
1158
1159        return builder.build();
1160    }
1161
1162    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1163        pw.println("NanStateManager:");
1164        pw.println("  mClients: [" + mClients + "]");
1165        pw.println("  mPendingResponses: [" + mPendingResponses + "]");
1166        pw.println("  mCapabilities: [" + mCapabilities + "]");
1167        pw.println("  mNextTransactionId: " + mNextTransactionId);
1168        for (int i = 0; i < mClients.size(); ++i) {
1169            mClients.valueAt(i).dump(fd, pw, args);
1170        }
1171    }
1172}
1173