Home | History | Annotate | Download | only in aware
      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.content.Context;
     20 import android.content.pm.PackageManager;
     21 import android.hardware.wifi.V1_0.NanStatusType;
     22 import android.net.wifi.RttManager;
     23 import android.net.wifi.aware.Characteristics;
     24 import android.net.wifi.aware.ConfigRequest;
     25 import android.net.wifi.aware.DiscoverySession;
     26 import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
     27 import android.net.wifi.aware.IWifiAwareEventCallback;
     28 import android.net.wifi.aware.IWifiAwareManager;
     29 import android.net.wifi.aware.PublishConfig;
     30 import android.net.wifi.aware.SubscribeConfig;
     31 import android.os.Binder;
     32 import android.os.HandlerThread;
     33 import android.os.IBinder;
     34 import android.os.RemoteException;
     35 import android.util.Log;
     36 import android.util.SparseArray;
     37 import android.util.SparseIntArray;
     38 
     39 import java.io.FileDescriptor;
     40 import java.io.PrintWriter;
     41 import 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  */
     49 public 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 boolean isUsageEnabled() {
     98         enforceAccessPermission();
     99 
    100         return mStateManager.isUsageEnabled();
    101     }
    102 
    103     @Override
    104     public Characteristics getCharacteristics() {
    105         enforceAccessPermission();
    106 
    107         return mStateManager.getCapabilities() == null ? null
    108                 : mStateManager.getCapabilities().toPublicCharacteristics();
    109     }
    110 
    111     @Override
    112     public void connect(final IBinder binder, String callingPackage,
    113             IWifiAwareEventCallback callback, ConfigRequest configRequest,
    114             boolean notifyOnIdentityChanged) {
    115         enforceAccessPermission();
    116         enforceChangePermission();
    117         if (callback == null) {
    118             throw new IllegalArgumentException("Callback must not be null");
    119         }
    120         if (binder == null) {
    121             throw new IllegalArgumentException("Binder must not be null");
    122         }
    123 
    124         if (notifyOnIdentityChanged) {
    125             enforceLocationPermission();
    126         }
    127 
    128         if (configRequest != null) {
    129             enforceConnectivityInternalPermission();
    130         } else {
    131             configRequest = new ConfigRequest.Builder().build();
    132         }
    133         configRequest.validate();
    134 
    135         final int uid = getMockableCallingUid();
    136         int pid = getCallingPid();
    137 
    138         final int clientId;
    139         synchronized (mLock) {
    140             clientId = mNextClientId++;
    141         }
    142 
    143         if (VDBG) {
    144             Log.v(TAG, "connect: uid=" + uid + ", clientId=" + clientId + ", configRequest"
    145                     + configRequest + ", notifyOnIdentityChanged=" + notifyOnIdentityChanged);
    146         }
    147 
    148         IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
    149             @Override
    150             public void binderDied() {
    151                 if (DBG) Log.d(TAG, "binderDied: clientId=" + clientId);
    152                 binder.unlinkToDeath(this, 0);
    153 
    154                 synchronized (mLock) {
    155                     mDeathRecipientsByClientId.delete(clientId);
    156                     mUidByClientId.delete(clientId);
    157                 }
    158 
    159                 mStateManager.disconnect(clientId);
    160             }
    161         };
    162 
    163         try {
    164             binder.linkToDeath(dr, 0);
    165         } catch (RemoteException e) {
    166             Log.e(TAG, "Error on linkToDeath - " + e);
    167             try {
    168                 callback.onConnectFail(NanStatusType.INTERNAL_FAILURE);
    169             } catch (RemoteException e1) {
    170                 Log.e(TAG, "Error on onConnectFail()");
    171             }
    172             return;
    173         }
    174 
    175         synchronized (mLock) {
    176             mDeathRecipientsByClientId.put(clientId, dr);
    177             mUidByClientId.put(clientId, uid);
    178         }
    179 
    180         mStateManager.connect(clientId, uid, pid, callingPackage, callback, configRequest,
    181                 notifyOnIdentityChanged);
    182     }
    183 
    184     @Override
    185     public void disconnect(int clientId, IBinder binder) {
    186         enforceAccessPermission();
    187         enforceChangePermission();
    188 
    189         int uid = getMockableCallingUid();
    190         enforceClientValidity(uid, clientId);
    191         if (VDBG) Log.v(TAG, "disconnect: uid=" + uid + ", clientId=" + clientId);
    192 
    193         if (binder == null) {
    194             throw new IllegalArgumentException("Binder must not be null");
    195         }
    196 
    197         synchronized (mLock) {
    198             IBinder.DeathRecipient dr = mDeathRecipientsByClientId.get(clientId);
    199             if (dr != null) {
    200                 binder.unlinkToDeath(dr, 0);
    201                 mDeathRecipientsByClientId.delete(clientId);
    202             }
    203             mUidByClientId.delete(clientId);
    204         }
    205 
    206         mStateManager.disconnect(clientId);
    207     }
    208 
    209     @Override
    210     public void terminateSession(int clientId, int sessionId) {
    211         enforceAccessPermission();
    212         enforceChangePermission();
    213 
    214         int uid = getMockableCallingUid();
    215         enforceClientValidity(uid, clientId);
    216         if (VDBG) {
    217             Log.v(TAG, "terminateSession: sessionId=" + sessionId + ", uid=" + uid + ", clientId="
    218                     + clientId);
    219         }
    220 
    221         mStateManager.terminateSession(clientId, sessionId);
    222     }
    223 
    224     @Override
    225     public void publish(int clientId, PublishConfig publishConfig,
    226             IWifiAwareDiscoverySessionCallback callback) {
    227         enforceAccessPermission();
    228         enforceChangePermission();
    229         enforceLocationPermission();
    230 
    231         if (callback == null) {
    232             throw new IllegalArgumentException("Callback must not be null");
    233         }
    234         if (publishConfig == null) {
    235             throw new IllegalArgumentException("PublishConfig must not be null");
    236         }
    237         publishConfig.assertValid(mStateManager.getCharacteristics());
    238 
    239         int uid = getMockableCallingUid();
    240         enforceClientValidity(uid, clientId);
    241         if (VDBG) {
    242             Log.v(TAG, "publish: uid=" + uid + ", clientId=" + clientId + ", publishConfig="
    243                     + publishConfig + ", callback=" + callback);
    244         }
    245 
    246         mStateManager.publish(clientId, publishConfig, callback);
    247     }
    248 
    249     @Override
    250     public void updatePublish(int clientId, int sessionId, PublishConfig publishConfig) {
    251         enforceAccessPermission();
    252         enforceChangePermission();
    253 
    254         if (publishConfig == null) {
    255             throw new IllegalArgumentException("PublishConfig must not be null");
    256         }
    257         publishConfig.assertValid(mStateManager.getCharacteristics());
    258 
    259         int uid = getMockableCallingUid();
    260         enforceClientValidity(uid, clientId);
    261         if (VDBG) {
    262             Log.v(TAG, "updatePublish: uid=" + uid + ", clientId=" + clientId + ", sessionId="
    263                     + sessionId + ", config=" + publishConfig);
    264         }
    265 
    266         mStateManager.updatePublish(clientId, sessionId, publishConfig);
    267     }
    268 
    269     @Override
    270     public void subscribe(int clientId, SubscribeConfig subscribeConfig,
    271             IWifiAwareDiscoverySessionCallback callback) {
    272         enforceAccessPermission();
    273         enforceChangePermission();
    274         enforceLocationPermission();
    275 
    276         if (callback == null) {
    277             throw new IllegalArgumentException("Callback must not be null");
    278         }
    279         if (subscribeConfig == null) {
    280             throw new IllegalArgumentException("SubscribeConfig must not be null");
    281         }
    282         subscribeConfig.assertValid(mStateManager.getCharacteristics());
    283 
    284         int uid = getMockableCallingUid();
    285         enforceClientValidity(uid, clientId);
    286         if (VDBG) {
    287             Log.v(TAG, "subscribe: uid=" + uid + ", clientId=" + clientId + ", config="
    288                     + subscribeConfig + ", callback=" + callback);
    289         }
    290 
    291         mStateManager.subscribe(clientId, subscribeConfig, callback);
    292     }
    293 
    294     @Override
    295     public void updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig) {
    296         enforceAccessPermission();
    297         enforceChangePermission();
    298 
    299         if (subscribeConfig == null) {
    300             throw new IllegalArgumentException("SubscribeConfig must not be null");
    301         }
    302         subscribeConfig.assertValid(mStateManager.getCharacteristics());
    303 
    304         int uid = getMockableCallingUid();
    305         enforceClientValidity(uid, clientId);
    306         if (VDBG) {
    307             Log.v(TAG, "updateSubscribe: uid=" + uid + ", clientId=" + clientId + ", sessionId="
    308                     + sessionId + ", config=" + subscribeConfig);
    309         }
    310 
    311         mStateManager.updateSubscribe(clientId, sessionId, subscribeConfig);
    312     }
    313 
    314     @Override
    315     public void sendMessage(int clientId, int sessionId, int peerId, byte[] message, int messageId,
    316             int retryCount) {
    317         enforceAccessPermission();
    318         enforceChangePermission();
    319 
    320         if (retryCount != 0) {
    321             enforceConnectivityInternalPermission();
    322         }
    323 
    324         if (message != null
    325                 && message.length > mStateManager.getCharacteristics().getMaxServiceNameLength()) {
    326             throw new IllegalArgumentException(
    327                     "Message length longer than supported by device characteristics");
    328         }
    329         if (retryCount < 0 || retryCount > DiscoverySession.getMaxSendRetryCount()) {
    330             throw new IllegalArgumentException("Invalid 'retryCount' must be non-negative "
    331                     + "and <= DiscoverySession.MAX_SEND_RETRY_COUNT");
    332         }
    333 
    334         int uid = getMockableCallingUid();
    335         enforceClientValidity(uid, clientId);
    336         if (VDBG) {
    337             Log.v(TAG,
    338                     "sendMessage: sessionId=" + sessionId + ", uid=" + uid + ", clientId="
    339                             + clientId + ", peerId=" + peerId + ", messageId=" + messageId
    340                             + ", retryCount=" + retryCount);
    341         }
    342 
    343         mStateManager.sendMessage(clientId, sessionId, peerId, message, messageId, retryCount);
    344     }
    345 
    346     @Override
    347     public int startRanging(int clientId, int sessionId, RttManager.ParcelableRttParams params) {
    348         enforceAccessPermission();
    349         enforceLocationPermission();
    350 
    351         // TODO: b/35676064 restricts access to this API until decide if will open.
    352         enforceConnectivityInternalPermission();
    353 
    354         int uid = getMockableCallingUid();
    355         enforceClientValidity(uid, clientId);
    356         if (VDBG) {
    357             Log.v(TAG, "startRanging: clientId=" + clientId + ", sessionId=" + sessionId + ", "
    358                     + ", parms=" + Arrays.toString(params.mParams));
    359         }
    360 
    361         if (params.mParams.length == 0) {
    362             throw new IllegalArgumentException("Empty ranging parameters");
    363         }
    364 
    365         int rangingId;
    366         synchronized (mLock) {
    367             rangingId = mNextRangingId++;
    368         }
    369         mStateManager.startRanging(clientId, sessionId, params.mParams, rangingId);
    370         return rangingId;
    371     }
    372 
    373     @Override
    374     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    375         if (mContext.checkCallingOrSelfPermission(
    376                 android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
    377             pw.println("Permission Denial: can't dump WifiAwareService from pid="
    378                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
    379             return;
    380         }
    381         pw.println("Wi-Fi Aware Service");
    382         synchronized (mLock) {
    383             pw.println("  mNextClientId: " + mNextClientId);
    384             pw.println("  mDeathRecipientsByClientId: " + mDeathRecipientsByClientId);
    385             pw.println("  mUidByClientId: " + mUidByClientId);
    386         }
    387         mStateManager.dump(fd, pw, args);
    388     }
    389 
    390     private void enforceClientValidity(int uid, int clientId) {
    391         synchronized (mLock) {
    392             int uidIndex = mUidByClientId.indexOfKey(clientId);
    393             if (uidIndex < 0 || mUidByClientId.valueAt(uidIndex) != uid) {
    394                 throw new SecurityException("Attempting to use invalid uid+clientId mapping: uid="
    395                         + uid + ", clientId=" + clientId);
    396             }
    397         }
    398     }
    399 
    400     private void enforceAccessPermission() {
    401         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, TAG);
    402     }
    403 
    404     private void enforceChangePermission() {
    405         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, TAG);
    406     }
    407 
    408     private void enforceLocationPermission() {
    409         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
    410                 TAG);
    411     }
    412 
    413     private void enforceConnectivityInternalPermission() {
    414         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
    415                 TAG);
    416     }
    417 }
    418