• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static com.android.server.wifi.aware.WifiAwareStateManager.INSTANT_MODE_24GHZ;
20 import static com.android.server.wifi.aware.WifiAwareStateManager.INSTANT_MODE_5GHZ;
21 import static com.android.server.wifi.aware.WifiAwareStateManager.INSTANT_MODE_DISABLED;
22 
23 import android.annotation.Nullable;
24 import android.app.AppOpsManager;
25 import android.content.Context;
26 import android.net.wifi.aware.ConfigRequest;
27 import android.net.wifi.aware.IWifiAwareEventCallback;
28 import android.net.wifi.aware.IdentityChangedListener;
29 import android.net.wifi.util.HexEncoding;
30 import android.os.RemoteException;
31 import android.util.LocalLog;
32 import android.util.Log;
33 import android.util.SparseArray;
34 
35 import com.android.server.wifi.util.WifiPermissionsUtil;
36 
37 import java.io.FileDescriptor;
38 import java.io.PrintWriter;
39 import java.util.Arrays;
40 
41 /**
42  * Manages the service-side Aware state of an individual "client". A client
43  * corresponds to a single instantiation of the WifiAwareManager - there could be
44  * multiple ones per UID/process (each of which is a separate client with its
45  * own session namespace). The client state is primarily: (1) callback (a
46  * singleton per client) through which Aware-wide events are called, and (2) a set
47  * of discovery sessions (publish and/or subscribe) which are created through
48  * this client and whose lifetime is tied to the lifetime of the client.
49  */
50 public class WifiAwareClientState {
51     private static final String TAG = "WifiAwareClientState";
52     private static final boolean VDBG = false; // STOPSHIP if true
53     private boolean mDbg = false;
54 
55     private final Context mContext;
56     private final IWifiAwareEventCallback mCallback;
57     private final SparseArray<WifiAwareDiscoverySessionState> mSessions = new SparseArray<>();
58     private final LocalLog mLocalLog;
59 
60     private final int mClientId;
61     private ConfigRequest mConfigRequest;
62     private final int mUid;
63     private final int mPid;
64     private final String mCallingPackage;
65     public final @Nullable String mCallingFeatureId;
66     private final boolean mNotifyIdentityChange;
67     private final WifiPermissionsUtil mWifiPermissionsUtil;
68     private final Object mAttributionSource;
69 
70     private final AppOpsManager mAppOps;
71     private final long mCreationTime;
72     private final boolean mAwareOffload;
73     public final int mCallerType;
74 
75     private static final byte[] ALL_ZERO_MAC = new byte[] {0, 0, 0, 0, 0, 0};
76     private byte[] mLastDiscoveryInterfaceMac = ALL_ZERO_MAC;
77     private byte[] mLastClusterId = ALL_ZERO_MAC;
78 
WifiAwareClientState(Context context, int clientId, int uid, int pid, String callingPackage, @Nullable String callingFeatureId, IWifiAwareEventCallback callback, ConfigRequest configRequest, boolean notifyIdentityChange, long creationTime, WifiPermissionsUtil wifiPermissionsUtil, Object attributionSource, LocalLog localLog, boolean awareOffload, int callerType)79     public WifiAwareClientState(Context context, int clientId, int uid, int pid,
80             String callingPackage, @Nullable String callingFeatureId,
81             IWifiAwareEventCallback callback, ConfigRequest configRequest,
82             boolean notifyIdentityChange, long creationTime,
83             WifiPermissionsUtil wifiPermissionsUtil, Object attributionSource, LocalLog localLog,
84             boolean awareOffload, int callerType) {
85         mContext = context;
86         mClientId = clientId;
87         mUid = uid;
88         mPid = pid;
89         mCallingPackage = callingPackage;
90         mCallingFeatureId = callingFeatureId;
91         mCallback = callback;
92         mConfigRequest = configRequest;
93         mNotifyIdentityChange = notifyIdentityChange;
94         mAwareOffload = awareOffload;
95 
96         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
97         mCreationTime = creationTime;
98         mWifiPermissionsUtil = wifiPermissionsUtil;
99         mAttributionSource = attributionSource;
100         mLocalLog = localLog;
101         mCallerType = callerType;
102     }
103 
104     /**
105      * Enable verbose logging.
106      */
enableVerboseLogging(boolean verbose)107     public void enableVerboseLogging(boolean verbose) {
108         mDbg = verbose | VDBG;
109     }
110 
111     /**
112      * Destroy the current client - corresponds to a disconnect() request from
113      * the client. Destroys all discovery sessions belonging to this client.
114      */
destroy()115     public void destroy() {
116         mLocalLog.log("onAwareSessionTerminated, ClientId:" + mClientId);
117         for (int i = 0; i < mSessions.size(); ++i) {
118             mSessions.valueAt(i).terminate();
119         }
120         mSessions.clear();
121         mConfigRequest = null;
122 
123         try {
124             mCallback.onAttachTerminate();
125         } catch (RemoteException e1) {
126             Log.e(TAG, "Error on onSessionTerminate()");
127         }
128     }
129 
getConfigRequest()130     public ConfigRequest getConfigRequest() {
131         return mConfigRequest;
132     }
133 
getClientId()134     public int getClientId() {
135         return mClientId;
136     }
137 
getUid()138     public int getUid() {
139         return mUid;
140     }
141 
getCallingPackage()142     public String getCallingPackage() {
143         return mCallingPackage;
144     }
145 
getNotifyIdentityChange()146     public boolean getNotifyIdentityChange() {
147         return mNotifyIdentityChange;
148     }
149 
getCreationTime()150     public long getCreationTime() {
151         return mCreationTime;
152     }
153 
getSessions()154     public SparseArray<WifiAwareDiscoverySessionState> getSessions() {
155         return mSessions;
156     }
157 
getAttributionSource()158     public Object getAttributionSource() {
159         return mAttributionSource;
160     }
161 
isAwareOffload()162     public boolean isAwareOffload() {
163         return mAwareOffload;
164     }
165     /**
166      * Searches the discovery sessions of this client and returns the one
167      * corresponding to the publish/subscribe ID. Used on callbacks from HAL to
168      * map callbacks to the correct discovery session.
169      *
170      * @param pubSubId The publish/subscribe match session ID.
171      * @return Aware session corresponding to the requested ID.
172      */
getAwareSessionStateForPubSubId(int pubSubId)173     public WifiAwareDiscoverySessionState getAwareSessionStateForPubSubId(int pubSubId) {
174         for (int i = 0; i < mSessions.size(); ++i) {
175             WifiAwareDiscoverySessionState session = mSessions.valueAt(i);
176             if (session.isPubSubIdSession(pubSubId)) {
177                 return session;
178             }
179         }
180 
181         return null;
182     }
183 
184     /**
185      * Add the session to the client database.
186      *
187      * @param session Session to be added.
188      */
addSession(WifiAwareDiscoverySessionState session)189     public void addSession(WifiAwareDiscoverySessionState session) {
190         int sessionId = session.getSessionId();
191         if (mSessions.get(sessionId) != null) {
192             Log.w(TAG, "createSession: sessionId already exists (replaced) - " + sessionId);
193         }
194 
195         mSessions.put(sessionId, session);
196     }
197 
198     /**
199      * Remove the specified session from the client database - without doing a
200      * terminate on the session. The assumption is that it is already
201      * terminated.
202      *
203      * @param sessionId The session ID of the session to be removed.
204      */
removeSession(int sessionId)205     public void removeSession(int sessionId) {
206         if (mSessions.get(sessionId) == null) {
207             Log.e(TAG, "removeSession: sessionId doesn't exist - " + sessionId);
208             return;
209         }
210 
211         mSessions.delete(sessionId);
212     }
213 
214     /**
215      * Destroy the discovery session: terminates discovery and frees up
216      * resources.
217      *
218      * @param sessionId The session ID of the session to be destroyed.
219      */
terminateSession(int sessionId)220     public WifiAwareDiscoverySessionState terminateSession(int sessionId) {
221         WifiAwareDiscoverySessionState session = mSessions.get(sessionId);
222         if (session == null) {
223             Log.e(TAG, "terminateSession: sessionId doesn't exist - " + sessionId);
224             return null;
225         }
226 
227         session.terminate();
228         mSessions.delete(sessionId);
229 
230         return session;
231     }
232 
233     /**
234      * Retrieve a session.
235      *
236      * @param sessionId Session ID of the session to be retrieved.
237      * @return Session or null if there's no session corresponding to the
238      *         sessionId.
239      */
getSession(int sessionId)240     public WifiAwareDiscoverySessionState getSession(int sessionId) {
241         return mSessions.get(sessionId);
242     }
243 
244     /**
245      * Called to dispatch the Aware interface address change to the client - as an
246      * identity change (interface address information not propagated to client -
247      * privacy concerns).
248      *
249      * @param mac The new MAC address of the discovery interface - optionally propagated to the
250      *            client.
251      */
onInterfaceAddressChange(byte[] mac)252     public void onInterfaceAddressChange(byte[] mac) {
253         mLocalLog.log("onInterfaceAddressChange: mClientId=" + mClientId
254                 + ", mNotifyIdentityChange=" + mNotifyIdentityChange
255                 + ", mac=" + String.valueOf(HexEncoding.encode(mac))
256                 + ", mLastDiscoveryInterfaceMac="
257                 + String.valueOf(HexEncoding.encode(mLastDiscoveryInterfaceMac)));
258         if (mNotifyIdentityChange && !Arrays.equals(mac, mLastDiscoveryInterfaceMac)) {
259             boolean hasPermission = mWifiPermissionsUtil.checkCallersLocationPermission(
260                     mCallingPackage, mCallingFeatureId, mUid,
261                     /* coarseForTargetSdkLessThanQ */ true, null);
262             try {
263                 if (VDBG) Log.v(TAG, "hasPermission=" + hasPermission);
264                 mCallback.onIdentityChanged(hasPermission ? mac : ALL_ZERO_MAC);
265             } catch (RemoteException e) {
266                 Log.w(TAG, "onIdentityChanged: RemoteException - ignored: " + e);
267             }
268         }
269 
270         mLastDiscoveryInterfaceMac = mac;
271     }
272 
273     /**
274      * Called to dispatch the Aware cluster change (due to joining of a new
275      * cluster or starting a cluster) to the client - as an identity change
276      * (interface address information not propagated to client - privacy
277      * concerns). Dispatched if the client registered for the identity changed
278      * event.
279      *
280      * @param clusterEventType The type of the cluster event that triggered the callback.
281      * @param clusterId The cluster ID of the cluster started or joined.
282      * @param currentDiscoveryInterfaceMac The MAC address of the discovery interface.
283      */
onClusterChange(@dentityChangedListener.ClusterChangeEvent int clusterEventType, byte[] clusterId, byte[] currentDiscoveryInterfaceMac)284     public void onClusterChange(@IdentityChangedListener.ClusterChangeEvent int clusterEventType,
285             byte[] clusterId, byte[] currentDiscoveryInterfaceMac) {
286         if (mDbg) {
287             Log.v(TAG,
288                     "onClusterChange: mClientId=" + mClientId + ", mNotifyIdentityChange="
289                             + mNotifyIdentityChange + ", clusterId=" + String.valueOf(
290                             HexEncoding.encode(clusterId)) + ", currentDiscoveryInterfaceMac="
291                             + String.valueOf(HexEncoding.encode(currentDiscoveryInterfaceMac))
292                             + ", mLastDiscoveryInterfaceMac=" + String.valueOf(
293                             HexEncoding.encode(mLastDiscoveryInterfaceMac))
294                             + ", mLastClusterId="
295                             + String.valueOf(HexEncoding.encode(mLastClusterId)));
296         }
297         if (!mNotifyIdentityChange) {
298             mLastDiscoveryInterfaceMac = currentDiscoveryInterfaceMac;
299             mLastClusterId = clusterId;
300             return;
301         }
302         boolean hasPermission = mWifiPermissionsUtil.checkCallersLocationPermission(
303                 mCallingPackage, mCallingFeatureId, mUid,
304                 /* coarseForTargetSdkLessThanQ */ true, null);
305         if (VDBG) Log.v(TAG, "hasPermission=" + hasPermission);
306         if (!Arrays.equals(currentDiscoveryInterfaceMac, mLastDiscoveryInterfaceMac)) {
307             try {
308                 mCallback.onIdentityChanged(
309                         hasPermission ? currentDiscoveryInterfaceMac : ALL_ZERO_MAC);
310             } catch (RemoteException e) {
311                 Log.w(TAG, "onIdentityChanged: RemoteException - ignored: " + e);
312             }
313         }
314 
315         mLastDiscoveryInterfaceMac = currentDiscoveryInterfaceMac;
316 
317         if (!Arrays.equals(clusterId, mLastClusterId)) {
318             try {
319                 mCallback.onClusterIdChanged(clusterEventType,
320                         hasPermission ? clusterId : ALL_ZERO_MAC);
321             } catch (RemoteException e) {
322                 Log.w(TAG, "onClusterIdChanged: RemoteException - ignored: " + e);
323             }
324         }
325 
326         mLastClusterId = clusterId;
327     }
328 
329     /**
330      * Check if client needs ranging enabled.
331      * @return True if one of the discovery session has ranging enabled, false otherwise.
332      */
isRangingEnabled()333     public boolean isRangingEnabled() {
334         for (int i = 0; i < mSessions.size(); ++i) {
335             if (mSessions.valueAt(i).isRangingEnabled()) {
336                 return true;
337             }
338         }
339         return false;
340     }
341 
342     /**
343      * Check the highest instant communication mode of the client.
344      * @param timeout Specify an interval when instant mode config timeout
345      * @return current instant mode one of the {@code INSTANT_MODE_*}
346      */
getInstantMode(long timeout)347     public int getInstantMode(long timeout) {
348         int instantMode = INSTANT_MODE_DISABLED;
349         for (int i = 0; i < mSessions.size(); ++i) {
350             int currentSession = mSessions.valueAt(i).getInstantMode(timeout);
351             if (currentSession == INSTANT_MODE_5GHZ) {
352                 return INSTANT_MODE_5GHZ;
353             }
354             if (currentSession == INSTANT_MODE_24GHZ) {
355                 instantMode = currentSession;
356             }
357         }
358         return instantMode;
359     }
360 
361     /**
362      * Dump the internal state of the class.
363      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)364     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
365         pw.println("AwareClientState:");
366         pw.println("  mClientId: " + mClientId);
367         pw.println("  mConfigRequest: " + mConfigRequest);
368         pw.println("  mNotifyIdentityChange: " + mNotifyIdentityChange);
369         pw.println("  mCallback: " + mCallback);
370         pw.println("  mSessions: [" + mSessions + "]");
371         for (int i = 0; i < mSessions.size(); ++i) {
372             mSessions.valueAt(i).dump(fd, pw, args);
373         }
374     }
375 }
376