FileDocCategorySizeDatePackage
RttManager.javaAPI DocAndroid 5.1 API18784Thu Mar 12 22:22:44 GMT 2015android.net.wifi

RttManager.java

package android.net.wifi;

import android.annotation.SystemApi;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;

import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;

import java.util.concurrent.CountDownLatch;

/** @hide */
@SystemApi
public class RttManager {

    private static final boolean DBG = true;
    private static final String TAG = "RttManager";

    public static final int RTT_TYPE_UNSPECIFIED    = 0;
    public static final int RTT_TYPE_ONE_SIDED      = 1;
    public static final int RTT_TYPE_11_V           = 2;
    public static final int RTT_TYPE_11_MC          = 4;

    public static final int RTT_PEER_TYPE_UNSPECIFIED    = 0;
    public static final int RTT_PEER_TYPE_AP             = 1;
    public static final int RTT_PEER_TYPE_STA            = 2;       /* requires NAN */

    public static final int RTT_CHANNEL_WIDTH_20      = 0;
    public static final int RTT_CHANNEL_WIDTH_40      = 1;
    public static final int RTT_CHANNEL_WIDTH_80      = 2;
    public static final int RTT_CHANNEL_WIDTH_160     = 3;
    public static final int RTT_CHANNEL_WIDTH_80P80   = 4;
    public static final int RTT_CHANNEL_WIDTH_5       = 5;
    public static final int RTT_CHANNEL_WIDTH_10      = 6;
    public static final int RTT_CHANNEL_WIDTH_UNSPECIFIED = -1;

    public static final int RTT_STATUS_SUCCESS                  = 0;
    public static final int RTT_STATUS_FAILURE                  = 1;
    public static final int RTT_STATUS_FAIL_NO_RSP              = 2;
    public static final int RTT_STATUS_FAIL_REJECTED            = 3;
    public static final int RTT_STATUS_FAIL_NOT_SCHEDULED_YET   = 4;
    public static final int RTT_STATUS_FAIL_TM_TIMEOUT          = 5;
    public static final int RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL  = 6;
    public static final int RTT_STATUS_FAIL_NO_CAPABILITY       = 7;
    public static final int RTT_STATUS_ABORTED                  = 8;

    public static final int REASON_UNSPECIFIED              = -1;
    public static final int REASON_NOT_AVAILABLE            = -2;
    public static final int REASON_INVALID_LISTENER         = -3;
    public static final int REASON_INVALID_REQUEST          = -4;

    public static final String DESCRIPTION_KEY  = "android.net.wifi.RttManager.Description";

    public class Capabilities {
        public int supportedType;
        public int supportedPeerType;
    }

    public Capabilities getCapabilities() {
        return new Capabilities();
    }

    /** specifies parameters for RTT request */
    public static class RttParams {

        /** type of device being ranged; one of RTT_PEER_TYPE_AP or RTT_PEER_TYPE_STA */
        public int deviceType;

        /** type of RTT being sought; one of RTT_TYPE_ONE_SIDED
         *  RTT_TYPE_11_V or RTT_TYPE_11_MC or RTT_TYPE_UNSPECIFIED */
        public int requestType;

        /** mac address of the device being ranged */
        public String bssid;

        /** channel frequency that the device is on; optional */
        public int frequency;

        /** optional channel width. wider channels result in better accuracy,
         *  but they take longer time, and even get aborted may times; use
         *  RTT_CHANNEL_WIDTH_UNSPECIFIED if not specifying */
        public int channelWidth;

        /** number of samples to be taken */
        public int num_samples;

        /** number of retries if a sample fails */
        public int num_retries;
    }

    /** pseudo-private class used to parcel arguments */
    public static class ParcelableRttParams implements Parcelable {

        public RttParams mParams[];

        ParcelableRttParams(RttParams[] params) {
            mParams = params;
        }

        /** Implement the Parcelable interface {@hide} */
        public int describeContents() {
            return 0;
        }

        /** Implement the Parcelable interface {@hide} */
        public void writeToParcel(Parcel dest, int flags) {
            if (mParams != null) {
                dest.writeInt(mParams.length);

                for (RttParams params : mParams) {
                    dest.writeInt(params.deviceType);
                    dest.writeInt(params.requestType);
                    dest.writeString(params.bssid);
                    dest.writeInt(params.frequency);
                    dest.writeInt(params.channelWidth);
                    dest.writeInt(params.num_samples);
                    dest.writeInt(params.num_retries);
                }
            } else {
                dest.writeInt(0);
            }
        }

        /** Implement the Parcelable interface {@hide} */
        public static final Creator<ParcelableRttParams> CREATOR =
                new Creator<ParcelableRttParams>() {
                    public ParcelableRttParams createFromParcel(Parcel in) {

                        int num = in.readInt();

                        if (num == 0) {
                            return new ParcelableRttParams(null);
                        }

                        RttParams params[] = new RttParams[num];
                        for (int i = 0; i < num; i++) {
                            params[i] = new RttParams();
                            params[i].deviceType = in.readInt();
                            params[i].requestType = in.readInt();
                            params[i].bssid = in.readString();
                            params[i].frequency = in.readInt();
                            params[i].channelWidth = in.readInt();
                            params[i].num_samples = in.readInt();
                            params[i].num_retries = in.readInt();

                        }

                        ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
                        return parcelableParams;
                    }

                    public ParcelableRttParams[] newArray(int size) {
                        return new ParcelableRttParams[size];
                    }
                };
    }

