1 /*
2  * Copyright 2023 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.bluetooth;
18 
19 import static android.Manifest.permission.BLUETOOTH_CONNECT;
20 import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
21 import static android.Manifest.permission.DUMP;
22 import static android.Manifest.permission.LOCAL_MAC_ADDRESS;
23 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
24 
25 import static com.android.server.bluetooth.BtPermissionUtils.checkConnectPermissionForDataDelivery;
26 import static com.android.server.bluetooth.BtPermissionUtils.getCallingAppId;
27 import static com.android.server.bluetooth.BtPermissionUtils.isCallerSystem;
28 
29 import static java.util.Objects.requireNonNull;
30 
31 import android.annotation.NonNull;
32 import android.annotation.Nullable;
33 import android.annotation.RequiresPermission;
34 import android.app.AppOpsManager;
35 import android.bluetooth.BluetoothAdapter;
36 import android.bluetooth.IBluetooth;
37 import android.bluetooth.IBluetoothManager;
38 import android.bluetooth.IBluetoothManagerCallback;
39 import android.content.AttributionSource;
40 import android.content.Context;
41 import android.os.Build;
42 import android.os.IBinder;
43 import android.os.Looper;
44 import android.os.ParcelFileDescriptor;
45 import android.os.UserManager;
46 import android.permission.PermissionManager;
47 
48 import androidx.annotation.RequiresApi;
49 
50 import java.io.FileDescriptor;
51 import java.io.PrintWriter;
52 
53 class BluetoothServiceBinder extends IBluetoothManager.Stub {
54     private static final String TAG = BluetoothServiceBinder.class.getSimpleName();
55 
56     private final BluetoothManagerService mBluetoothManagerService;
57     private final Context mContext;
58     private final UserManager mUserManager;
59     private final AppOpsManager mAppOpsManager;
60     private final PermissionManager mPermissionManager;
61     private final BtPermissionUtils mPermissionUtils;
62     private final Looper unusedmLooper;
63 
BluetoothServiceBinder( BluetoothManagerService bms, Looper looper, Context ctx, UserManager userManager)64     BluetoothServiceBinder(
65             BluetoothManagerService bms, Looper looper, Context ctx, UserManager userManager) {
66         mBluetoothManagerService = bms;
67         unusedmLooper = looper;
68         mContext = ctx;
69         mUserManager = userManager;
70         mAppOpsManager =
71                 requireNonNull(
72                         ctx.getSystemService(AppOpsManager.class),
73                         "AppOpsManager system service cannot be null");
74         mPermissionManager =
75                 requireNonNull(
76                         ctx.getSystemService(PermissionManager.class),
77                         "PermissionManager system service cannot be null");
78         mPermissionUtils = new BtPermissionUtils(ctx);
79     }
80 
81     @Override
82     @Nullable
registerAdapter(@onNull IBluetoothManagerCallback callback)83     public IBinder registerAdapter(@NonNull IBluetoothManagerCallback callback) {
84         requireNonNull(callback, "Callback cannot be null in registerAdapter");
85         IBluetooth bluetooth = mBluetoothManagerService.registerAdapter(callback);
86         if (bluetooth == null) {
87             return null;
88         }
89         return bluetooth.asBinder();
90     }
91 
92     @Override
unregisterAdapter(@onNull IBluetoothManagerCallback callback)93     public void unregisterAdapter(@NonNull IBluetoothManagerCallback callback) {
94         requireNonNull(callback, "Callback cannot be null in unregisterAdapter");
95         mBluetoothManagerService.unregisterAdapter(callback);
96     }
97 
98     @Override
enable(@onNull AttributionSource source)99     public boolean enable(@NonNull AttributionSource source) {
100         requireNonNull(source, "AttributionSource cannot be null in enable");
101 
102         final String errorMsg =
103                 mPermissionUtils.callerCanToggle(
104                         mContext,
105                         source,
106                         mUserManager,
107                         mAppOpsManager,
108                         mPermissionManager,
109                         "enable",
110                         true);
111         if (!errorMsg.isEmpty()) {
112             Log.d(TAG, "enable(): FAILED: " + errorMsg);
113             return false;
114         }
115 
116         return mBluetoothManagerService.enable(source.getPackageName());
117     }
118 
119     @Override
enableNoAutoConnect(AttributionSource source)120     public boolean enableNoAutoConnect(AttributionSource source) {
121         requireNonNull(source, "AttributionSource cannot be null in enableNoAutoConnect");
122 
123         final String errorMsg =
124                 mPermissionUtils.callerCanToggle(
125                         mContext,
126                         source,
127                         mUserManager,
128                         mAppOpsManager,
129                         mPermissionManager,
130                         "enableNoAutoConnect",
131                         false);
132         if (!errorMsg.isEmpty()) {
133             Log.d(TAG, "enableNoAutoConnect(): FAILED: " + errorMsg);
134             return false;
135         }
136 
137         if (!BtPermissionUtils.isCallerNfc(getCallingAppId())) {
138             throw new SecurityException("No permission to enable Bluetooth quietly");
139         }
140 
141         return mBluetoothManagerService.enableNoAutoConnect(source.getPackageName());
142     }
143 
144     @Override
disable(AttributionSource source, boolean persist)145     public boolean disable(AttributionSource source, boolean persist) {
146         requireNonNull(source, "AttributionSource cannot be null in disable");
147 
148         if (!persist) {
149             BtPermissionUtils.enforcePrivileged(mContext);
150         }
151 
152         final String errorMsg =
153                 mPermissionUtils.callerCanToggle(
154                         mContext,
155                         source,
156                         mUserManager,
157                         mAppOpsManager,
158                         mPermissionManager,
159                         "disable",
160                         true);
161         if (!errorMsg.isEmpty()) {
162             Log.d(TAG, "disable(): FAILED: " + errorMsg);
163             return false;
164         }
165 
166         return mBluetoothManagerService.disable(source.getPackageName(), persist);
167     }
168 
169     @Override
getState()170     public int getState() {
171         if (!isCallerSystem(getCallingAppId())
172                 && !mPermissionUtils.checkIfCallerIsForegroundUser(mUserManager)) {
173             Log.w(TAG, "getState(): UNAUTHORIZED. Report OFF for non-active and non system user");
174             return BluetoothAdapter.STATE_OFF;
175         }
176 
177         return mBluetoothManagerService.getState();
178     }
179 
180     @Override
181     @RequiresPermission(allOf = {BLUETOOTH_CONNECT, LOCAL_MAC_ADDRESS})
getAddress(AttributionSource source)182     public String getAddress(AttributionSource source) {
183         requireNonNull(source, "AttributionSource cannot be null in getAddress");
184 
185         if (!checkConnectPermissionForDataDelivery(
186                 mContext, mPermissionManager, source, "getAddress")) {
187             return null;
188         }
189 
190         if (!isCallerSystem(getCallingAppId())
191                 && !mPermissionUtils.checkIfCallerIsForegroundUser(mUserManager)) {
192             Log.w(TAG, "getAddress(): Not allowed for non-active and non system user");
193             return null;
194         }
195 
196         if (mContext.checkCallingOrSelfPermission(LOCAL_MAC_ADDRESS) != PERMISSION_GRANTED) {
197             // TODO(b/280890575): Throws a SecurityException instead
198             Log.w(TAG, "getAddress(): Client does not have LOCAL_MAC_ADDRESS permission");
199             return BluetoothAdapter.DEFAULT_MAC_ADDRESS;
200         }
201 
202         return mBluetoothManagerService.getAddress();
203     }
204 
205     @Override
206     @RequiresPermission(BLUETOOTH_CONNECT)
getName(AttributionSource source)207     public String getName(AttributionSource source) {
208         requireNonNull(source, "AttributionSource cannot be null in getName");
209 
210         if (!checkConnectPermissionForDataDelivery(
211                 mContext, mPermissionManager, source, "getName")) {
212             return null;
213         }
214 
215         if (!isCallerSystem(getCallingAppId())
216                 && !mPermissionUtils.checkIfCallerIsForegroundUser(mUserManager)) {
217             Log.w(TAG, "getName(): not allowed for non-active and non system user");
218             return null;
219         }
220 
221         return mBluetoothManagerService.getName();
222     }
223 
224     @Override
225     @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
onFactoryReset(AttributionSource source)226     public boolean onFactoryReset(AttributionSource source) {
227         requireNonNull(source, "AttributionSource cannot be null in onFactoryReset");
228 
229         BtPermissionUtils.enforcePrivileged(mContext);
230 
231         if (!checkConnectPermissionForDataDelivery(
232                 mContext, mPermissionManager, source, "onFactoryReset")) {
233             return false;
234         }
235 
236         return mBluetoothManagerService.onFactoryReset();
237     }
238 
239     @Override
isBleScanAvailable()240     public boolean isBleScanAvailable() {
241         return mBluetoothManagerService.isBleScanAvailable();
242     }
243 
244     @Override
245     @RequiresPermission(BLUETOOTH_CONNECT)
enableBle(AttributionSource source, IBinder token)246     public boolean enableBle(AttributionSource source, IBinder token) {
247         requireNonNull(source, "AttributionSource cannot be null in enableBle");
248         requireNonNull(token, "IBinder cannot be null in enableBle");
249 
250         final String errorMsg =
251                 mPermissionUtils.callerCanToggle(
252                         mContext,
253                         source,
254                         mUserManager,
255                         mAppOpsManager,
256                         mPermissionManager,
257                         "enableBle",
258                         false);
259         if (!errorMsg.isEmpty()) {
260             Log.d(TAG, "enableBle(): FAILED: " + errorMsg);
261             return false;
262         }
263 
264         return mBluetoothManagerService.enableBle(source.getPackageName(), token);
265     }
266 
267     @Override
268     @RequiresPermission(BLUETOOTH_CONNECT)
disableBle(AttributionSource source, IBinder token)269     public boolean disableBle(AttributionSource source, IBinder token) {
270         requireNonNull(source, "AttributionSource cannot be null in disableBle");
271         requireNonNull(token, "IBinder cannot be null in disableBle");
272 
273         final String errorMsg =
274                 mPermissionUtils.callerCanToggle(
275                         mContext,
276                         source,
277                         mUserManager,
278                         mAppOpsManager,
279                         mPermissionManager,
280                         "disableBle",
281                         false);
282         if (!errorMsg.isEmpty()) {
283             Log.d(TAG, "disableBle(): FAILED: " + errorMsg);
284             return false;
285         }
286 
287         return mBluetoothManagerService.disableBle(source.getPackageName(), token);
288     }
289 
290     @Override
isHearingAidProfileSupported()291     public boolean isHearingAidProfileSupported() {
292         return mBluetoothManagerService.isHearingAidProfileSupported();
293     }
294 
295     @Override
296     @RequiresPermission(BLUETOOTH_PRIVILEGED)
setBtHciSnoopLogMode(int mode)297     public int setBtHciSnoopLogMode(int mode) {
298         BtPermissionUtils.enforcePrivileged(mContext);
299 
300         return mBluetoothManagerService.setBtHciSnoopLogMode(mode);
301     }
302 
303     @Override
304     @RequiresPermission(BLUETOOTH_PRIVILEGED)
getBtHciSnoopLogMode()305     public int getBtHciSnoopLogMode() {
306         BtPermissionUtils.enforcePrivileged(mContext);
307 
308         return mBluetoothManagerService.getBtHciSnoopLogMode();
309     }
310 
311     @Override
handleShellCommand( @onNull ParcelFileDescriptor in, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull String[] args)312     public int handleShellCommand(
313             @NonNull ParcelFileDescriptor in,
314             @NonNull ParcelFileDescriptor out,
315             @NonNull ParcelFileDescriptor err,
316             @NonNull String[] args) {
317         return new BluetoothShellCommand(mBluetoothManagerService)
318                 .exec(
319                         this,
320                         in.getFileDescriptor(),
321                         out.getFileDescriptor(),
322                         err.getFileDescriptor(),
323                         args);
324     }
325 
326     @Override
327     @RequiresPermission(BLUETOOTH_PRIVILEGED)
isAutoOnSupported()328     public boolean isAutoOnSupported() {
329         BtPermissionUtils.enforcePrivileged(mContext);
330         return mBluetoothManagerService.isAutoOnSupported();
331     }
332 
333     @Override
334     @RequiresPermission(BLUETOOTH_PRIVILEGED)
isAutoOnEnabled()335     public boolean isAutoOnEnabled() {
336         BtPermissionUtils.enforcePrivileged(mContext);
337         return mBluetoothManagerService.isAutoOnEnabled();
338     }
339 
340     @Override
341     @RequiresPermission(BLUETOOTH_PRIVILEGED)
342     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
setAutoOnEnabled(boolean status)343     public void setAutoOnEnabled(boolean status) {
344         BtPermissionUtils.enforcePrivileged(mContext);
345         mBluetoothManagerService.setAutoOnEnabled(status);
346     }
347 
348     @Override
349     @RequiresPermission(DUMP)
dump(FileDescriptor fd, PrintWriter writer, String[] args)350     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
351         if (mContext.checkCallingOrSelfPermission(DUMP) != PERMISSION_GRANTED) {
352             // TODO(b/280890575): Throws SecurityException instead
353             Log.w(TAG, "dump(): Client does not have DUMP permission");
354             return;
355         }
356 
357         mBluetoothManagerService.dump(fd, writer, args);
358     }
359 }
360