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