    /** specifies RTT results */
    public static class RttResult {
        /** mac address of the device being ranged */
        public String bssid;

        /** status of the request */
        public int status;

        /** type of the request used */
        public int requestType;

        /** timestamp of completion, in microsecond since boot */
        public long ts;

        /** average RSSI observed */
        public int rssi;

        /** RSSI spread (i.e. max - min) */
        public int rssi_spread;

        /** average transmit rate */
        public int tx_rate;

        /** average round trip time in nano second */
        public long rtt_ns;

        /** standard deviation observed in round trip time */
        public long rtt_sd_ns;

        /** spread (i.e. max - min) round trip time */
        public long rtt_spread_ns;

        /** average distance in centimeter, computed based on rtt_ns */
        public int distance_cm;

        /** standard deviation observed in distance */
        public int distance_sd_cm;

        /** spread (i.e. max - min) distance */
        public int distance_spread_cm;
    }


    /** pseudo-private class used to parcel results */
    public static class ParcelableRttResults implements Parcelable {

        public RttResult mResults[];

        public ParcelableRttResults(RttResult[] results) {
            mResults = results;
        }

        /** Implement the Parcelable interface {@hide} */
        public int describeContents() {
            return 0;
        }

        /** Implement the Parcelable interface {@hide} */
        public void writeToParcel(Parcel dest, int flags) {
            if (mResults != null) {
                dest.writeInt(mResults.length);
                for (RttResult result : mResults) {
                    dest.writeString(result.bssid);
                    dest.writeInt(result.status);
                    dest.writeInt(result.requestType);
                    dest.writeLong(result.ts);
                    dest.writeInt(result.rssi);
                    dest.writeInt(result.rssi_spread);
                    dest.writeInt(result.tx_rate);
                    dest.writeLong(result.rtt_ns);
                    dest.writeLong(result.rtt_sd_ns);
                    dest.writeLong(result.rtt_spread_ns);
                    dest.writeInt(result.distance_cm);
                    dest.writeInt(result.distance_sd_cm);
                    dest.writeInt(result.distance_spread_cm);
                }
            } else {
                dest.writeInt(0);
            }
        }

        /** Implement the Parcelable interface {@hide} */
        public static final Creator<ParcelableRttResults> CREATOR =
                new Creator<ParcelableRttResults>() {
                    public ParcelableRttResults createFromParcel(Parcel in) {

                        int num = in.readInt();

                        if (num == 0) {
                            return new ParcelableRttResults(null);
                        }

                        RttResult results[] = new RttResult[num];
                        for (int i = 0; i < num; i++) {
                            results[i] = new RttResult();
                            results[i].bssid = in.readString();
                            results[i].status = in.readInt();
                            results[i].requestType = in.readInt();
                            results[i].ts = in.readLong();
                            results[i].rssi = in.readInt();
                            results[i].rssi_spread = in.readInt();
                            results[i].tx_rate = in.readInt();
                            results[i].rtt_ns = in.readLong();
                            results[i].rtt_sd_ns = in.readLong();
                            results[i].rtt_spread_ns = in.readLong();
                            results[i].distance_cm = in.readInt();
                            results[i].distance_sd_cm = in.readInt();
                            results[i].distance_spread_cm = in.readInt();
                        }

                        ParcelableRttResults parcelableResults = new ParcelableRttResults(results);
                        return parcelableResults;
                    }

                    public ParcelableRttResults[] newArray(int size) {
                        return new ParcelableRttResults[size];
                    }
                };
    }


    public static interface RttListener {
        public void onSuccess(RttResult[] results);
        public void onFailure(int reason, String description);
        public void onAborted();
    }

    public void startRanging(RttParams[] params, RttListener listener) {
        validateChannel();
        ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
        sAsyncChannel.sendMessage(CMD_OP_START_RANGING,
                0, putListener(listener), parcelableParams);
    }

    public void stopRanging(RttListener listener) {
        validateChannel();
        sAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
    }

    /* private methods */
    public static final int BASE = Protocol.BASE_WIFI_RTT_MANAGER;

    public static final int CMD_OP_START_RANGING        = BASE + 0;
    public static final int CMD_OP_STOP_RANGING         = BASE + 1;
    public static final int CMD_OP_FAILED               = BASE + 2;
    public static final int CMD_OP_SUCCEEDED            = BASE + 3;
    public static final int CMD_OP_ABORTED              = BASE + 4;

    private Context mContext;
    private IRttManager mService;

    private static final int INVALID_KEY = 0;
    private static int sListenerKey = 1;

