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.IWifiNanSessionListener;
20import android.net.wifi.nan.PublishData;
21import android.net.wifi.nan.PublishSettings;
22import android.net.wifi.nan.SubscribeData;
23import android.net.wifi.nan.SubscribeSettings;
24import android.net.wifi.nan.WifiNanSessionListener;
25import android.os.RemoteException;
26import android.util.Log;
27import android.util.SparseArray;
28
29import libcore.util.HexEncoding;
30
31import java.io.FileDescriptor;
32import java.io.PrintWriter;
33
34public class WifiNanSessionState {
35    private static final String TAG = "WifiNanSessionState";
36    private static final boolean DBG = false;
37    private static final boolean VDBG = false; // STOPSHIP if true
38
39    private final SparseArray<String> mMacByRequestorInstanceId = new SparseArray<>();
40
41    private int mSessionId;
42    private IWifiNanSessionListener mListener;
43    private int mEvents;
44
45    private boolean mPubSubIdValid = false;
46    private int mPubSubId;
47
48    private static final int SESSION_TYPE_NOT_INIT = 0;
49    private static final int SESSION_TYPE_PUBLISH = 1;
50    private static final int SESSION_TYPE_SUBSCRIBE = 2;
51    private int mSessionType = SESSION_TYPE_NOT_INIT;
52
53    public WifiNanSessionState(int sessionId, IWifiNanSessionListener listener, int events) {
54        mSessionId = sessionId;
55        mListener = listener;
56        mEvents = events;
57    }
58
59    public void destroy() {
60        stop(WifiNanStateManager.getInstance().createNextTransactionId());
61        if (mPubSubIdValid) {
62            mMacByRequestorInstanceId.clear();
63            mListener = null;
64            mPubSubIdValid = false;
65        }
66    }
67
68    public int getSessionId() {
69        return mSessionId;
70    }
71
72    public boolean isPubSubIdSession(int pubSubId) {
73        return mPubSubIdValid && mPubSubId == pubSubId;
74    }
75
76    public void publish(short transactionId, PublishData data, PublishSettings settings) {
77        if (mSessionType == SESSION_TYPE_SUBSCRIBE) {
78            throw new IllegalStateException("A SUBSCRIBE session is being used for publish");
79        }
80        mSessionType = SESSION_TYPE_PUBLISH;
81
82        WifiNanNative.getInstance().publish(transactionId, mPubSubIdValid ? mPubSubId : 0, data,
83                settings);
84    }
85
86    public void subscribe(short transactionId, SubscribeData data, SubscribeSettings settings) {
87        if (mSessionType == SESSION_TYPE_PUBLISH) {
88            throw new IllegalStateException("A PUBLISH session is being used for publish");
89        }
90        mSessionType = SESSION_TYPE_SUBSCRIBE;
91
92        WifiNanNative.getInstance().subscribe(transactionId, mPubSubIdValid ? mPubSubId : 0, data,
93                settings);
94    }
95
96    public void sendMessage(short transactionId, int peerId, byte[] message, int messageLength,
97            int messageId) {
98        if (!mPubSubIdValid) {
99            Log.e(TAG, "sendMessage: attempting to send a message on a non-live session "
100                    + "(no successful publish or subscribe");
101            onMessageSendFail(messageId, WifiNanSessionListener.FAIL_REASON_NO_MATCH_SESSION);
102            return;
103        }
104
105        String peerMacStr = mMacByRequestorInstanceId.get(peerId);
106        if (peerMacStr == null) {
107            Log.e(TAG, "sendMessage: attempting to send a message to an address which didn't "
108                    + "match/contact us");
109            onMessageSendFail(messageId, WifiNanSessionListener.FAIL_REASON_NO_MATCH_SESSION);
110            return;
111        }
112        byte[] peerMac = HexEncoding.decode(peerMacStr.toCharArray(), false);
113
114        WifiNanNative.getInstance().sendMessage(transactionId, mPubSubId, peerId, peerMac, message,
115                messageLength);
116    }
117
118    public void stop(short transactionId) {
119        if (!mPubSubIdValid || mSessionType == SESSION_TYPE_NOT_INIT) {
120            Log.e(TAG, "sendMessage: attempting to stop pub/sub on a non-live session (no "
121                    + "successful publish or subscribe");
122            return;
123        }
124
125        if (mSessionType == SESSION_TYPE_PUBLISH) {
126            WifiNanNative.getInstance().stopPublish(transactionId, mPubSubId);
127        } else if (mSessionType == SESSION_TYPE_SUBSCRIBE) {
128            WifiNanNative.getInstance().stopSubscribe(transactionId, mPubSubId);
129        }
130    }
131
132    public void onPublishSuccess(int publishId) {
133        mPubSubId = publishId;
134        mPubSubIdValid = true;
135    }
136
137    public void onPublishFail(int status) {
138        mPubSubIdValid = false;
139        try {
140            if (mListener != null && (mEvents & WifiNanSessionListener.LISTEN_PUBLISH_FAIL) != 0) {
141                mListener.onPublishFail(status);
142            }
143        } catch (RemoteException e) {
144            Log.w(TAG, "onPublishFail: RemoteException (FYI): " + e);
145        }
146    }
147
148    public void onPublishTerminated(int status) {
149        mPubSubIdValid = false;
150        try {
151            if (mListener != null
152                    && (mEvents & WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED) != 0) {
153                mListener.onPublishTerminated(status);
154            }
155        } catch (RemoteException e) {
156            Log.w(TAG, "onPublishTerminated: RemoteException (FYI): " + e);
157        }
158    }
159
160    public void onSubscribeSuccess(int subscribeId) {
161        mPubSubId = subscribeId;
162        mPubSubIdValid = true;
163    }
164
165    public void onSubscribeFail(int status) {
166        mPubSubIdValid = false;
167        try {
168            if (mListener != null
169                    && (mEvents & WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL) != 0) {
170                mListener.onSubscribeFail(status);
171            }
172        } catch (RemoteException e) {
173            Log.w(TAG, "onSubscribeFail: RemoteException (FYI): " + e);
174        }
175    }
176
177    public void onSubscribeTerminated(int status) {
178        mPubSubIdValid = false;
179        try {
180            if (mListener != null
181                    && (mEvents & WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED) != 0) {
182                mListener.onSubscribeTerminated(status);
183            }
184        } catch (RemoteException e) {
185            Log.w(TAG, "onSubscribeTerminated: RemoteException (FYI): " + e);
186        }
187    }
188
189    public void onMessageSendSuccess(int messageId) {
190        try {
191            if (mListener != null
192                    && (mEvents & WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS) != 0) {
193                mListener.onMessageSendSuccess(messageId);
194            }
195        } catch (RemoteException e) {
196            Log.w(TAG, "onMessageSendSuccess: RemoteException (FYI): " + e);
197        }
198    }
199
200    public void onMessageSendFail(int messageId, int status) {
201        try {
202            if (mListener != null
203                    && (mEvents & WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL) != 0) {
204                mListener.onMessageSendFail(messageId, status);
205            }
206        } catch (RemoteException e) {
207            Log.w(TAG, "onMessageSendFail: RemoteException (FYI): " + e);
208        }
209    }
210
211    public void onMatch(int requestorInstanceId, byte[] peerMac, byte[] serviceSpecificInfo,
212            int serviceSpecificInfoLength, byte[] matchFilter, int matchFilterLength) {
213        String prevMac = mMacByRequestorInstanceId.get(requestorInstanceId);
214        mMacByRequestorInstanceId.put(requestorInstanceId, new String(HexEncoding.encode(peerMac)));
215
216        if (DBG) Log.d(TAG, "onMatch: previous peer MAC replaced - " + prevMac);
217
218        try {
219            if (mListener != null && (mEvents & WifiNanSessionListener.LISTEN_MATCH) != 0) {
220                mListener.onMatch(requestorInstanceId, serviceSpecificInfo,
221                        serviceSpecificInfoLength, matchFilter, matchFilterLength);
222            }
223        } catch (RemoteException e) {
224            Log.w(TAG, "onMatch: RemoteException (FYI): " + e);
225        }
226    }
227
228    public void onMessageReceived(int requestorInstanceId, byte[] peerMac, byte[] message,
229            int messageLength) {
230        String prevMac = mMacByRequestorInstanceId.get(requestorInstanceId);
231        mMacByRequestorInstanceId.put(requestorInstanceId, new String(HexEncoding.encode(peerMac)));
232
233        if (DBG) {
234            Log.d(TAG, "onMessageReceived: previous peer MAC replaced - " + prevMac);
235        }
236
237        try {
238            if (mListener != null
239                    && (mEvents & WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED) != 0) {
240                mListener.onMessageReceived(requestorInstanceId, message, messageLength);
241            }
242        } catch (RemoteException e) {
243            Log.w(TAG, "onMessageReceived: RemoteException (FYI): " + e);
244        }
245    }
246
247    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
248        pw.println("NanSessionState:");
249        pw.println("  mSessionId: " + mSessionId);
250        pw.println("  mSessionType: " + mSessionType);
251        pw.println("  mEvents: " + mEvents);
252        pw.println("  mPubSubId: " + (mPubSubIdValid ? Integer.toString(mPubSubId) : "not valid"));
253        pw.println("  mMacByRequestorInstanceId: [" + mMacByRequestorInstanceId + "]");
254    }
255}
256