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