1/*
2 * Copyright (C) 2017 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.googlecode.android_scripting.facade.wifi;
18
19import android.app.Service;
20import android.content.Context;
21import android.net.wifi.RttManager;
22import android.net.wifi.RttManager.ResponderCallback;
23import android.net.wifi.RttManager.ResponderConfig;
24import android.net.wifi.RttManager.RttCapabilities;
25import android.net.wifi.RttManager.RttListener;
26import android.net.wifi.RttManager.RttParams;
27import android.net.wifi.RttManager.RttResult;
28import android.os.Bundle;
29import android.os.Parcelable;
30
31import com.googlecode.android_scripting.facade.EventFacade;
32import com.googlecode.android_scripting.facade.FacadeManager;
33import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
34import com.googlecode.android_scripting.rpc.Rpc;
35import com.googlecode.android_scripting.rpc.RpcParameter;
36
37import org.json.JSONArray;
38import org.json.JSONException;
39import org.json.JSONObject;
40
41import java.util.ArrayList;
42import java.util.HashMap;
43import java.util.Hashtable;
44import java.util.Map;
45
46/**
47 * WifiRttManager functions.
48 */
49public class WifiRttManagerFacade extends RpcReceiver {
50    private final Service mService;
51    private final RttManager mRtt;
52    private final EventFacade mEventFacade;
53    private final Map<Integer, RttListener> mRangingListeners;
54    private final Map<Integer, RttResponderCallback> mResponderCallbacks;
55
56    public WifiRttManagerFacade(FacadeManager manager) {
57        super(manager);
58        mService = manager.getService();
59        mRtt = (RttManager) mService.getSystemService(Context.WIFI_RTT_SERVICE);
60        mEventFacade = manager.getReceiver(EventFacade.class);
61        mRangingListeners = new Hashtable<Integer, RttListener>();
62        mResponderCallbacks = new HashMap<>();
63    }
64
65    public static class RangingListener implements RttListener {
66        private static final String TAG = "WifiRttRanging";
67        private static int sCount = 0;
68        private final EventFacade mEventFacade;
69        public final int mId;
70
71        public RangingListener(EventFacade eventFacade) {
72            sCount += 1;
73            mId = sCount;
74            mEventFacade = eventFacade;
75        }
76
77        public static Bundle packRttResult(RttResult result) {
78            Bundle rttResult = new Bundle();
79            rttResult.putString("BSSID", result.bssid);
80            rttResult.putInt("txRate", result.txRate);
81            rttResult.putInt("rxRate", result.rxRate);
82            rttResult.putInt("distance", result.distance);
83            rttResult.putInt("distanceStandardDeviation",
84                    result.distanceStandardDeviation);
85            rttResult.putInt("distanceSpread", result.distanceSpread);
86            rttResult.putInt("burstDuration", result.burstDuration);
87            rttResult.putLong("rtt", result.rtt);
88            rttResult.putLong("rttStandardDeviation",
89                    result.rttStandardDeviation);
90            rttResult.putLong("rttSpread", result.rttSpread);
91            rttResult.putLong("ts", result.ts);
92            rttResult.putInt("rssi", result.rssi);
93            rttResult.putInt("rssiSpread", result.rssiSpread);
94            rttResult.putInt("retryAfterDuration", result.retryAfterDuration);
95            rttResult.putInt("measurementType", result.measurementType);
96            rttResult.putInt("status", result.status);
97            rttResult.putInt("frameNumberPerBurstPeer",
98                    result.frameNumberPerBurstPeer);
99            rttResult.putInt("successMeasurementFrameNumber",
100                    result.successMeasurementFrameNumber);
101            rttResult.putInt("measurementFrameNumber",
102                    result.measurementFrameNumber);
103            rttResult.putInt("burstNumber", result.burstNumber);
104            rttResult.putInt("status", result.status);
105            return rttResult;
106        }
107
108        @Override
109        public void onSuccess(RttResult[] results) {
110            if (results == null) {
111                mEventFacade
112                        .postEvent(RangingListener.TAG + mId + "onSuccess", null);
113                return;
114            }
115            Bundle msg = new Bundle();
116            Parcelable[] resultBundles = new Parcelable[results.length];
117            for (int i = 0; i < results.length; i++) {
118                resultBundles[i] = packRttResult(results[i]);
119            }
120            msg.putParcelableArray("Results", resultBundles);
121            mEventFacade
122                    .postEvent(RangingListener.TAG + mId + "onSuccess", msg);
123        }
124
125        @Override
126        public void onFailure(int reason, String description) {
127            Bundle msg = new Bundle();
128            msg.putInt("Reason", reason);
129            msg.putString("Description", description);
130            mEventFacade
131                    .postEvent(RangingListener.TAG + mId + "onFailure", msg);
132        }
133
134        @Override
135        public void onAborted() {
136            mEventFacade.postEvent(RangingListener.TAG + mId + "onAborted",
137                    new Bundle());
138        }
139    }
140
141    /**
142     * A {@link ResponderCallback} that handles success and failures for enabling RTT responder
143     * mode.
144     */
145    private static class RttResponderCallback extends ResponderCallback {
146        private static final String TAG = "WifiRtt";
147
148        // A monotonic increasing counter for responder callback ids.
149        private static int sCount = 0;
150
151        private final int mId;
152        private final EventFacade mEventFacade;
153
154        RttResponderCallback(EventFacade eventFacade) {
155            sCount++;
156            mId = sCount;
157            mEventFacade = eventFacade;
158        }
159
160        @Override
161        public void onResponderEnabled(ResponderConfig config) {
162            Bundle bundle = new Bundle();
163            bundle.putString("macAddress", config.macAddress);
164            bundle.putInt("frequency", config.frequency);
165            bundle.putInt("centerFreq0", config.centerFreq0);
166            bundle.putInt("centerFreq1", config.centerFreq1);
167            bundle.putInt("channelWidth", config.channelWidth);
168            bundle.putInt("preamble", config.preamble);
169            mEventFacade.postEvent(TAG + mId + "onResponderEnabled", bundle);
170        }
171
172        @Override
173        public void onResponderEnableFailure(int reason) {
174            Bundle bundle = new Bundle();
175            bundle.putInt("reason", reason);
176            mEventFacade.postEvent(TAG + mId + "onResponderEnableFailure", bundle);
177        }
178    }
179
180    @Rpc(description = "Get wifi Rtt capabilities.")
181    public RttCapabilities wifiRttGetCapabilities() {
182        return mRtt.getRttCapabilities();
183    }
184
185    public static RttParams parseRttParam(JSONObject j) throws JSONException {
186        RttParams result = new RttParams();
187        if (j.has("deviceType")) {
188            result.deviceType = j.getInt("deviceType");
189        }
190        if (j.has("requestType")) {
191            result.requestType = j.getInt("requestType");
192        }
193        if (j.has("bssid")) {
194            result.bssid = j.getString("bssid");
195        }
196        if (j.has("frequency")) {
197            result.frequency = j.getInt("frequency");
198        }
199        if (j.has("channelWidth")) {
200            result.channelWidth = j.getInt("channelWidth");
201        }
202        if (j.has("centerFreq0")) {
203            result.centerFreq0 = j.getInt("centerFreq0");
204        }
205        if (j.has("centerFreq1")) {
206            result.centerFreq1 = j.getInt("centerFreq1");
207        }
208        if (j.has("numberBurst")) {
209            result.numberBurst = j.getInt("numberBurst");
210        }
211        if (j.has("burstTimeout")) {
212            result.burstTimeout = j.getInt("burstTimeout");
213        }
214        if (j.has("interval")) {
215            result.interval = j.getInt("interval");
216        }
217        if (j.has("numSamplesPerBurst")) {
218            result.numSamplesPerBurst = j.getInt("numSamplesPerBurst");
219        }
220        if (j.has("numRetriesPerMeasurementFrame")) {
221            result.numRetriesPerMeasurementFrame = j
222                    .getInt("numRetriesPerMeasurementFrame");
223        }
224        if (j.has("numRetriesPerFTMR")) {
225            result.numRetriesPerFTMR = j.getInt("numRetriesPerFTMR");
226        }
227        if (j.has("LCIRequest")) {
228            result.LCIRequest = j.getBoolean("LCIRequest");
229        }
230        if (j.has("LCRRequest")) {
231            result.LCRRequest = j.getBoolean("LCRRequest");
232        }
233        if (j.has("preamble")) {
234            result.preamble = j.getInt("preamble");
235        }
236        if (j.has("bandwidth")) {
237            result.bandwidth = j.getInt("bandwidth");
238        }
239        return result;
240    }
241
242    /**
243     * Start WiFi RTT ranging using the given params. Returns the id associated with the
244     * {@link RttListener} used for ranging.
245     */
246    @Rpc(description = "Start ranging.", returns = "Id of the listener associated with the "
247            + "started ranging.")
248    public Integer wifiRttStartRanging(
249            @RpcParameter(name = "params") JSONArray params)
250            throws JSONException {
251        RttParams[] rParams = new RttParams[params.length()];
252        for (int i = 0; i < params.length(); i++) {
253            rParams[i] = parseRttParam(params.getJSONObject(i));
254        }
255        RangingListener listener = new RangingListener(mEventFacade);
256        mRangingListeners.put(listener.mId, listener);
257        mRtt.startRanging(rParams, listener);
258        return listener.mId;
259    }
260
261    /**
262     * Stop WiFi Rtt ranging for {@link RttListener} identified by the given {@code index}.
263     */
264    @Rpc(description = "Stop ranging.")
265    public void wifiRttStopRanging(@RpcParameter(name = "index") Integer index) {
266        mRtt.stopRanging(mRangingListeners.remove(index));
267    }
268
269    /**
270     * Enable WiFi RTT responder role. Returns the id associated with the {@link ResponderCallback}
271     * used for enabling responder.
272     */
273    @Rpc(description = "Enable responder", returns = "Id of the callback associated with enabling")
274    public Integer wifiRttEnableResponder() {
275        RttResponderCallback callback = new RttResponderCallback(mEventFacade);
276        mResponderCallbacks.put(callback.mId, callback);
277        mRtt.enableResponder(callback);
278        return callback.mId;
279    }
280
281    /**
282     * Disable WiFi RTT responder role for the {@link ResponderCallback} identified by the given
283     * {@code index}.
284     */
285    @Rpc(description = "Disable responder")
286    public void wifiRttDisableResponder(@RpcParameter(name = "index") Integer index) {
287        mRtt.disableResponder(mResponderCallbacks.remove(index));
288    }
289
290    @Override
291    public void shutdown() {
292        ArrayList<Integer> keys = new ArrayList<Integer>(
293                mRangingListeners.keySet());
294        for (int k : keys) {
295            wifiRttStopRanging(k);
296        }
297        for (int index : mResponderCallbacks.keySet()) {
298            wifiRttDisableResponder(index);
299        }
300    }
301}
302