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.nan;
18
19import android.content.Context;
20import android.content.pm.PackageManager;
21import android.net.wifi.nan.ConfigRequest;
22import android.net.wifi.nan.IWifiNanEventListener;
23import android.net.wifi.nan.IWifiNanManager;
24import android.net.wifi.nan.IWifiNanSessionListener;
25import android.net.wifi.nan.PublishData;
26import android.net.wifi.nan.PublishSettings;
27import android.net.wifi.nan.SubscribeData;
28import android.net.wifi.nan.SubscribeSettings;
29import android.os.Binder;
30import android.os.HandlerThread;
31import android.os.IBinder;
32import android.os.RemoteException;
33import android.util.Log;
34import android.util.SparseArray;
35
36import java.io.FileDescriptor;
37import java.io.PrintWriter;
38
39public class WifiNanServiceImpl extends IWifiNanManager.Stub {
40    private static final String TAG = "WifiNanService";
41    private static final boolean DBG = false;
42    private static final boolean VDBG = false; // STOPSHIP if true
43
44    private Context mContext;
45    private WifiNanStateManager mStateManager;
46    private final boolean mNanSupported;
47
48    private final Object mLock = new Object();
49    private final SparseArray<IBinder.DeathRecipient> mDeathRecipientsByUid = new SparseArray<>();
50    private int mNextNetworkRequestToken = 1;
51    private int mNextSessionId = 1;
52
53    public WifiNanServiceImpl(Context context) {
54        mContext = context.getApplicationContext();
55
56        mNanSupported = mContext.getPackageManager()
57                .hasSystemFeature(PackageManager.FEATURE_WIFI_NAN);
58        if (DBG) Log.w(TAG, "WifiNanServiceImpl: mNanSupported=" + mNanSupported);
59
60        mStateManager = WifiNanStateManager.getInstance();
61    }
62
63    public void start() {
64        Log.i(TAG, "Starting Wi-Fi NAN service");
65
66        // TODO: share worker thread with other Wi-Fi handlers
67        HandlerThread wifiNanThread = new HandlerThread("wifiNanService");
68        wifiNanThread.start();
69
70        mStateManager.start(wifiNanThread.getLooper());
71    }
72
73    @Override
74    public void connect(final IBinder binder, IWifiNanEventListener listener, int events) {
75        enforceAccessPermission();
76        enforceChangePermission();
77
78        final int uid = getCallingUid();
79
80        if (VDBG) Log.v(TAG, "connect: uid=" + uid);
81
82
83        IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
84            @Override
85            public void binderDied() {
86                if (DBG) Log.d(TAG, "binderDied: uid=" + uid);
87                binder.unlinkToDeath(this, 0);
88
89                synchronized (mLock) {
90                    mDeathRecipientsByUid.delete(uid);
91                }
92
93                mStateManager.disconnect(uid);
94            }
95        };
96        synchronized (mLock) {
97            mDeathRecipientsByUid.put(uid, dr);
98        }
99        try {
100            binder.linkToDeath(dr, 0);
101        } catch (RemoteException e) {
102            Log.w(TAG, "Error on linkToDeath - " + e);
103        }
104
105        mStateManager.connect(uid, listener, events);
106    }
107
108    @Override
109    public void disconnect(IBinder binder) {
110        enforceAccessPermission();
111        enforceChangePermission();
112
113        int uid = getCallingUid();
114
115        if (VDBG) Log.v(TAG, "disconnect: uid=" + uid);
116
117        synchronized (mLock) {
118            IBinder.DeathRecipient dr = mDeathRecipientsByUid.get(uid);
119            if (dr != null) {
120                binder.unlinkToDeath(dr, 0);
121                mDeathRecipientsByUid.delete(uid);
122            }
123        }
124
125        mStateManager.disconnect(uid);
126    }
127
128    @Override
129    public void requestConfig(ConfigRequest configRequest) {
130        enforceAccessPermission();
131        enforceChangePermission();
132
133        if (VDBG) {
134            Log.v(TAG,
135                    "requestConfig: uid=" + getCallingUid() + ", configRequest=" + configRequest);
136        }
137
138        mStateManager.requestConfig(getCallingUid(), configRequest);
139    }
140
141    @Override
142    public void stopSession(int sessionId) {
143        enforceAccessPermission();
144        enforceChangePermission();
145
146        if (VDBG) Log.v(TAG, "stopSession: sessionId=" + sessionId + ", uid=" + getCallingUid());
147
148        mStateManager.stopSession(getCallingUid(), sessionId);
149    }
150
151    @Override
152    public void destroySession(int sessionId) {
153        enforceAccessPermission();
154        enforceChangePermission();
155
156        if (VDBG) Log.v(TAG, "destroySession: sessionId=" + sessionId + ", uid=" + getCallingUid());
157
158        mStateManager.destroySession(getCallingUid(), sessionId);
159    }
160
161    @Override
162    public int createSession(IWifiNanSessionListener listener, int events) {
163        enforceAccessPermission();
164        enforceChangePermission();
165
166        if (VDBG) Log.v(TAG, "createSession: uid=" + getCallingUid());
167
168        int sessionId;
169        synchronized (mLock) {
170            sessionId = mNextSessionId++;
171        }
172
173        mStateManager.createSession(getCallingUid(), sessionId, listener, events);
174
175        return sessionId;
176    }
177
178    @Override
179    public void publish(int sessionId, PublishData publishData, PublishSettings publishSettings) {
180        enforceAccessPermission();
181        enforceChangePermission();
182
183        if (VDBG) {
184            Log.v(TAG, "publish: uid=" + getCallingUid() + ", sessionId=" + sessionId + ", data='"
185                    + publishData + "', settings=" + publishSettings);
186        }
187
188        mStateManager.publish(getCallingUid(), sessionId, publishData, publishSettings);
189    }
190
191    @Override
192    public void subscribe(int sessionId, SubscribeData subscribeData,
193            SubscribeSettings subscribeSettings) {
194        enforceAccessPermission();
195        enforceChangePermission();
196
197        if (VDBG) {
198            Log.v(TAG, "subscribe: uid=" + getCallingUid() + ", sessionId=" + sessionId + ", data='"
199                    + subscribeData + "', settings=" + subscribeSettings);
200        }
201
202        mStateManager.subscribe(getCallingUid(), sessionId, subscribeData, subscribeSettings);
203    }
204
205    @Override
206    public void sendMessage(int sessionId, int peerId, byte[] message, int messageLength,
207            int messageId) {
208        enforceAccessPermission();
209        enforceChangePermission();
210
211        if (VDBG) {
212            Log.v(TAG,
213                    "sendMessage: sessionId=" + sessionId + ", uid=" + getCallingUid() + ", peerId="
214                            + peerId + ", messageLength=" + messageLength + ", messageId="
215                            + messageId);
216        }
217
218        mStateManager.sendMessage(getCallingUid(), sessionId, peerId, message, messageLength,
219                messageId);
220    }
221
222    @Override
223    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
224        if (mContext.checkCallingOrSelfPermission(
225                android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
226            pw.println("Permission Denial: can't dump WifiNanService from pid="
227                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
228            return;
229        }
230        pw.println("Wi-Fi NAN Service");
231        pw.println("  mNanSupported: " + mNanSupported);
232        pw.println("  mNextNetworkRequestToken: " + mNextNetworkRequestToken);
233        pw.println("  mNextSessionId: " + mNextSessionId);
234        pw.println("  mDeathRecipientsByUid: " + mDeathRecipientsByUid);
235        mStateManager.dump(fd, pw, args);
236    }
237
238    private void enforceAccessPermission() {
239        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, TAG);
240    }
241
242    private void enforceChangePermission() {
243        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, TAG);
244    }
245}
246