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.annotation.Nullable; 20 import android.app.AppOpsManager; 21 import android.content.Context; 22 import android.net.wifi.aware.ConfigRequest; 23 import android.net.wifi.aware.IWifiAwareEventCallback; 24 import android.net.wifi.util.HexEncoding; 25 import android.os.RemoteException; 26 import android.os.WorkSource; 27 import android.util.Log; 28 import android.util.SparseArray; 29 30 import com.android.server.wifi.util.WifiPermissionsUtil; 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 VDBG = false; // STOPSHIP if true 48 private boolean mDbg = false; 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 @Nullable String mCallingFeatureId; 63 private final boolean mNotifyIdentityChange; 64 private final WifiPermissionsUtil mWifiPermissionsUtil; 65 66 private final AppOpsManager mAppOps; 67 private final long mCreationTime; 68 69 private static final byte[] ALL_ZERO_MAC = new byte[] {0, 0, 0, 0, 0, 0}; 70 private byte[] mLastDiscoveryInterfaceMac = ALL_ZERO_MAC; 71 WifiAwareClientState(Context context, int clientId, int uid, int pid, String callingPackage, @Nullable String callingFeatureId, IWifiAwareEventCallback callback, ConfigRequest configRequest, boolean notifyIdentityChange, long creationTime, WifiPermissionsUtil wifiPermissionsUtil)72 public WifiAwareClientState(Context context, int clientId, int uid, int pid, 73 String callingPackage, @Nullable String callingFeatureId, 74 IWifiAwareEventCallback callback, ConfigRequest configRequest, 75 boolean notifyIdentityChange, long creationTime, 76 WifiPermissionsUtil wifiPermissionsUtil) { 77 mContext = context; 78 mClientId = clientId; 79 mUid = uid; 80 mPid = pid; 81 mCallingPackage = callingPackage; 82 mCallingFeatureId = callingFeatureId; 83 mCallback = callback; 84 mConfigRequest = configRequest; 85 mNotifyIdentityChange = notifyIdentityChange; 86 87 mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 88 mCreationTime = creationTime; 89 mWifiPermissionsUtil = wifiPermissionsUtil; 90 } 91 92 /** 93 * Enable verbose logging. 94 */ enableVerboseLogging(boolean verbose)95 public void enableVerboseLogging(boolean verbose) { 96 mDbg = verbose | VDBG; 97 } 98 99 /** 100 * Destroy the current client - corresponds to a disconnect() request from 101 * the client. Destroys all discovery sessions belonging to this client. 102 */ destroy()103 public void destroy() { 104 for (int i = 0; i < mSessions.size(); ++i) { 105 mSessions.valueAt(i).terminate(); 106 } 107 mSessions.clear(); 108 mConfigRequest = null; 109 } 110 getConfigRequest()111 public ConfigRequest getConfigRequest() { 112 return mConfigRequest; 113 } 114 getClientId()115 public int getClientId() { 116 return mClientId; 117 } 118 getUid()119 public int getUid() { 120 return mUid; 121 } 122 getCallingPackage()123 public String getCallingPackage() { 124 return mCallingPackage; 125 } 126 getNotifyIdentityChange()127 public boolean getNotifyIdentityChange() { 128 return mNotifyIdentityChange; 129 } 130 getCreationTime()131 public long getCreationTime() { 132 return mCreationTime; 133 } 134 getSessions()135 public SparseArray<WifiAwareDiscoverySessionState> getSessions() { 136 return mSessions; 137 } 138 139 /** 140 * Searches the discovery sessions of this client and returns the one 141 * corresponding to the publish/subscribe ID. Used on callbacks from HAL to 142 * map callbacks to the correct discovery session. 143 * 144 * @param pubSubId The publish/subscribe match session ID. 145 * @return Aware session corresponding to the requested ID. 146 */ getAwareSessionStateForPubSubId(int pubSubId)147 public WifiAwareDiscoverySessionState getAwareSessionStateForPubSubId(int pubSubId) { 148 for (int i = 0; i < mSessions.size(); ++i) { 149 WifiAwareDiscoverySessionState session = mSessions.valueAt(i); 150 if (session.isPubSubIdSession(pubSubId)) { 151 return session; 152 } 153 } 154 155 return null; 156 } 157 158 /** 159 * Add the session to the client database. 160 * 161 * @param session Session to be added. 162 */ addSession(WifiAwareDiscoverySessionState session)163 public void addSession(WifiAwareDiscoverySessionState session) { 164 int sessionId = session.getSessionId(); 165 if (mSessions.get(sessionId) != null) { 166 Log.w(TAG, "createSession: sessionId already exists (replaced) - " + sessionId); 167 } 168 169 mSessions.put(sessionId, session); 170 } 171 172 /** 173 * Remove the specified session from the client database - without doing a 174 * terminate on the session. The assumption is that it is already 175 * terminated. 176 * 177 * @param sessionId The session ID of the session to be removed. 178 */ removeSession(int sessionId)179 public void removeSession(int sessionId) { 180 if (mSessions.get(sessionId) == null) { 181 Log.e(TAG, "removeSession: sessionId doesn't exist - " + sessionId); 182 return; 183 } 184 185 mSessions.delete(sessionId); 186 } 187 188 /** 189 * Destroy the discovery session: terminates discovery and frees up 190 * resources. 191 * 192 * @param sessionId The session ID of the session to be destroyed. 193 */ terminateSession(int sessionId)194 public WifiAwareDiscoverySessionState terminateSession(int sessionId) { 195 WifiAwareDiscoverySessionState session = mSessions.get(sessionId); 196 if (session == null) { 197 Log.e(TAG, "terminateSession: sessionId doesn't exist - " + sessionId); 198 return null; 199 } 200 201 session.terminate(); 202 mSessions.delete(sessionId); 203 204 return session; 205 } 206 207 /** 208 * Retrieve a session. 209 * 210 * @param sessionId Session ID of the session to be retrieved. 211 * @return Session or null if there's no session corresponding to the 212 * sessionId. 213 */ getSession(int sessionId)214 public WifiAwareDiscoverySessionState getSession(int sessionId) { 215 return mSessions.get(sessionId); 216 } 217 218 /** 219 * Called to dispatch the Aware interface address change to the client - as an 220 * identity change (interface address information not propagated to client - 221 * privacy concerns). 222 * 223 * @param mac The new MAC address of the discovery interface - optionally propagated to the 224 * client. 225 */ onInterfaceAddressChange(byte[] mac)226 public void onInterfaceAddressChange(byte[] mac) { 227 if (mDbg) { 228 Log.v(TAG, 229 "onInterfaceAddressChange: mClientId=" + mClientId + ", mNotifyIdentityChange=" 230 + mNotifyIdentityChange + ", mac=" + String.valueOf( 231 HexEncoding.encode(mac)) + ", mLastDiscoveryInterfaceMac=" 232 + String.valueOf(HexEncoding.encode(mLastDiscoveryInterfaceMac))); 233 } 234 if (mNotifyIdentityChange && !Arrays.equals(mac, mLastDiscoveryInterfaceMac)) { 235 try { 236 boolean hasPermission = mWifiPermissionsUtil.checkCallersLocationPermission( 237 mCallingPackage, mCallingFeatureId, mUid, 238 /* coarseForTargetSdkLessThanQ */ true, null); 239 if (VDBG) Log.v(TAG, "hasPermission=" + hasPermission); 240 mCallback.onIdentityChanged(hasPermission ? mac : ALL_ZERO_MAC); 241 } catch (RemoteException e) { 242 Log.w(TAG, "onIdentityChanged: RemoteException - ignored: " + e); 243 } 244 } 245 246 mLastDiscoveryInterfaceMac = mac; 247 } 248 249 /** 250 * Called to dispatch the Aware cluster change (due to joining of a new 251 * cluster or starting a cluster) to the client - as an identity change 252 * (interface address information not propagated to client - privacy 253 * concerns). Dispatched if the client registered for the identity changed 254 * event. 255 * 256 * @param mac The cluster ID of the cluster started or joined. 257 * @param currentDiscoveryInterfaceMac The MAC address of the discovery interface. 258 */ onClusterChange(int flag, byte[] mac, byte[] currentDiscoveryInterfaceMac)259 public void onClusterChange(int flag, byte[] mac, byte[] currentDiscoveryInterfaceMac) { 260 if (mDbg) { 261 Log.v(TAG, 262 "onClusterChange: mClientId=" + mClientId + ", mNotifyIdentityChange=" 263 + mNotifyIdentityChange + ", mac=" + String.valueOf( 264 HexEncoding.encode(mac)) + ", currentDiscoveryInterfaceMac=" 265 + String.valueOf(HexEncoding.encode(currentDiscoveryInterfaceMac)) 266 + ", mLastDiscoveryInterfaceMac=" + String.valueOf( 267 HexEncoding.encode(mLastDiscoveryInterfaceMac))); 268 } 269 if (mNotifyIdentityChange && !Arrays.equals(currentDiscoveryInterfaceMac, 270 mLastDiscoveryInterfaceMac)) { 271 try { 272 boolean hasPermission = mWifiPermissionsUtil.checkCallersLocationPermission( 273 mCallingPackage, mCallingFeatureId, mUid, 274 /* coarseForTargetSdkLessThanQ */ true, null); 275 if (VDBG) Log.v(TAG, "hasPermission=" + hasPermission); 276 mCallback.onIdentityChanged( 277 hasPermission ? currentDiscoveryInterfaceMac : ALL_ZERO_MAC); 278 } catch (RemoteException e) { 279 Log.w(TAG, "onIdentityChanged: RemoteException - ignored: " + e); 280 } 281 } 282 283 mLastDiscoveryInterfaceMac = currentDiscoveryInterfaceMac; 284 } 285 286 /** 287 * Check if client needs ranging enabled. 288 * @return True if one of the discovery session has ranging enabled, false otherwise. 289 */ isRangingEnabled()290 public boolean isRangingEnabled() { 291 for (int i = 0; i < mSessions.size(); ++i) { 292 if (mSessions.valueAt(i).isRangingEnabled()) { 293 return true; 294 } 295 } 296 return false; 297 } 298 299 /** 300 * Dump the internal state of the class. 301 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)302 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 303 pw.println("AwareClientState:"); 304 pw.println(" mClientId: " + mClientId); 305 pw.println(" mConfigRequest: " + mConfigRequest); 306 pw.println(" mNotifyIdentityChange: " + mNotifyIdentityChange); 307 pw.println(" mCallback: " + mCallback); 308 pw.println(" mSessions: [" + mSessions + "]"); 309 for (int i = 0; i < mSessions.size(); ++i) { 310 mSessions.valueAt(i).dump(fd, pw, args); 311 } 312 } 313 } 314