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