Home | History | Annotate | Download | only in wifi
      1 package android.net.wifi;
      2 
      3 import android.annotation.SystemApi;
      4 import android.content.Context;
      5 import android.os.Bundle;
      6 import android.os.Handler;
      7 import android.os.HandlerThread;
      8 import android.os.Looper;
      9 import android.os.Message;
     10 import android.os.Messenger;
     11 import android.os.Parcel;
     12 import android.os.Parcelable;
     13 import android.os.RemoteException;
     14 import android.util.Log;
     15 import android.util.SparseArray;
     16 
     17 import com.android.internal.util.AsyncChannel;
     18 import com.android.internal.util.Protocol;
     19 
     20 import java.util.concurrent.CountDownLatch;
     21 
     22 /** @hide */
     23 @SystemApi
     24 public class RttManager {
     25 
     26     private static final boolean DBG = true;
     27     private static final String TAG = "RttManager";
     28 
     29     public static final int RTT_TYPE_UNSPECIFIED    = 0;
     30     public static final int RTT_TYPE_ONE_SIDED      = 1;
     31     public static final int RTT_TYPE_11_V           = 2;
     32     public static final int RTT_TYPE_11_MC          = 4;
     33 
     34     public static final int RTT_PEER_TYPE_UNSPECIFIED    = 0;
     35     public static final int RTT_PEER_TYPE_AP             = 1;
     36     public static final int RTT_PEER_TYPE_STA            = 2;       /* requires NAN */
     37 
     38     public static final int RTT_CHANNEL_WIDTH_20      = 0;
     39     public static final int RTT_CHANNEL_WIDTH_40      = 1;
     40     public static final int RTT_CHANNEL_WIDTH_80      = 2;
     41     public static final int RTT_CHANNEL_WIDTH_160     = 3;
     42     public static final int RTT_CHANNEL_WIDTH_80P80   = 4;
     43     public static final int RTT_CHANNEL_WIDTH_5       = 5;
     44     public static final int RTT_CHANNEL_WIDTH_10      = 6;
     45     public static final int RTT_CHANNEL_WIDTH_UNSPECIFIED = -1;
     46 
     47     public static final int RTT_STATUS_SUCCESS                  = 0;
     48     public static final int RTT_STATUS_FAILURE                  = 1;
     49     public static final int RTT_STATUS_FAIL_NO_RSP              = 2;
     50     public static final int RTT_STATUS_FAIL_REJECTED            = 3;
     51     public static final int RTT_STATUS_FAIL_NOT_SCHEDULED_YET   = 4;
     52     public static final int RTT_STATUS_FAIL_TM_TIMEOUT          = 5;
     53     public static final int RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL  = 6;
     54     public static final int RTT_STATUS_FAIL_NO_CAPABILITY       = 7;
     55     public static final int RTT_STATUS_ABORTED                  = 8;
     56 
     57     public static final int REASON_UNSPECIFIED              = -1;
     58     public static final int REASON_NOT_AVAILABLE            = -2;
     59     public static final int REASON_INVALID_LISTENER         = -3;
     60     public static final int REASON_INVALID_REQUEST          = -4;
     61 
     62     public static final String DESCRIPTION_KEY  = "android.net.wifi.RttManager.Description";
     63 
     64     public class Capabilities {
     65         public int supportedType;
     66         public int supportedPeerType;
     67     }
     68 
     69     public Capabilities getCapabilities() {
     70         return new Capabilities();
     71     }
     72 
     73     /** specifies parameters for RTT request */
     74     public static class RttParams {
     75 
     76         /** type of device being ranged; one of RTT_PEER_TYPE_AP or RTT_PEER_TYPE_STA */
     77         public int deviceType;
     78 
     79         /** type of RTT being sought; one of RTT_TYPE_ONE_SIDED
     80          *  RTT_TYPE_11_V or RTT_TYPE_11_MC or RTT_TYPE_UNSPECIFIED */
     81         public int requestType;
     82 
     83         /** mac address of the device being ranged */
     84         public String bssid;
     85 
     86         /** channel frequency that the device is on; optional */
     87         public int frequency;
     88 
     89         /** optional channel width. wider channels result in better accuracy,
     90          *  but they take longer time, and even get aborted may times; use
     91          *  RTT_CHANNEL_WIDTH_UNSPECIFIED if not specifying */
     92         public int channelWidth;
     93 
     94         /** number of samples to be taken */
     95         public int num_samples;
     96 
     97         /** number of retries if a sample fails */
     98         public int num_retries;
     99     }
    100 
    101     /** pseudo-private class used to parcel arguments */
    102     public static class ParcelableRttParams implements Parcelable {
    103 
    104         public RttParams mParams[];
    105 
    106         ParcelableRttParams(RttParams[] params) {
    107             mParams = params;
    108         }
    109 
    110         /** Implement the Parcelable interface {@hide} */
    111         public int describeContents() {
    112             return 0;
    113         }
    114 
    115         /** Implement the Parcelable interface {@hide} */
    116         public void writeToParcel(Parcel dest, int flags) {
    117             if (mParams != null) {
    118                 dest.writeInt(mParams.length);
    119 
    120                 for (RttParams params : mParams) {
    121                     dest.writeInt(params.deviceType);
    122                     dest.writeInt(params.requestType);
    123                     dest.writeString(params.bssid);
    124                     dest.writeInt(params.frequency);
    125                     dest.writeInt(params.channelWidth);
    126                     dest.writeInt(params.num_samples);
    127                     dest.writeInt(params.num_retries);
    128                 }
    129             } else {
    130                 dest.writeInt(0);
    131             }
    132         }
    133 
    134         /** Implement the Parcelable interface {@hide} */
    135         public static final Creator<ParcelableRttParams> CREATOR =
    136                 new Creator<ParcelableRttParams>() {
    137                     public ParcelableRttParams createFromParcel(Parcel in) {
    138 
    139                         int num = in.readInt();
    140 
    141                         if (num == 0) {
    142                             return new ParcelableRttParams(null);
    143                         }
    144 
    145                         RttParams params[] = new RttParams[num];
    146                         for (int i = 0; i < num; i++) {
    147                             params[i] = new RttParams();
    148                             params[i].deviceType = in.readInt();
    149                             params[i].requestType = in.readInt();
    150                             params[i].bssid = in.readString();
    151                             params[i].frequency = in.readInt();
    152                             params[i].channelWidth = in.readInt();
    153                             params[i].num_samples = in.readInt();
    154                             params[i].num_retries = in.readInt();
    155 
    156                         }
    157 
    158                         ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
    159                         return parcelableParams;
    160                     }
    161 
    162                     public ParcelableRttParams[] newArray(int size) {
    163                         return new ParcelableRttParams[size];
    164                     }
    165                 };
    166     }
    167 
    168     /** specifies RTT results */
    169     public static class RttResult {
    170         /** mac address of the device being ranged */
    171         public String bssid;
    172 
    173         /** status of the request */
    174         public int status;
    175 
    176         /** type of the request used */
    177         public int requestType;
    178 
    179         /** timestamp of completion, in microsecond since boot */
    180         public long ts;
    181 
    182         /** average RSSI observed */
    183         public int rssi;
    184 
    185         /** RSSI spread (i.e. max - min) */
    186         public int rssi_spread;
    187 
    188         /** average transmit rate */
    189         public int tx_rate;
    190 
    191         /** average round trip time in nano second */
    192         public long rtt_ns;
    193 
    194         /** standard deviation observed in round trip time */
    195         public long rtt_sd_ns;
    196 
    197         /** spread (i.e. max - min) round trip time */
    198         public long rtt_spread_ns;
    199 
    200         /** average distance in centimeter, computed based on rtt_ns */
    201         public int distance_cm;
    202 
    203         /** standard deviation observed in distance */
    204         public int distance_sd_cm;
    205 
    206         /** spread (i.e. max - min) distance */
    207         public int distance_spread_cm;
    208     }
    209 
    210 
    211     /** pseudo-private class used to parcel results */
    212     public static class ParcelableRttResults implements Parcelable {
    213 
    214         public RttResult mResults[];
    215 
    216         public ParcelableRttResults(RttResult[] results) {
    217             mResults = results;
    218         }
    219 
    220         /** Implement the Parcelable interface {@hide} */
    221         public int describeContents() {
    222             return 0;
    223         }
    224 
    225         /** Implement the Parcelable interface {@hide} */
    226         public void writeToParcel(Parcel dest, int flags) {
    227             if (mResults != null) {
    228                 dest.writeInt(mResults.length);
    229                 for (RttResult result : mResults) {
    230                     dest.writeString(result.bssid);
    231                     dest.writeInt(result.status);
    232                     dest.writeInt(result.requestType);
    233                     dest.writeLong(result.ts);
    234                     dest.writeInt(result.rssi);
    235                     dest.writeInt(result.rssi_spread);
    236                     dest.writeInt(result.tx_rate);
    237                     dest.writeLong(result.rtt_ns);
    238                     dest.writeLong(result.rtt_sd_ns);
    239                     dest.writeLong(result.rtt_spread_ns);
    240                     dest.writeInt(result.distance_cm);
    241                     dest.writeInt(result.distance_sd_cm);
    242                     dest.writeInt(result.distance_spread_cm);
    243                 }
    244             } else {
    245                 dest.writeInt(0);
    246             }
    247         }
    248 
    249         /** Implement the Parcelable interface {@hide} */
    250         public static final Creator<ParcelableRttResults> CREATOR =
    251                 new Creator<ParcelableRttResults>() {
    252                     public ParcelableRttResults createFromParcel(Parcel in) {
    253 
    254                         int num = in.readInt();
    255 
    256                         if (num == 0) {
    257                             return new ParcelableRttResults(null);
    258                         }
    259 
    260                         RttResult results[] = new RttResult[num];
    261                         for (int i = 0; i < num; i++) {
    262                             results[i] = new RttResult();
    263                             results[i].bssid = in.readString();
    264                             results[i].status = in.readInt();
    265                             results[i].requestType = in.readInt();
    266                             results[i].ts = in.readLong();
    267                             results[i].rssi = in.readInt();
    268                             results[i].rssi_spread = in.readInt();
    269                             results[i].tx_rate = in.readInt();
    270                             results[i].rtt_ns = in.readLong();
    271                             results[i].rtt_sd_ns = in.readLong();
    272                             results[i].rtt_spread_ns = in.readLong();
    273                             results[i].distance_cm = in.readInt();
    274                             results[i].distance_sd_cm = in.readInt();
    275                             results[i].distance_spread_cm = in.readInt();
    276                         }
    277 
    278                         ParcelableRttResults parcelableResults = new ParcelableRttResults(results);
    279                         return parcelableResults;
    280                     }
    281 
    282                     public ParcelableRttResults[] newArray(int size) {
    283                         return new ParcelableRttResults[size];
    284                     }
    285                 };
    286     }
    287 
    288 
    289     public static interface RttListener {
    290         public void onSuccess(RttResult[] results);
    291         public void onFailure(int reason, String description);
    292         public void onAborted();
    293     }
    294 
    295     public void startRanging(RttParams[] params, RttListener listener) {
    296         validateChannel();
    297         ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
    298         sAsyncChannel.sendMessage(CMD_OP_START_RANGING,
    299                 0, putListener(listener), parcelableParams);
    300     }
    301 
    302     public void stopRanging(RttListener listener) {
    303         validateChannel();
    304         sAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
    305     }
    306 
    307     /* private methods */
    308     public static final int BASE = Protocol.BASE_WIFI_RTT_MANAGER;
    309 
    310     public static final int CMD_OP_START_RANGING        = BASE + 0;
    311     public static final int CMD_OP_STOP_RANGING         = BASE + 1;
    312     public static final int CMD_OP_FAILED               = BASE + 2;
    313     public static final int CMD_OP_SUCCEEDED            = BASE + 3;
    314     public static final int CMD_OP_ABORTED              = BASE + 4;
    315 
    316     private Context mContext;
    317     private IRttManager mService;
    318 
    319     private static final int INVALID_KEY = 0;
    320     private static int sListenerKey = 1;
    321 
    322     private static final SparseArray sListenerMap = new SparseArray();
    323     private static final Object sListenerMapLock = new Object();
    324 
    325     private static AsyncChannel sAsyncChannel;
    326     private static CountDownLatch sConnected;
    327 
    328     private static final Object sThreadRefLock = new Object();
    329     private static int sThreadRefCount;
    330     private static HandlerThread sHandlerThread;
    331 
    332     /**
    333      * Create a new WifiScanner instance.
    334      * Applications will almost always want to use
    335      * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
    336      * the standard {@link android.content.Context#WIFI_RTT_SERVICE Context.WIFI_RTT_SERVICE}.
    337      * @param context the application context
    338      * @param service the Binder interface
    339      * @hide
    340      */
    341 
    342     public RttManager(Context context, IRttManager service) {
    343         mContext = context;
    344         mService = service;
    345         init();
    346     }
    347 
    348     private void init() {
    349         synchronized (sThreadRefLock) {
    350             if (++sThreadRefCount == 1) {
    351                 Messenger messenger = null;
    352                 try {
    353                     Log.d(TAG, "Get the messenger from " + mService);
    354                     messenger = mService.getMessenger();
    355                 } catch (RemoteException e) {
    356                     /* do nothing */
    357                 } catch (SecurityException e) {
    358                     /* do nothing */
    359                 }
    360 
    361                 if (messenger == null) {
    362                     sAsyncChannel = null;
    363                     return;
    364                 }
    365 
    366                 sHandlerThread = new HandlerThread("WifiScanner");
    367                 sAsyncChannel = new AsyncChannel();
    368                 sConnected = new CountDownLatch(1);
    369 
    370                 sHandlerThread.start();
    371                 Handler handler = new ServiceHandler(sHandlerThread.getLooper());
    372                 sAsyncChannel.connect(mContext, handler, messenger);
    373                 try {
    374                     sConnected.await();
    375                 } catch (InterruptedException e) {
    376                     Log.e(TAG, "interrupted wait at init");
    377                 }
    378             }
    379         }
    380     }
    381 
    382     private void validateChannel() {
    383         if (sAsyncChannel == null) throw new IllegalStateException(
    384                 "No permission to access and change wifi or a bad initialization");
    385     }
    386 
    387     private static int putListener(Object listener) {
    388         if (listener == null) return INVALID_KEY;
    389         int key;
    390         synchronized (sListenerMapLock) {
    391             do {
    392                 key = sListenerKey++;
    393             } while (key == INVALID_KEY);
    394             sListenerMap.put(key, listener);
    395         }
    396         return key;
    397     }
    398 
    399     private static Object getListener(int key) {
    400         if (key == INVALID_KEY) return null;
    401         synchronized (sListenerMapLock) {
    402             Object listener = sListenerMap.get(key);
    403             return listener;
    404         }
    405     }
    406 
    407     private static int getListenerKey(Object listener) {
    408         if (listener == null) return INVALID_KEY;
    409         synchronized (sListenerMapLock) {
    410             int index = sListenerMap.indexOfValue(listener);
    411             if (index == -1) {
    412                 return INVALID_KEY;
    413             } else {
    414                 return sListenerMap.keyAt(index);
    415             }
    416         }
    417     }
    418 
    419     private static Object removeListener(int key) {
    420         if (key == INVALID_KEY) return null;
    421         synchronized (sListenerMapLock) {
    422             Object listener = sListenerMap.get(key);
    423             sListenerMap.remove(key);
    424             return listener;
    425         }
    426     }
    427 
    428     private static int removeListener(Object listener) {
    429         int key = getListenerKey(listener);
    430         if (key == INVALID_KEY) return key;
    431         synchronized (sListenerMapLock) {
    432             sListenerMap.remove(key);
    433             return key;
    434         }
    435     }
    436 
    437     private static class ServiceHandler extends Handler {
    438         ServiceHandler(Looper looper) {
    439             super(looper);
    440         }
    441         @Override
    442         public void handleMessage(Message msg) {
    443             switch (msg.what) {
    444                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
    445                     if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
    446                         sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
    447                     } else {
    448                         Log.e(TAG, "Failed to set up channel connection");
    449                         // This will cause all further async API calls on the WifiManager
    450                         // to fail and throw an exception
    451                         sAsyncChannel = null;
    452                     }
    453                     sConnected.countDown();
    454                     return;
    455                 case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
    456                     return;
    457                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
    458                     Log.e(TAG, "Channel connection lost");
    459                     // This will cause all further async API calls on the WifiManager
    460                     // to fail and throw an exception
    461                     sAsyncChannel = null;
    462                     getLooper().quit();
    463                     return;
    464             }
    465 
    466             Object listener = getListener(msg.arg2);
    467             if (listener == null) {
    468                 if (DBG) Log.d(TAG, "invalid listener key = " + msg.arg2);
    469                 return;
    470             } else {
    471                 if (DBG) Log.d(TAG, "listener key = " + msg.arg2);
    472             }
    473 
    474             switch (msg.what) {
    475                 /* ActionListeners grouped together */
    476                 case CMD_OP_SUCCEEDED :
    477                     reportSuccess(listener, msg);
    478                     removeListener(msg.arg2);
    479                     break;
    480                 case CMD_OP_FAILED :
    481                     reportFailure(listener, msg);
    482                     removeListener(msg.arg2);
    483                     break;
    484                 case CMD_OP_ABORTED :
    485                     ((RttListener) listener).onAborted();
    486                     removeListener(msg.arg2);
    487                     break;
    488                 default:
    489                     if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
    490                     return;
    491             }
    492         }
    493 
    494         void reportSuccess(Object listener, Message msg) {
    495             RttListener rttListener = (RttListener) listener;
    496             ParcelableRttResults parcelableResults = (ParcelableRttResults) msg.obj;
    497             ((RttListener) listener).onSuccess(parcelableResults.mResults);
    498         }
    499 
    500         void reportFailure(Object listener, Message msg) {
    501             RttListener rttListener = (RttListener) listener;
    502             Bundle bundle = (Bundle) msg.obj;
    503             ((RttListener) listener).onFailure(msg.arg1, bundle.getString(DESCRIPTION_KEY));
    504         }
    505     }
    506 
    507 }
    508 
    509