WifiAwareClientState.java revision 475637585ac0b80e5c9c3a137c722f1d88916d51
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 17package com.android.server.wifi.aware; 18 19import android.Manifest; 20import android.app.AppOpsManager; 21import android.content.Context; 22import android.content.pm.PackageManager; 23import android.net.wifi.RttManager; 24import android.net.wifi.aware.ConfigRequest; 25import android.net.wifi.aware.IWifiAwareEventCallback; 26import android.os.RemoteException; 27import android.util.Log; 28import android.util.SparseArray; 29 30import libcore.util.HexEncoding; 31 32import java.io.FileDescriptor; 33import java.io.PrintWriter; 34import 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 */ 45public 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( 244 hasPermission ? currentDiscoveryInterfaceMac : ALL_ZERO_MAC); 245 } catch (RemoteException e) { 246 Log.w(TAG, "onIdentityChanged: RemoteException - ignored: " + e); 247 } 248 } 249 250 mLastDiscoveryInterfaceMac = currentDiscoveryInterfaceMac; 251 } 252 253 private boolean hasLocationingPermission() { 254 // FINE provides COARSE, so only have to check for the latter 255 return mContext.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, mPid, mUid) 256 == PackageManager.PERMISSION_GRANTED && mAppOps.noteOp( 257 AppOpsManager.OP_COARSE_LOCATION, mUid, mCallingPackage) 258 == AppOpsManager.MODE_ALLOWED; 259 } 260 261 /** 262 * Called on RTT success - forwards call to client. 263 */ 264 public void onRangingSuccess(int rangingId, RttManager.ParcelableRttResults results) { 265 if (VDBG) { 266 Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", results=" + results); 267 } 268 try { 269 mCallback.onRangingSuccess(rangingId, results); 270 } catch (RemoteException e) { 271 Log.w(TAG, "onRangingSuccess: RemoteException - ignored: " + e); 272 } 273 } 274 275 /** 276 * Called on RTT failure - forwards call to client. 277 */ 278 public void onRangingFailure(int rangingId, int reason, String description) { 279 if (VDBG) { 280 Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", reason=" + reason 281 + ", description=" + description); 282 } 283 try { 284 mCallback.onRangingFailure(rangingId, reason, description); 285 } catch (RemoteException e) { 286 Log.w(TAG, "onRangingFailure: RemoteException - ignored: " + e); 287 } 288 } 289 290 /** 291 * Called on RTT operation aborted - forwards call to client. 292 */ 293 public void onRangingAborted(int rangingId) { 294 if (VDBG) Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId); 295 try { 296 mCallback.onRangingAborted(rangingId); 297 } catch (RemoteException e) { 298 Log.w(TAG, "onRangingAborted: RemoteException - ignored: " + e); 299 } 300 } 301 302 /** 303 * Dump the internal state of the class. 304 */ 305 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 306 pw.println("AwareClientState:"); 307 pw.println(" mClientId: " + mClientId); 308 pw.println(" mConfigRequest: " + mConfigRequest); 309 pw.println(" mNotifyIdentityChange: " + mNotifyIdentityChange); 310 pw.println(" mCallback: " + mCallback); 311 pw.println(" mSessions: [" + mSessions + "]"); 312 for (int i = 0; i < mSessions.size(); ++i) { 313 mSessions.valueAt(i).dump(fd, pw, args); 314 } 315 } 316} 317