WifiAwareServiceImpl.java revision c29acea6ceda3aa4ee537c05ce7d05dac2655cf9
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.content.Context;
20import android.content.pm.PackageManager;
21import android.net.wifi.RttManager;
22import android.net.wifi.aware.ConfigRequest;
23import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
24import android.net.wifi.aware.IWifiAwareEventCallback;
25import android.net.wifi.aware.IWifiAwareManager;
26import android.net.wifi.aware.PublishConfig;
27import android.net.wifi.aware.SubscribeConfig;
28import android.net.wifi.aware.WifiAwareCharacteristics;
29import android.net.wifi.aware.WifiAwareDiscoveryBaseSession;
30import android.os.Binder;
31import android.os.HandlerThread;
32import android.os.IBinder;
33import android.os.RemoteException;
34import android.util.Log;
35import android.util.SparseArray;
36import android.util.SparseIntArray;
37
38import java.io.FileDescriptor;
39import java.io.PrintWriter;
40import java.util.Arrays;
41
42/**
43 * Implementation of the IWifiAwareManager AIDL interface. Performs validity
44 * (permission and clientID-UID mapping) checks and delegates execution to the
45 * WifiAwareStateManager singleton handler. Limited state to feedback which has to
46 * be provided instantly: client and session IDs.
47 */
48public class WifiAwareServiceImpl extends IWifiAwareManager.Stub {
49    private static final String TAG = "WifiAwareService";
50    private static final boolean DBG = false;
51    private static final boolean VDBG = false; // STOPSHIP if true
52
53    private Context mContext;
54    private WifiAwareStateManager mStateManager;
55
56    private final Object mLock = new Object();
57    private final SparseArray<IBinder.DeathRecipient> mDeathRecipientsByClientId =
58            new SparseArray<>();
59    private int mNextClientId = 1;
60    private int mNextRangingId = 1;
61    private final SparseIntArray mUidByClientId = new SparseIntArray();
62
63    public WifiAwareServiceImpl(Context context) {
64        mContext = context.getApplicationContext();
65        mStateManager = WifiAwareStateManager.getInstance();
66    }
67
68    /**
69     * Proxy for the final native call of the parent class. Enables mocking of
70     * the function.
71     */
72    public int getMockableCallingUid() {
73        return getCallingUid();
74    }
75
76    /**
77     * Start the service: allocate a new thread (for now), start the handlers of
78     * the components of the service.
79     */
80    public void start() {
81        Log.i(TAG, "Starting Wi-Fi Aware service");
82
83        // TODO: share worker thread with other Wi-Fi handlers (b/27924886)
84        HandlerThread wifiAwareThread = new HandlerThread("wifiAwareService");
85        wifiAwareThread.start();
86
87        mStateManager.start(mContext, wifiAwareThread.getLooper());
88    }
89
90    /**
91     * Start/initialize portions of the service which require the boot stage to be complete.
92     */
93    public void startLate() {
94        Log.i(TAG, "Late initialization of Wi-Fi Aware service");
95
96        mStateManager.startLate();
97    }
98
99    @Override
100    public void enableUsage() {
101        enforceAccessPermission();
102        enforceChangePermission();
103        enforceConnectivityInternalPermission();
104
105        mStateManager.enableUsage();
106    }
107
108    @Override
109    public void disableUsage() {
110        enforceAccessPermission();
111        enforceChangePermission();
112        enforceConnectivityInternalPermission();
113
114        mStateManager.disableUsage();
115
116        /*
117         * Potential leak (b/27796984) since we keep app information here (uid,
118         * binder-link-to-death), while clearing all state information. However:
119         * (1) can't clear all information since don't have binder, (2)
120         * information will clear once app dies, (3) allows us to do security
121         * checks in the future.
122         */
123    }
124
125    @Override
126    public boolean isUsageEnabled() {
127        enforceAccessPermission();
128
129        return mStateManager.isUsageEnabled();
130    }
131
132    @Override
133    public WifiAwareCharacteristics getCharacteristics() {
134        enforceAccessPermission();
135
136        return mStateManager.getCapabilities() == null ? null
137                : mStateManager.getCapabilities().toPublicCharacteristics();
138    }
139
140    @Override
141    public void connect(final IBinder binder, String callingPackage,
142            IWifiAwareEventCallback callback, ConfigRequest configRequest,
143            boolean notifyOnIdentityChanged) {
144        enforceAccessPermission();
145        enforceChangePermission();
146        if (callback == null) {
147            throw new IllegalArgumentException("Callback must not be null");
148        }
149        if (binder == null) {
150            throw new IllegalArgumentException("Binder must not be null");
151        }
152
153        if (notifyOnIdentityChanged) {
154            enforceLocationPermission();
155        }
156
157        if (configRequest != null) {
158            enforceConnectivityInternalPermission();
159        } else {
160            configRequest = new ConfigRequest.Builder().build();
161        }
162        configRequest.validate();
163
164        final int uid = getMockableCallingUid();
165        int pid = getCallingPid();
166
167        final int clientId;
168        synchronized (mLock) {
169            clientId = mNextClientId++;
170        }
171
172        if (VDBG) {
173            Log.v(TAG, "connect: uid=" + uid + ", clientId=" + clientId + ", configRequest"
174                    + configRequest + ", notifyOnIdentityChanged=" + notifyOnIdentityChanged);
175        }
176
177        IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
178            @Override
179            public void binderDied() {
180                if (DBG) Log.d(TAG, "binderDied: clientId=" + clientId);
181                binder.unlinkToDeath(this, 0);
182
183                synchronized (mLock) {
184                    mDeathRecipientsByClientId.delete(clientId);
185                    mUidByClientId.delete(clientId);
186                }
187
188                mStateManager.disconnect(clientId);
189            }
190        };
191
192        try {
193            binder.linkToDeath(dr, 0);
194        } catch (RemoteException e) {
195            Log.e(TAG, "Error on linkToDeath - " + e);
196            try {
197                callback.onConnectFail(WifiAwareNative.AWARE_STATUS_ERROR);
198            } catch (RemoteException e1) {
199                Log.e(TAG, "Error on onConnectFail()");
200            }
201            return;
202        }
203
204        synchronized (mLock) {
205            mDeathRecipientsByClientId.put(clientId, dr);
206            mUidByClientId.put(clientId, uid);
207        }
208
209        mStateManager.connect(clientId, uid, pid, callingPackage, callback, configRequest,
210                notifyOnIdentityChanged);
211    }
212
213    @Override
214    public void disconnect(int clientId, IBinder binder) {
215        enforceAccessPermission();
216        enforceChangePermission();
217
218        int uid = getMockableCallingUid();
219        enforceClientValidity(uid, clientId);
220        if (VDBG) Log.v(TAG, "disconnect: uid=" + uid + ", clientId=" + clientId);
221
222        if (binder == null) {
223            throw new IllegalArgumentException("Binder must not be null");
224        }
225
226        synchronized (mLock) {
227            IBinder.DeathRecipient dr = mDeathRecipientsByClientId.get(clientId);
228            if (dr != null) {
229                binder.unlinkToDeath(dr, 0);
230                mDeathRecipientsByClientId.delete(clientId);
231            }
232            mUidByClientId.delete(clientId);
233        }
234
235        mStateManager.disconnect(clientId);
236    }
237
238    @Override
239    public void terminateSession(int clientId, int sessionId) {
240        enforceAccessPermission();
241        enforceChangePermission();
242
243        int uid = getMockableCallingUid();
244        enforceClientValidity(uid, clientId);
245        if (VDBG) {
246            Log.v(TAG, "terminateSession: sessionId=" + sessionId + ", uid=" + uid + ", clientId="
247                    + clientId);
248        }
249
250        mStateManager.terminateSession(clientId, sessionId);
251    }
252
253    @Override
254    public void publish(int clientId, PublishConfig publishConfig,
255            IWifiAwareDiscoverySessionCallback callback) {
256        enforceAccessPermission();
257        enforceChangePermission();
258        enforceLocationPermission();
259
260        if (callback == null) {
261            throw new IllegalArgumentException("Callback must not be null");
262        }
263        if (publishConfig == null) {
264            throw new IllegalArgumentException("PublishConfig must not be null");
265        }
266        publishConfig.assertValid(mStateManager.getCharacteristics());
267
268        int uid = getMockableCallingUid();
269        enforceClientValidity(uid, clientId);
270        if (VDBG) {
271            Log.v(TAG, "publish: uid=" + uid + ", clientId=" + clientId + ", publishConfig="
272                    + publishConfig + ", callback=" + callback);
273        }
274
275        mStateManager.publish(clientId, publishConfig, callback);
276    }
277
278    @Override
279    public void updatePublish(int clientId, int sessionId, PublishConfig publishConfig) {
280        enforceAccessPermission();
281        enforceChangePermission();
282
283        if (publishConfig == null) {
284            throw new IllegalArgumentException("PublishConfig must not be null");
285        }
286        publishConfig.assertValid(mStateManager.getCharacteristics());
287
288        int uid = getMockableCallingUid();
289        enforceClientValidity(uid, clientId);
290        if (VDBG) {
291            Log.v(TAG, "updatePublish: uid=" + uid + ", clientId=" + clientId + ", sessionId="
292                    + sessionId + ", config=" + publishConfig);
293        }
294
295        mStateManager.updatePublish(clientId, sessionId, publishConfig);
296    }
297
298    @Override
299    public void subscribe(int clientId, SubscribeConfig subscribeConfig,
300            IWifiAwareDiscoverySessionCallback callback) {
301        enforceAccessPermission();
302        enforceChangePermission();
303        enforceLocationPermission();
304
305        if (callback == null) {
306            throw new IllegalArgumentException("Callback must not be null");
307        }
308        if (subscribeConfig == null) {
309            throw new IllegalArgumentException("SubscribeConfig must not be null");
310        }
311        subscribeConfig.assertValid(mStateManager.getCharacteristics());
312
313        int uid = getMockableCallingUid();
314        enforceClientValidity(uid, clientId);
315        if (VDBG) {
316            Log.v(TAG, "subscribe: uid=" + uid + ", clientId=" + clientId + ", config="
317                    + subscribeConfig + ", callback=" + callback);
318        }
319
320        mStateManager.subscribe(clientId, subscribeConfig, callback);
321    }
322
323    @Override
324    public void updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig) {
325        enforceAccessPermission();
326        enforceChangePermission();
327
328        if (subscribeConfig == null) {
329            throw new IllegalArgumentException("SubscribeConfig must not be null");
330        }
331        subscribeConfig.assertValid(mStateManager.getCharacteristics());
332
333        int uid = getMockableCallingUid();
334        enforceClientValidity(uid, clientId);
335        if (VDBG) {
336            Log.v(TAG, "updateSubscribe: uid=" + uid + ", clientId=" + clientId + ", sessionId="
337                    + sessionId + ", config=" + subscribeConfig);
338        }
339
340        mStateManager.updateSubscribe(clientId, sessionId, subscribeConfig);
341    }
342
343    @Override
344    public void sendMessage(int clientId, int sessionId, int peerId, byte[] message, int messageId,
345            int retryCount) {
346        enforceAccessPermission();
347        enforceChangePermission();
348
349        if (message != null
350                && message.length > mStateManager.getCharacteristics().getMaxServiceNameLength()) {
351            throw new IllegalArgumentException(
352                    "Message length longer than supported by device characteristics");
353        }
354        if (retryCount < 0 || retryCount > WifiAwareDiscoveryBaseSession.getMaxSendRetryCount()) {
355            throw new IllegalArgumentException("Invalid 'retryCount' must be non-negative "
356                    + "and <= WifiAwareDiscoveryBaseSession.MAX_SEND_RETRY_COUNT");
357        }
358
359        int uid = getMockableCallingUid();
360        enforceClientValidity(uid, clientId);
361        if (VDBG) {
362            Log.v(TAG,
363                    "sendMessage: sessionId=" + sessionId + ", uid=" + uid + ", clientId="
364                            + clientId + ", peerId=" + peerId + ", messageId=" + messageId
365                            + ", retryCount=" + retryCount);
366        }
367
368        mStateManager.sendMessage(clientId, sessionId, peerId, message, messageId, retryCount);
369    }
370
371    @Override
372    public int startRanging(int clientId, int sessionId, RttManager.ParcelableRttParams params) {
373        enforceAccessPermission();
374        enforceLocationPermission();
375
376        int uid = getMockableCallingUid();
377        enforceClientValidity(uid, clientId);
378        if (VDBG) {
379            Log.v(TAG, "startRanging: clientId=" + clientId + ", sessionId=" + sessionId + ", "
380                    + ", parms=" + Arrays.toString(params.mParams));
381        }
382
383        if (params.mParams.length == 0) {
384            throw new IllegalArgumentException("Empty ranging parameters");
385        }
386
387        int rangingId;
388        synchronized (mLock) {
389            rangingId = mNextRangingId++;
390        }
391        mStateManager.startRanging(clientId, sessionId, params.mParams, rangingId);
392        return rangingId;
393    }
394
395    @Override
396    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
397        if (mContext.checkCallingOrSelfPermission(
398                android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
399            pw.println("Permission Denial: can't dump WifiAwareService from pid="
400                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
401            return;
402        }
403        pw.println("Wi-Fi Aware Service");
404        synchronized (mLock) {
405            pw.println("  mNextClientId: " + mNextClientId);
406            pw.println("  mDeathRecipientsByClientId: " + mDeathRecipientsByClientId);
407            pw.println("  mUidByClientId: " + mUidByClientId);
408        }
409        mStateManager.dump(fd, pw, args);
410    }
411
412    private void enforceClientValidity(int uid, int clientId) {
413        synchronized (mLock) {
414            int uidIndex = mUidByClientId.indexOfKey(clientId);
415            if (uidIndex < 0 || mUidByClientId.valueAt(uidIndex) != uid) {
416                throw new SecurityException("Attempting to use invalid uid+clientId mapping: uid="
417                        + uid + ", clientId=" + clientId);
418            }
419        }
420    }
421
422    private void enforceAccessPermission() {
423        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, TAG);
424    }
425
426    private void enforceChangePermission() {
427        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, TAG);
428    }
429
430    private void enforceLocationPermission() {
431        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
432                TAG);
433    }
434
435    private void enforceConnectivityInternalPermission() {
436        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
437                TAG);
438    }
439}
440