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
17package com.android.server.wifi.aware;
18
19import android.Manifest;
20import android.app.AppOpsManager;
21import android.content.Context;
22import android.content.pm.PackageManager;
23import android.database.ContentObserver;
24import android.hardware.wifi.V1_0.NanStatusType;
25import android.net.wifi.aware.Characteristics;
26import android.net.wifi.aware.ConfigRequest;
27import android.net.wifi.aware.DiscoverySession;
28import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
29import android.net.wifi.aware.IWifiAwareEventCallback;
30import android.net.wifi.aware.IWifiAwareMacAddressProvider;
31import android.net.wifi.aware.IWifiAwareManager;
32import android.net.wifi.aware.PublishConfig;
33import android.net.wifi.aware.SubscribeConfig;
34import android.os.Binder;
35import android.os.Handler;
36import android.os.HandlerThread;
37import android.os.IBinder;
38import android.os.RemoteException;
39import android.os.ResultReceiver;
40import android.os.ShellCallback;
41import android.provider.Settings;
42import android.util.Log;
43import android.util.SparseArray;
44import android.util.SparseIntArray;
45
46import com.android.server.wifi.FrameworkFacade;
47import com.android.server.wifi.WifiInjector;
48import com.android.server.wifi.util.WifiPermissionsUtil;
49import com.android.server.wifi.util.WifiPermissionsWrapper;
50
51import java.io.FileDescriptor;
52import java.io.PrintWriter;
53import 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 */
61public 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
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     */
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     */
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
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     */
153    public void startLate() {
154        Log.i(TAG, "Late initialization of Wi-Fi Aware service");
155
156        mStateManager.startLate();
157    }
158
159    @Override
160    public boolean isUsageEnabled() {
161        enforceAccessPermission();
162
163        return mStateManager.isUsageEnabled();
164    }
165
166    @Override
167    public Characteristics getCharacteristics() {
168        enforceAccessPermission();
169
170        return mStateManager.getCapabilities() == null ? null
171                : mStateManager.getCapabilities().toPublicCharacteristics();
172    }
173
174    @Override
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
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
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
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
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
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
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
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
424    public void requestMacAddresses(int uid, List peerIds, IWifiAwareMacAddressProvider callback) {
425        enforceNetworkStackPermission();
426
427        mStateManager.requestMacAddresses(uid, peerIds, callback);
428    }
429
430    @Override
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
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
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
463    private void enforceAccessPermission() {
464        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, TAG);
465    }
466
467    private void enforceChangePermission() {
468        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, TAG);
469    }
470
471    private void enforceLocationPermission(String callingPackage, int uid) {
472        mWifiPermissionsUtil.enforceLocationPermission(callingPackage, uid);
473    }
474
475    private void enforceNetworkStackPermission() {
476        mContext.enforceCallingOrSelfPermission(Manifest.permission.NETWORK_STACK, TAG);
477    }
478}
479