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.WifiSettingsConfigStore.WIFI_VERBOSE_LOGGING_ENABLED;
20 
21 import android.Manifest;
22 import android.annotation.NonNull;
23 import android.app.AppOpsManager;
24 import android.content.Context;
25 import android.content.pm.PackageManager;
26 import android.hardware.wifi.V1_0.NanStatusType;
27 import android.net.wifi.aware.AwareResources;
28 import android.net.wifi.aware.Characteristics;
29 import android.net.wifi.aware.ConfigRequest;
30 import android.net.wifi.aware.DiscoverySession;
31 import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
32 import android.net.wifi.aware.IWifiAwareEventCallback;
33 import android.net.wifi.aware.IWifiAwareMacAddressProvider;
34 import android.net.wifi.aware.IWifiAwareManager;
35 import android.net.wifi.aware.PublishConfig;
36 import android.net.wifi.aware.SubscribeConfig;
37 import android.os.Binder;
38 import android.os.Handler;
39 import android.os.HandlerThread;
40 import android.os.IBinder;
41 import android.os.ParcelFileDescriptor;
42 import android.os.RemoteException;
43 import android.util.Log;
44 import android.util.SparseArray;
45 import android.util.SparseIntArray;
46 
47 import com.android.server.wifi.Clock;
48 import com.android.server.wifi.WifiSettingsConfigStore;
49 import com.android.server.wifi.util.NetdWrapper;
50 import com.android.server.wifi.util.WifiPermissionsUtil;
51 import com.android.server.wifi.util.WifiPermissionsWrapper;
52 
53 import java.io.FileDescriptor;
54 import java.io.PrintWriter;
55 import java.util.List;
56 
57 /**
58  * Implementation of the IWifiAwareManager AIDL interface. Performs validity
59  * (permission and clientID-UID mapping) checks and delegates execution to the
60  * WifiAwareStateManager singleton handler. Limited state to feedback which has to
61  * be provided instantly: client and session IDs.
62  */
63 public class WifiAwareServiceImpl extends IWifiAwareManager.Stub {
64     private static final String TAG = "WifiAwareService";
65     private boolean mDbg = false;
66 
67     private Context mContext;
68     private AppOpsManager mAppOps;
69     private WifiPermissionsUtil mWifiPermissionsUtil;
70     private WifiAwareStateManager mStateManager;
71     private WifiAwareShellCommand mShellCommand;
72     private Handler mHandler;
73 
74     private final Object mLock = new Object();
75     private final SparseArray<IBinder.DeathRecipient> mDeathRecipientsByClientId =
76             new SparseArray<>();
77     private int mNextClientId = 1;
78     private final SparseIntArray mUidByClientId = new SparseIntArray();
79 
WifiAwareServiceImpl(Context context)80     public WifiAwareServiceImpl(Context context) {
81         mContext = context;
82         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
83     }
84 
85     /**
86      * Proxy for the final native call of the parent class. Enables mocking of
87      * the function.
88      */
getMockableCallingUid()89     public int getMockableCallingUid() {
90         return getCallingUid();
91     }
92 
93     /**
94      * Start the service: allocate a new thread (for now), start the handlers of
95      * the components of the service.
96      */
start(HandlerThread handlerThread, WifiAwareStateManager awareStateManager, WifiAwareShellCommand awareShellCommand, WifiAwareMetrics awareMetrics, WifiPermissionsUtil wifiPermissionsUtil, WifiPermissionsWrapper permissionsWrapper, WifiSettingsConfigStore settingsConfigStore, WifiAwareNativeManager wifiAwareNativeManager, WifiAwareNativeApi wifiAwareNativeApi, WifiAwareNativeCallback wifiAwareNativeCallback, NetdWrapper netdWrapper)97     public void start(HandlerThread handlerThread, WifiAwareStateManager awareStateManager,
98             WifiAwareShellCommand awareShellCommand, WifiAwareMetrics awareMetrics,
99             WifiPermissionsUtil wifiPermissionsUtil, WifiPermissionsWrapper permissionsWrapper,
100             WifiSettingsConfigStore settingsConfigStore,
101             WifiAwareNativeManager wifiAwareNativeManager, WifiAwareNativeApi wifiAwareNativeApi,
102             WifiAwareNativeCallback wifiAwareNativeCallback, NetdWrapper netdWrapper) {
103         Log.i(TAG, "Starting Wi-Fi Aware service");
104 
105         mWifiPermissionsUtil = wifiPermissionsUtil;
106         mStateManager = awareStateManager;
107         mShellCommand = awareShellCommand;
108         mHandler = new Handler(handlerThread.getLooper());
109 
110         mHandler.post(() -> {
111             mStateManager.start(mContext, handlerThread.getLooper(), awareMetrics,
112                     wifiPermissionsUtil, permissionsWrapper, new Clock(), netdWrapper);
113 
114             settingsConfigStore.registerChangeListener(
115                     WIFI_VERBOSE_LOGGING_ENABLED,
116                     (key, newValue) -> enableVerboseLogging(newValue, awareStateManager,
117                             wifiAwareNativeManager, wifiAwareNativeApi, wifiAwareNativeCallback),
118                     mHandler);
119             enableVerboseLogging(settingsConfigStore.get(WIFI_VERBOSE_LOGGING_ENABLED),
120                     awareStateManager,
121                     wifiAwareNativeManager, wifiAwareNativeApi,
122                     wifiAwareNativeCallback);
123         });
124     }
125 
enableVerboseLogging(boolean dbg, WifiAwareStateManager awareStateManager, WifiAwareNativeManager wifiAwareNativeManager, WifiAwareNativeApi wifiAwareNativeApi, WifiAwareNativeCallback wifiAwareNativeCallback)126     private void enableVerboseLogging(boolean dbg, WifiAwareStateManager awareStateManager,
127             WifiAwareNativeManager wifiAwareNativeManager, WifiAwareNativeApi wifiAwareNativeApi,
128             WifiAwareNativeCallback wifiAwareNativeCallback) {
129         mDbg = dbg;
130         awareStateManager.enableVerboseLogging(dbg);
131         if (awareStateManager.mDataPathMgr != null) { // needed for unit tests
132             awareStateManager.mDataPathMgr.enableVerboseLogging(dbg);
133         }
134         wifiAwareNativeCallback.enableVerboseLogging(dbg);
135         wifiAwareNativeManager.enableVerboseLogging(dbg);
136         wifiAwareNativeApi.enableVerboseLogging(dbg);
137     }
138 
139     /**
140      * Start/initialize portions of the service which require the boot stage to be complete.
141      */
startLate()142     public void startLate() {
143         Log.i(TAG, "Late initialization of Wi-Fi Aware service");
144 
145         mHandler.post(() -> mStateManager.startLate());
146     }
147 
148     @Override
isUsageEnabled()149     public boolean isUsageEnabled() {
150         enforceAccessPermission();
151 
152         return mStateManager.isUsageEnabled();
153     }
154 
155     @Override
getCharacteristics()156     public Characteristics getCharacteristics() {
157         enforceAccessPermission();
158 
159         return mStateManager.getCapabilities() == null ? null
160                 : mStateManager.getCapabilities().toPublicCharacteristics();
161     }
162 
163     @Override
getAvailableAwareResources()164     public AwareResources getAvailableAwareResources() {
165         enforceAccessPermission();
166         return mStateManager.getAvailableAwareResources();
167     }
168 
169     @Override
isDeviceAttached()170     public boolean isDeviceAttached() {
171         enforceAccessPermission();
172         return mDeathRecipientsByClientId.size() != 0;
173     }
174 
175     @Override
enableInstantCommunicationMode(String callingPackage, boolean enable)176     public void enableInstantCommunicationMode(String callingPackage, boolean enable) {
177         if (!mWifiPermissionsUtil.isSystem(callingPackage, Binder.getCallingUid())) {
178             Log.i(TAG, "enableInstantCommunicationMode not allowed for uid="
179                     + Binder.getCallingUid());
180             return;
181         }
182         enforceChangePermission();
183         mStateManager.enableInstantCommunicationMode(enable);
184     }
185 
186     @Override
isInstantCommunicationModeEnabled()187     public boolean isInstantCommunicationModeEnabled() {
188         enforceAccessPermission();
189         return mStateManager.isInstantCommunicationModeEnabled();
190     }
191 
192     @Override
connect(final IBinder binder, String callingPackage, String callingFeatureId, IWifiAwareEventCallback callback, ConfigRequest configRequest, boolean notifyOnIdentityChanged)193     public void connect(final IBinder binder, String callingPackage, String callingFeatureId,
194             IWifiAwareEventCallback callback, ConfigRequest configRequest,
195             boolean notifyOnIdentityChanged) {
196         enforceAccessPermission();
197         enforceChangePermission();
198 
199         final int uid = getMockableCallingUid();
200         mAppOps.checkPackage(uid, callingPackage);
201 
202         if (callback == null) {
203             throw new IllegalArgumentException("Callback must not be null");
204         }
205         if (binder == null) {
206             throw new IllegalArgumentException("Binder must not be null");
207         }
208 
209         if (notifyOnIdentityChanged) {
210             enforceLocationPermission(callingPackage, callingFeatureId, getMockableCallingUid());
211         }
212 
213         if (configRequest != null) {
214             enforceNetworkStackPermission();
215         } else {
216             configRequest = new ConfigRequest.Builder().build();
217         }
218         configRequest.validate();
219 
220 
221         int pid = getCallingPid();
222 
223         final int clientId;
224         synchronized (mLock) {
225             clientId = mNextClientId++;
226         }
227 
228         if (mDbg) {
229             Log.v(TAG, "connect: uid=" + uid + ", clientId=" + clientId + ", configRequest"
230                     + configRequest + ", notifyOnIdentityChanged=" + notifyOnIdentityChanged);
231         }
232 
233         IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
234             @Override
235             public void binderDied() {
236                 if (mDbg) Log.v(TAG, "binderDied: clientId=" + clientId);
237                 binder.unlinkToDeath(this, 0);
238 
239                 synchronized (mLock) {
240                     mDeathRecipientsByClientId.delete(clientId);
241                     mUidByClientId.delete(clientId);
242                 }
243 
244                 mStateManager.disconnect(clientId);
245             }
246         };
247 
248         try {
249             binder.linkToDeath(dr, 0);
250         } catch (RemoteException e) {
251             Log.e(TAG, "Error on linkToDeath - " + e);
252             try {
253                 callback.onConnectFail(NanStatusType.INTERNAL_FAILURE);
254             } catch (RemoteException e1) {
255                 Log.e(TAG, "Error on onConnectFail()");
256             }
257             return;
258         }
259 
260         synchronized (mLock) {
261             mDeathRecipientsByClientId.put(clientId, dr);
262             mUidByClientId.put(clientId, uid);
263         }
264 
265         mStateManager.connect(clientId, uid, pid, callingPackage, callingFeatureId, callback,
266                 configRequest, notifyOnIdentityChanged);
267     }
268 
269     @Override
disconnect(int clientId, IBinder binder)270     public void disconnect(int clientId, IBinder binder) {
271         enforceAccessPermission();
272         enforceChangePermission();
273 
274         int uid = getMockableCallingUid();
275         enforceClientValidity(uid, clientId);
276         if (mDbg) Log.v(TAG, "disconnect: uid=" + uid + ", clientId=" + clientId);
277 
278         if (binder == null) {
279             throw new IllegalArgumentException("Binder must not be null");
280         }
281 
282         synchronized (mLock) {
283             IBinder.DeathRecipient dr = mDeathRecipientsByClientId.get(clientId);
284             if (dr != null) {
285                 binder.unlinkToDeath(dr, 0);
286                 mDeathRecipientsByClientId.delete(clientId);
287             }
288             mUidByClientId.delete(clientId);
289         }
290 
291         mStateManager.disconnect(clientId);
292     }
293 
294     @Override
terminateSession(int clientId, int sessionId)295     public void terminateSession(int clientId, int sessionId) {
296         enforceAccessPermission();
297         enforceChangePermission();
298 
299         int uid = getMockableCallingUid();
300         enforceClientValidity(uid, clientId);
301         if (mDbg) {
302             Log.v(TAG, "terminateSession: sessionId=" + sessionId + ", uid=" + uid + ", clientId="
303                     + clientId);
304         }
305 
306         mStateManager.terminateSession(clientId, sessionId);
307     }
308 
309     @Override
publish(String callingPackage, String callingFeatureId, int clientId, PublishConfig publishConfig, IWifiAwareDiscoverySessionCallback callback)310     public void publish(String callingPackage, String callingFeatureId, int clientId,
311             PublishConfig publishConfig, IWifiAwareDiscoverySessionCallback callback) {
312         enforceAccessPermission();
313         enforceChangePermission();
314 
315         int uid = getMockableCallingUid();
316         mAppOps.checkPackage(uid, callingPackage);
317 
318         enforceLocationPermission(callingPackage, callingFeatureId, getMockableCallingUid());
319 
320         if (callback == null) {
321             throw new IllegalArgumentException("Callback must not be null");
322         }
323         if (publishConfig == null) {
324             throw new IllegalArgumentException("PublishConfig must not be null");
325         }
326         publishConfig.assertValid(mStateManager.getCharacteristics(),
327                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT));
328 
329         enforceClientValidity(uid, clientId);
330         if (mDbg) {
331             Log.v(TAG, "publish: uid=" + uid + ", clientId=" + clientId + ", publishConfig="
332                     + publishConfig + ", callback=" + callback);
333         }
334 
335         mStateManager.publish(clientId, publishConfig, callback);
336     }
337 
338     @Override
updatePublish(int clientId, int sessionId, PublishConfig publishConfig)339     public void updatePublish(int clientId, int sessionId, PublishConfig publishConfig) {
340         enforceAccessPermission();
341         enforceChangePermission();
342 
343         if (publishConfig == null) {
344             throw new IllegalArgumentException("PublishConfig must not be null");
345         }
346         publishConfig.assertValid(mStateManager.getCharacteristics(),
347                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT));
348 
349         int uid = getMockableCallingUid();
350         enforceClientValidity(uid, clientId);
351         if (mDbg) {
352             Log.v(TAG, "updatePublish: uid=" + uid + ", clientId=" + clientId + ", sessionId="
353                     + sessionId + ", config=" + publishConfig);
354         }
355 
356         mStateManager.updatePublish(clientId, sessionId, publishConfig);
357     }
358 
359     @Override
subscribe(String callingPackage, String callingFeatureId, int clientId, SubscribeConfig subscribeConfig, IWifiAwareDiscoverySessionCallback callback)360     public void subscribe(String callingPackage, String callingFeatureId, int clientId,
361             SubscribeConfig subscribeConfig, IWifiAwareDiscoverySessionCallback callback) {
362         enforceAccessPermission();
363         enforceChangePermission();
364 
365         int uid = getMockableCallingUid();
366         mAppOps.checkPackage(uid, callingPackage);
367 
368         enforceLocationPermission(callingPackage, callingFeatureId, getMockableCallingUid());
369 
370         if (callback == null) {
371             throw new IllegalArgumentException("Callback must not be null");
372         }
373         if (subscribeConfig == null) {
374             throw new IllegalArgumentException("SubscribeConfig must not be null");
375         }
376         subscribeConfig.assertValid(mStateManager.getCharacteristics(),
377                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT));
378 
379         enforceClientValidity(uid, clientId);
380         if (mDbg) {
381             Log.v(TAG, "subscribe: uid=" + uid + ", clientId=" + clientId + ", config="
382                     + subscribeConfig + ", callback=" + callback);
383         }
384 
385         mStateManager.subscribe(clientId, subscribeConfig, callback);
386     }
387 
388     @Override
updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig)389     public void updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig) {
390         enforceAccessPermission();
391         enforceChangePermission();
392 
393         if (subscribeConfig == null) {
394             throw new IllegalArgumentException("SubscribeConfig must not be null");
395         }
396         subscribeConfig.assertValid(mStateManager.getCharacteristics(),
397                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT));
398 
399         int uid = getMockableCallingUid();
400         enforceClientValidity(uid, clientId);
401         if (mDbg) {
402             Log.v(TAG, "updateSubscribe: uid=" + uid + ", clientId=" + clientId + ", sessionId="
403                     + sessionId + ", config=" + subscribeConfig);
404         }
405 
406         mStateManager.updateSubscribe(clientId, sessionId, subscribeConfig);
407     }
408 
409     @Override
sendMessage(int clientId, int sessionId, int peerId, byte[] message, int messageId, int retryCount)410     public void sendMessage(int clientId, int sessionId, int peerId, byte[] message, int messageId,
411             int retryCount) {
412         enforceAccessPermission();
413         enforceChangePermission();
414 
415         if (retryCount != 0) {
416             enforceNetworkStackPermission();
417         }
418 
419         if (message != null && message.length
420                 > mStateManager.getCharacteristics().getMaxServiceSpecificInfoLength()) {
421             throw new IllegalArgumentException(
422                     "Message length longer than supported by device characteristics");
423         }
424         if (retryCount < 0 || retryCount > DiscoverySession.getMaxSendRetryCount()) {
425             throw new IllegalArgumentException("Invalid 'retryCount' must be non-negative "
426                     + "and <= DiscoverySession.MAX_SEND_RETRY_COUNT");
427         }
428 
429         int uid = getMockableCallingUid();
430         enforceClientValidity(uid, clientId);
431         if (mDbg) {
432             Log.v(TAG,
433                     "sendMessage: sessionId=" + sessionId + ", uid=" + uid + ", clientId="
434                             + clientId + ", peerId=" + peerId + ", messageId=" + messageId
435                             + ", retryCount=" + retryCount);
436         }
437 
438         mStateManager.sendMessage(uid, clientId, sessionId, peerId, message, messageId, retryCount);
439     }
440 
441     @Override
requestMacAddresses(int uid, List peerIds, IWifiAwareMacAddressProvider callback)442     public void requestMacAddresses(int uid, List peerIds, IWifiAwareMacAddressProvider callback) {
443         enforceNetworkStackPermission();
444 
445         mStateManager.requestMacAddresses(uid, peerIds, callback);
446     }
447 
448     @Override
handleShellCommand(@onNull ParcelFileDescriptor in, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull String[] args)449     public int handleShellCommand(@NonNull ParcelFileDescriptor in,
450             @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
451             @NonNull String[] args) {
452         return mShellCommand.exec(
453                 this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
454                 args);
455     }
456 
457     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)458     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
459         if (mContext.checkCallingOrSelfPermission(
460                 android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
461             pw.println("Permission Denial: can't dump WifiAwareService from pid="
462                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
463             return;
464         }
465         pw.println("Wi-Fi Aware Service");
466         synchronized (mLock) {
467             pw.println("  mNextClientId: " + mNextClientId);
468             pw.println("  mDeathRecipientsByClientId: " + mDeathRecipientsByClientId);
469             pw.println("  mUidByClientId: " + mUidByClientId);
470         }
471         mStateManager.dump(fd, pw, args);
472     }
473 
enforceClientValidity(int uid, int clientId)474     private void enforceClientValidity(int uid, int clientId) {
475         synchronized (mLock) {
476             int uidIndex = mUidByClientId.indexOfKey(clientId);
477             if (uidIndex < 0 || mUidByClientId.valueAt(uidIndex) != uid) {
478                 throw new SecurityException("Attempting to use invalid uid+clientId mapping: uid="
479                         + uid + ", clientId=" + clientId);
480             }
481         }
482     }
483 
enforceAccessPermission()484     private void enforceAccessPermission() {
485         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, TAG);
486     }
487 
enforceChangePermission()488     private void enforceChangePermission() {
489         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, TAG);
490     }
491 
enforceLocationPermission(String callingPackage, String callingFeatureId, int uid)492     private void enforceLocationPermission(String callingPackage, String callingFeatureId,
493             int uid) {
494         mWifiPermissionsUtil.enforceLocationPermission(callingPackage, callingFeatureId, uid);
495     }
496 
enforceNetworkStackPermission()497     private void enforceNetworkStackPermission() {
498         mContext.enforceCallingOrSelfPermission(Manifest.permission.NETWORK_STACK, TAG);
499     }
500 }
501