• 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 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