Home | History | Annotate | Download | only in aware
      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 
     17 package com.android.server.wifi.aware;
     18 
     19 import android.Manifest;
     20 import android.app.AppOpsManager;
     21 import android.content.Context;
     22 import android.content.pm.PackageManager;
     23 import android.net.wifi.RttManager;
     24 import android.net.wifi.aware.ConfigRequest;
     25 import android.net.wifi.aware.IWifiAwareEventCallback;
     26 import android.os.RemoteException;
     27 import android.util.Log;
     28 import android.util.SparseArray;
     29 
     30 import libcore.util.HexEncoding;
     31 
     32 import java.io.FileDescriptor;
     33 import java.io.PrintWriter;
     34 import java.util.Arrays;
     35 
     36 /**
     37  * Manages the service-side Aware state of an individual "client". A client
     38  * corresponds to a single instantiation of the WifiAwareManager - there could be
     39  * multiple ones per UID/process (each of which is a separate client with its
     40  * own session namespace). The client state is primarily: (1) callback (a
     41  * singleton per client) through which Aware-wide events are called, and (2) a set
     42  * of discovery sessions (publish and/or subscribe) which are created through
     43  * this client and whose lifetime is tied to the lifetime of the client.
     44  */
     45 public class WifiAwareClientState {
     46     private static final String TAG = "WifiAwareClientState";
     47     private static final boolean DBG = false;
     48     private static final boolean VDBG = false; // STOPSHIP if true
     49 
     50     /* package */ static final int CLUSTER_CHANGE_EVENT_STARTED = 0;
     51     /* package */ static final int CLUSTER_CHANGE_EVENT_JOINED = 1;
     52 
     53     private final Context mContext;
     54     private final IWifiAwareEventCallback mCallback;
     55     private final SparseArray<WifiAwareDiscoverySessionState> mSessions = new SparseArray<>();
     56 
     57     private final int mClientId;
     58     private ConfigRequest mConfigRequest;
     59     private final int mUid;
     60     private final int mPid;
     61     private final String mCallingPackage;
     62     private final boolean mNotifyIdentityChange;
     63 
     64     private AppOpsManager mAppOps;
     65 
     66     private static final byte[] ALL_ZERO_MAC = new byte[] {0, 0, 0, 0, 0, 0};
     67     private byte[] mLastDiscoveryInterfaceMac = ALL_ZERO_MAC;
     68 
     69     public WifiAwareClientState(Context context, int clientId, int uid, int pid,
     70             String callingPackage, IWifiAwareEventCallback callback, ConfigRequest configRequest,
     71             boolean notifyIdentityChange) {
     72         mContext = context;
     73         mClientId = clientId;
     74         mUid = uid;
     75         mPid = pid;
     76         mCallingPackage = callingPackage;
     77         mCallback = callback;
     78         mConfigRequest = configRequest;
     79         mNotifyIdentityChange = notifyIdentityChange;
     80 
     81         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
     82     }
     83 
     84     /**
     85      * Destroy the current client - corresponds to a disconnect() request from
     86      * the client. Destroys all discovery sessions belonging to this client.
     87      */
     88     public void destroy() {
     89         for (int i = 0; i < mSessions.size(); ++i) {
     90             mSessions.valueAt(i).terminate();
     91         }
     92         mSessions.clear();
     93         mConfigRequest = null;
     94     }
     95 
     96     public ConfigRequest getConfigRequest() {
     97         return mConfigRequest;
     98     }
     99 
    100     public int getClientId() {
    101         return mClientId;
    102     }
    103 
    104     public int getUid() {
    105         return mUid;
    106     }
    107 
    108     public boolean getNotifyIdentityChange() {
    109         return mNotifyIdentityChange;
    110     }
    111 
    112     /**
    113      * Searches the discovery sessions of this client and returns the one
    114      * corresponding to the publish/subscribe ID. Used on callbacks from HAL to
    115      * map callbacks to the correct discovery session.
    116      *
    117      * @param pubSubId The publish/subscribe match session ID.
    118      * @return Aware session corresponding to the requested ID.
    119      */
    120     public WifiAwareDiscoverySessionState getAwareSessionStateForPubSubId(int pubSubId) {
    121         for (int i = 0; i < mSessions.size(); ++i) {
    122             WifiAwareDiscoverySessionState session = mSessions.valueAt(i);
    123             if (session.isPubSubIdSession(pubSubId)) {
    124                 return session;
    125             }
    126         }
    127 
    128         return null;
    129     }
    130 
    131     /**
    132      * Add the session to the client database.
    133      *
    134      * @param session Session to be added.
    135      */
    136     public void addSession(WifiAwareDiscoverySessionState session) {
    137         int sessionId = session.getSessionId();
    138         if (mSessions.get(sessionId) != null) {
    139             Log.w(TAG, "createSession: sessionId already exists (replaced) - " + sessionId);
    140         }
    141 
    142         mSessions.put(sessionId, session);
    143     }
    144 
    145     /**
    146      * Remove the specified session from the client database - without doing a
    147      * terminate on the session. The assumption is that it is already
    148      * terminated.
    149      *
    150      * @param sessionId The session ID of the session to be removed.
    151      */
    152     public void removeSession(int sessionId) {
    153         if (mSessions.get(sessionId) == null) {
    154             Log.e(TAG, "removeSession: sessionId doesn't exist - " + sessionId);
    155             return;
    156         }
    157 
    158         mSessions.delete(sessionId);
    159     }
    160 
    161     /**
    162      * Destroy the discovery session: terminates discovery and frees up
    163      * resources.
    164      *
    165      * @param sessionId The session ID of the session to be destroyed.
    166      */
    167     public void terminateSession(int sessionId) {
    168         WifiAwareDiscoverySessionState session = mSessions.get(sessionId);
    169         if (session == null) {
    170             Log.e(TAG, "terminateSession: sessionId doesn't exist - " + sessionId);
    171             return;
    172         }
    173 
    174         session.terminate();
    175         mSessions.delete(sessionId);
    176     }
    177 
    178     /**
    179      * Retrieve a session.
    180      *
    181      * @param sessionId Session ID of the session to be retrieved.
    182      * @return Session or null if there's no session corresponding to the
    183      *         sessionId.
    184      */
    185     public WifiAwareDiscoverySessionState getSession(int sessionId) {
    186         return mSessions.get(sessionId);
    187     }
    188 
    189     /**
    190      * Called to dispatch the Aware interface address change to the client - as an
    191      * identity change (interface address information not propagated to client -
    192      * privacy concerns).
    193      *
    194      * @param mac The new MAC address of the discovery interface - optionally propagated to the
    195      *            client.
    196      */
    197     public void onInterfaceAddressChange(byte[] mac) {
    198         if (VDBG) {
    199             Log.v(TAG,
    200                     "onInterfaceAddressChange: mClientId=" + mClientId + ", mNotifyIdentityChange="
    201                             + mNotifyIdentityChange + ", mac=" + String.valueOf(
    202                             HexEncoding.encode(mac)) + ", mLastDiscoveryInterfaceMac="
    203                             + String.valueOf(HexEncoding.encode(mLastDiscoveryInterfaceMac)));
    204         }
    205         if (mNotifyIdentityChange && !Arrays.equals(mac, mLastDiscoveryInterfaceMac)) {
    206             try {
    207                 boolean hasPermission = hasLocationingPermission();
    208                 if (VDBG) Log.v(TAG, "hasPermission=" + hasPermission);
    209                 mCallback.onIdentityChanged(hasPermission ? mac : ALL_ZERO_MAC);
    210             } catch (RemoteException e) {
    211                 Log.w(TAG, "onIdentityChanged: RemoteException - ignored: " + e);
    212             }
    213         }
    214 
    215         mLastDiscoveryInterfaceMac = mac;
    216     }
    217 
    218     /**
    219      * Called to dispatch the Aware cluster change (due to joining of a new
    220      * cluster or starting a cluster) to the client - as an identity change
    221      * (interface address information not propagated to client - privacy
    222      * concerns). Dispatched if the client registered for the identity changed
    223      * event.
    224      *
    225      * @param mac The cluster ID of the cluster started or joined.
    226      * @param currentDiscoveryInterfaceMac The MAC address of the discovery interface.
    227      */
    228     public void onClusterChange(int flag, byte[] mac, byte[] currentDiscoveryInterfaceMac) {
    229         if (VDBG) {
    230             Log.v(TAG,
    231                     "onClusterChange: mClientId=" + mClientId + ", mNotifyIdentityChange="
    232                             + mNotifyIdentityChange + ", mac=" + String.valueOf(
    233                             HexEncoding.encode(mac)) + ", currentDiscoveryInterfaceMac="
    234                             + String.valueOf(HexEncoding.encode(currentDiscoveryInterfaceMac))
    235                             + ", mLastDiscoveryInterfaceMac=" + String.valueOf(
    236                             HexEncoding.encode(mLastDiscoveryInterfaceMac)));
    237         }
    238         if (mNotifyIdentityChange && !Arrays.equals(currentDiscoveryInterfaceMac,
    239                 mLastDiscoveryInterfaceMac)) {
    240             try {
    241                 boolean hasPermission = hasLocationingPermission();
    242                 if (VDBG) Log.v(TAG, "hasPermission=" + hasPermission);
    243                 mCallback.onIdentityChanged(hasPermission ? mac : ALL_ZERO_MAC);
    244             } catch (RemoteException e) {
    245                 Log.w(TAG, "onIdentityChanged: RemoteException - ignored: " + e);
    246             }
    247         }
    248 
    249         mLastDiscoveryInterfaceMac = currentDiscoveryInterfaceMac;
    250     }
    251 
    252     private boolean hasLocationingPermission() {
    253         // FINE provides COARSE, so only have to check for the latter
    254         return mContext.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, mPid, mUid)
    255                 == PackageManager.PERMISSION_GRANTED && mAppOps.noteOp(
    256                 AppOpsManager.OP_COARSE_LOCATION, mUid, mCallingPackage)
    257                 == AppOpsManager.MODE_ALLOWED;
    258     }
    259 
    260     /**
    261      * Called on RTT success - forwards call to client.
    262      */
    263     public void onRangingSuccess(int rangingId, RttManager.ParcelableRttResults results) {
    264         if (VDBG) {
    265             Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", results=" + results);
    266         }
    267         try {
    268             mCallback.onRangingSuccess(rangingId, results);
    269         } catch (RemoteException e) {
    270             Log.w(TAG, "onRangingSuccess: RemoteException - ignored: " + e);
    271         }
    272     }
    273 
    274     /**
    275      * Called on RTT failure - forwards call to client.
    276      */
    277     public void onRangingFailure(int rangingId, int reason, String description) {
    278         if (VDBG) {
    279             Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", reason=" + reason
    280                     + ", description=" + description);
    281         }
    282         try {
    283             mCallback.onRangingFailure(rangingId, reason, description);
    284         } catch (RemoteException e) {
    285             Log.w(TAG, "onRangingFailure: RemoteException - ignored: " + e);
    286         }
    287     }
    288 
    289     /**
    290      * Called on RTT operation aborted - forwards call to client.
    291      */
    292     public void onRangingAborted(int rangingId) {
    293         if (VDBG) Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId);
    294         try {
    295             mCallback.onRangingAborted(rangingId);
    296         } catch (RemoteException e) {
    297             Log.w(TAG, "onRangingAborted: RemoteException - ignored: " + e);
    298         }
    299     }
    300 
    301     /**
    302      * Dump the internal state of the class.
    303      */
    304     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    305         pw.println("AwareClientState:");
    306         pw.println("  mClientId: " + mClientId);
    307         pw.println("  mConfigRequest: " + mConfigRequest);
    308         pw.println("  mNotifyIdentityChange: " + mNotifyIdentityChange);
    309         pw.println("  mCallback: " + mCallback);
    310         pw.println("  mSessions: [" + mSessions + "]");
    311         for (int i = 0; i < mSessions.size(); ++i) {
    312             mSessions.valueAt(i).dump(fd, pw, args);
    313         }
    314     }
    315 }
    316