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