    private static final SparseArray sListenerMap = new SparseArray();
    private static final Object sListenerMapLock = new Object();

    private static AsyncChannel sAsyncChannel;
    private static CountDownLatch sConnected;

    private static final Object sThreadRefLock = new Object();
    private static int sThreadRefCount;
    private static HandlerThread sHandlerThread;

    /**
     * Create a new WifiScanner instance.
     * Applications will almost always want to use
     * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
     * the standard {@link android.content.Context#WIFI_RTT_SERVICE Context.WIFI_RTT_SERVICE}.
     * @param context the application context
     * @param service the Binder interface
     * @hide
     */

    public RttManager(Context context, IRttManager service) {
        mContext = context;
        mService = service;
        init();
    }

    private void init() {
        synchronized (sThreadRefLock) {
            if (++sThreadRefCount == 1) {
                Messenger messenger = null;
                try {
                    Log.d(TAG, "Get the messenger from " + mService);
                    messenger = mService.getMessenger();
                } catch (RemoteException e) {
                    /* do nothing */
                } catch (SecurityException e) {
                    /* do nothing */
                }

                if (messenger == null) {
                    sAsyncChannel = null;
                    return;
                }

                sHandlerThread = new HandlerThread("WifiScanner");
                sAsyncChannel = new AsyncChannel();
                sConnected = new CountDownLatch(1);

                sHandlerThread.start();
                Handler handler = new ServiceHandler(sHandlerThread.getLooper());
                sAsyncChannel.connect(mContext, handler, messenger);
                try {
                    sConnected.await();
                } catch (InterruptedException e) {
                    Log.e(TAG, "interrupted wait at init");
                }
            }
        }
    }

    private void validateChannel() {
        if (sAsyncChannel == null) throw new IllegalStateException(
                "No permission to access and change wifi or a bad initialization");
    }

    private static int putListener(Object listener) {
        if (listener == null) return INVALID_KEY;
        int key;
        synchronized (sListenerMapLock) {
            do {
                key = sListenerKey++;
            } while (key == INVALID_KEY);
            sListenerMap.put(key, listener);
        }
        return key;
    }

    private static Object getListener(int key) {
        if (key == INVALID_KEY) return null;
        synchronized (sListenerMapLock) {
            Object listener = sListenerMap.get(key);
            return listener;
        }
    }

    private static int getListenerKey(Object listener) {
        if (listener == null) return INVALID_KEY;
        synchronized (sListenerMapLock) {
            int index = sListenerMap.indexOfValue(listener);
            if (index == -1) {
                return INVALID_KEY;
            } else {
                return sListenerMap.keyAt(index);
            }
        }
    }

    private static Object removeListener(int key) {
        if (key == INVALID_KEY) return null;
        synchronized (sListenerMapLock) {
            Object listener = sListenerMap.get(key);
            sListenerMap.remove(key);
            return listener;
        }
    }

    private static int removeListener(Object listener) {
        int key = getListenerKey(listener);
        if (key == INVALID_KEY) return key;
        synchronized (sListenerMapLock) {
            sListenerMap.remove(key);
            return key;
        }
    }

    private static class ServiceHandler extends Handler {
        ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
                        sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
                    } else {
                        Log.e(TAG, "Failed to set up channel connection");
                        // This will cause all further async API calls on the WifiManager
                        // to fail and throw an exception
                        sAsyncChannel = null;
                    }
                    sConnected.countDown();
                    return;
                case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
                    return;
                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
                    Log.e(TAG, "Channel connection lost");
                    // This will cause all further async API calls on the WifiManager
                    // to fail and throw an exception
                    sAsyncChannel = null;
                    getLooper().quit();
                    return;
            }

            Object listener = getListener(msg.arg2);
            if (listener == null) {
                if (DBG) Log.d(TAG, "invalid listener key = " + msg.arg2);
                return;
            } else {
                if (DBG) Log.d(TAG, "listener key = " + msg.arg2);
            }

            switch (msg.what) {
                /* ActionListeners grouped together */
                case CMD_OP_SUCCEEDED :
                    reportSuccess(listener, msg);
                    removeListener(msg.arg2);
                    break;
                case CMD_OP_FAILED :
                    reportFailure(listener, msg);
                    removeListener(msg.arg2);
                    break;
                case CMD_OP_ABORTED :
                    ((RttListener) listener).onAborted();
                    removeListener(msg.arg2);
                    break;
                default:
                    if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
                    return;
            }
        }

        void reportSuccess(Object listener, Message msg) {
            RttListener rttListener = (RttListener) listener;
            ParcelableRttResults parcelableResults = (ParcelableRttResults) msg.obj;
            ((RttListener) listener).onSuccess(parcelableResults.mResults);
        }

        void reportFailure(Object listener, Message msg) {
            RttListener rttListener = (RttListener) listener;
            Bundle bundle = (Bundle) msg.obj;
            ((RttListener) listener).onFailure(msg.arg1, bundle.getString(DESCRIPTION_KEY));
        }
    }

}