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