Home | History | Annotate | Download | only in wifi
      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 
     17 package com.googlecode.android_scripting.facade.wifi;
     18 
     19 import android.app.Service;
     20 import android.content.BroadcastReceiver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.IntentFilter;
     24 import android.content.pm.PackageManager;
     25 import android.net.MacAddress;
     26 import android.net.wifi.aware.PeerHandle;
     27 import android.net.wifi.rtt.RangingRequest;
     28 import android.net.wifi.rtt.RangingResult;
     29 import android.net.wifi.rtt.RangingResultCallback;
     30 import android.net.wifi.rtt.WifiRttManager;
     31 import android.os.Bundle;
     32 import android.os.Parcelable;
     33 import android.os.RemoteException;
     34 import android.os.WorkSource;
     35 
     36 import com.android.internal.annotations.GuardedBy;
     37 
     38 import libcore.util.HexEncoding;
     39 
     40 import com.googlecode.android_scripting.facade.EventFacade;
     41 import com.googlecode.android_scripting.facade.FacadeManager;
     42 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
     43 import com.googlecode.android_scripting.rpc.Rpc;
     44 import com.googlecode.android_scripting.rpc.RpcOptional;
     45 import com.googlecode.android_scripting.rpc.RpcParameter;
     46 
     47 import org.json.JSONArray;
     48 import org.json.JSONException;
     49 
     50 import java.util.List;
     51 
     52 /**
     53  * Facade for RTTv2 manager.
     54  */
     55 public class WifiRtt2ManagerFacade extends RpcReceiver {
     56     private final Service mService;
     57     private final EventFacade mEventFacade;
     58     private final StateChangedReceiver mStateChangedReceiver;
     59 
     60     private final Object mLock = new Object(); // lock access to the following vars
     61 
     62     @GuardedBy("mLock")
     63     private WifiRttManager mMgr;
     64 
     65     @GuardedBy("mLock")
     66     private int mNextRangingResultCallbackId = 1;
     67 
     68     public WifiRtt2ManagerFacade(FacadeManager manager) {
     69         super(manager);
     70         mService = manager.getService();
     71         mEventFacade = manager.getReceiver(EventFacade.class);
     72 
     73         mMgr = (WifiRttManager) mService.getSystemService(Context.WIFI_RTT_RANGING_SERVICE);
     74 
     75         mStateChangedReceiver = new StateChangedReceiver();
     76         IntentFilter filter = new IntentFilter(WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED);
     77         mService.registerReceiver(mStateChangedReceiver, filter);
     78     }
     79 
     80     @Override
     81     public void shutdown() {
     82         // empty
     83     }
     84 
     85     @Rpc(description = "Does the device support the Wi-Fi RTT feature?")
     86     public Boolean doesDeviceSupportWifiRttFeature() {
     87         return mService.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT);
     88     }
     89 
     90     @Rpc(description = "Is Wi-Fi RTT Usage Enabled?")
     91     public Boolean wifiIsRttAvailable() throws RemoteException {
     92         synchronized (mLock) {
     93             return mMgr.isAvailable();
     94         }
     95     }
     96 
     97     @Rpc(description = "The maximum number of peers permitted in a single RTT request")
     98     public Integer wifiRttMaxPeersInRequest() {
     99       return RangingRequest.getMaxPeers();
    100     }
    101 
    102     /**
    103      * Start Wi-Fi RTT ranging to an Acccess Point using the given scan results. Returns the id
    104      * associated with the listener used for ranging. The ranging result event will be decorated
    105      * with the listener id.
    106      */
    107     @Rpc(description = "Start ranging to an Access Points.", returns = "Id of the listener "
    108             + "associated with the started ranging.")
    109     public Integer wifiRttStartRangingToAccessPoints(
    110             @RpcParameter(name = "scanResults") JSONArray scanResults,
    111             @RpcParameter(name = "uidsOverride", description = "Overrides for the UID")
    112                 @RpcOptional JSONArray uidsOverride) throws JSONException {
    113         synchronized (mLock) {
    114             int id = mNextRangingResultCallbackId++;
    115             RangingResultCallback callback = new RangingResultCallbackFacade(id);
    116             mMgr.startRanging(getWorkSource(uidsOverride),
    117                     new RangingRequest.Builder().addAccessPoints(
    118                             WifiJsonParser.getScanResults(scanResults)).build(),
    119                     mService.getMainExecutor(), callback);
    120             return id;
    121         }
    122     }
    123 
    124     @Rpc(description = "Start ranging to an Aware peer.", returns = "Id of the listener "
    125             + "associated with the started ranging.")
    126     public Integer wifiRttStartRangingToAwarePeerMac(
    127             @RpcParameter(name = "peerMac") String peerMac,
    128             @RpcParameter(name = "uidsOverride", description = "Overrides for the UID")
    129             @RpcOptional JSONArray uidsOverride) throws JSONException {
    130         synchronized (mLock) {
    131             int id = mNextRangingResultCallbackId++;
    132             RangingResultCallback callback = new RangingResultCallbackFacade(id);
    133             byte[] peerMacBytes = HexEncoding.decode(peerMac); // since Aware returns string w/o ":"
    134             mMgr.startRanging(getWorkSource(uidsOverride),
    135                     new RangingRequest.Builder().addWifiAwarePeer(
    136                             MacAddress.fromBytes(peerMacBytes)).build(), mService.getMainExecutor(),
    137                     callback);
    138             return id;
    139         }
    140     }
    141 
    142     @Rpc(description = "Start ranging to an Aware peer.", returns = "Id of the listener "
    143             + "associated with the started ranging.")
    144     public Integer wifiRttStartRangingToAwarePeerId(
    145             @RpcParameter(name = "peer ID") Integer peerId,
    146             @RpcParameter(name = "uidsOverride", description = "Overrides for the UID")
    147             @RpcOptional JSONArray uidsOverride) throws JSONException {
    148         synchronized (mLock) {
    149             int id = mNextRangingResultCallbackId++;
    150             RangingResultCallback callback = new RangingResultCallbackFacade(id);
    151             mMgr.startRanging(getWorkSource(uidsOverride),
    152                     new RangingRequest.Builder().addWifiAwarePeer(
    153                             new PeerHandle(peerId)).build(), mService.getMainExecutor(), callback);
    154             return id;
    155         }
    156     }
    157 
    158     @Rpc(description = "Cancel ranging requests for the specified UIDs")
    159     public void wifiRttCancelRanging(@RpcParameter(name = "uids", description = "List of UIDs")
    160         @RpcOptional JSONArray uids) throws JSONException {
    161         synchronized (mLock) {
    162             mMgr.cancelRanging(getWorkSource(uids));
    163         }
    164     }
    165 
    166     private class RangingResultCallbackFacade extends RangingResultCallback {
    167         private int mCallbackId;
    168 
    169         RangingResultCallbackFacade(int callbackId) {
    170             mCallbackId = callbackId;
    171         }
    172 
    173         @Override
    174         public void onRangingFailure(int status) {
    175             Bundle msg = new Bundle();
    176             msg.putInt("status", status);
    177             mEventFacade.postEvent("WifiRttRangingFailure_" + mCallbackId, msg);
    178         }
    179 
    180         @Override
    181         public void onRangingResults(List<RangingResult> results) {
    182             Bundle msg = new Bundle();
    183             Parcelable[] resultBundles = new Parcelable[results.size()];
    184             for (int i = 0; i < results.size(); i++) {
    185                 resultBundles[i] = packRttResult(results.get(i));
    186             }
    187             msg.putParcelableArray("Results", resultBundles);
    188             mEventFacade.postEvent("WifiRttRangingResults_" + mCallbackId, msg);
    189         }
    190     }
    191 
    192     // conversion utilities
    193     private static Bundle packRttResult(RangingResult result) {
    194         Bundle bundle = new Bundle();
    195         bundle.putInt("status", result.getStatus());
    196         if (result.getStatus() == RangingResult.STATUS_SUCCESS) { // only valid on SUCCESS
    197             bundle.putInt("distanceMm", result.getDistanceMm());
    198             bundle.putInt("distanceStdDevMm", result.getDistanceStdDevMm());
    199             bundle.putInt("rssi", result.getRssi());
    200             bundle.putInt("numAttemptedMeasurements", result.getNumAttemptedMeasurements());
    201             bundle.putInt("numSuccessfulMeasurements", result.getNumSuccessfulMeasurements());
    202             bundle.putByteArray("lci", result.getLci());
    203             bundle.putByteArray("lcr", result.getLcr());
    204             bundle.putLong("timestamp", result.getRangingTimestampMillis());
    205         }
    206         if (result.getPeerHandle() != null) {
    207             bundle.putInt("peerId", result.getPeerHandle().peerId);
    208         }
    209         if (result.getMacAddress() != null) {
    210             bundle.putByteArray("mac", result.getMacAddress().toByteArray());
    211             bundle.putString("macAsString", result.getMacAddress().toString());
    212         }
    213         return bundle;
    214     }
    215 
    216     private static WorkSource getWorkSource(JSONArray uids) throws JSONException {
    217         if (uids == null) {
    218             return null;
    219         }
    220         WorkSource ws = new WorkSource();
    221         for (int i = 0; i < uids.length(); ++i) {
    222             ws.add(uids.getInt(i));
    223         }
    224         return ws;
    225     }
    226 
    227     class StateChangedReceiver extends BroadcastReceiver {
    228         @Override
    229         public void onReceive(Context c, Intent intent) {
    230             boolean isAvailable = mMgr.isAvailable();
    231             mEventFacade.postEvent(isAvailable ? "WifiRttAvailable" : "WifiRttNotAvailable",
    232                     new Bundle());
    233         }
    234     }
    235 }
    236