• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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.bluetooth.btservice;
18 
19 import static android.Manifest.permission.BLUETOOTH_CONNECT;
20 
21 import android.annotation.SuppressLint;
22 import android.app.ActivityManager;
23 import android.app.Service;
24 import android.bluetooth.BluetoothAdapter;
25 import android.bluetooth.BluetoothDevice;
26 import android.content.Attributable;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.pm.PackageManager;
32 import android.os.IBinder;
33 import android.os.UserHandle;
34 import android.os.UserManager;
35 import android.util.Log;
36 
37 import com.android.bluetooth.BluetoothMetricsProto;
38 import com.android.bluetooth.Utils;
39 
40 /**
41  * Base class for a background service that runs a Bluetooth profile
42  */
43 public abstract class ProfileService extends Service {
44     private static final boolean DBG = false;
45 
46     public static final String BLUETOOTH_PRIVILEGED =
47             android.Manifest.permission.BLUETOOTH_PRIVILEGED;
48 
49     public interface IProfileServiceBinder extends IBinder {
50         /**
51          * Called in {@link #onDestroy()}
52          */
cleanup()53         void cleanup();
54     }
55 
56     //Profile services will not be automatically restarted.
57     //They must be explicitly restarted by AdapterService
58     private static final int PROFILE_SERVICE_MODE = Service.START_NOT_STICKY;
59     private BluetoothAdapter mAdapter;
60     private IProfileServiceBinder mBinder;
61     private final String mName;
62     private AdapterService mAdapterService;
63     private BroadcastReceiver mUserSwitchedReceiver;
64     private boolean mProfileStarted = false;
65     private volatile boolean mTestModeEnabled = false;
66 
getName()67     public String getName() {
68         return getClass().getSimpleName();
69     }
70 
isAvailable()71     public boolean isAvailable() {
72         return mProfileStarted;
73     }
74 
isTestModeEnabled()75     protected boolean isTestModeEnabled() {
76         return mTestModeEnabled;
77     }
78 
79     /**
80      * Called in {@link #onCreate()} to init binder interface for this profile service
81      *
82      * @return initialized binder interface for this profile service
83      */
initBinder()84     protected abstract IProfileServiceBinder initBinder();
85 
86     /**
87      * Called in {@link #onCreate()} to init basic stuff in this service
88      */
89     // Suppressed since this is called from framework
90     @SuppressLint("AndroidFrameworkRequiresPermission")
create()91     protected void create() {}
92 
93     /**
94      * Called in {@link #onStartCommand(Intent, int, int)} when the service is started by intent
95      *
96      * @return True in successful condition, False otherwise
97      */
98     // Suppressed since this is called from framework
99     @SuppressLint("AndroidFrameworkRequiresPermission")
start()100     protected abstract boolean start();
101 
102     /**
103      * Called in {@link #onStartCommand(Intent, int, int)} when the service is stopped by intent
104      *
105      * @return True in successful condition, False otherwise
106      */
107     // Suppressed since this is called from framework
108     @SuppressLint("AndroidFrameworkRequiresPermission")
stop()109     protected abstract boolean stop();
110 
111     /**
112      * Called in {@link #onDestroy()} when this object is completely discarded
113      */
114     // Suppressed since this is called from framework
115     @SuppressLint("AndroidFrameworkRequiresPermission")
cleanup()116     protected void cleanup() {}
117 
118     /**
119      * @param userId is equivalent to the result of ActivityManager.getCurrentUser()
120      */
121     // Suppressed since this is called from framework
122     @SuppressLint("AndroidFrameworkRequiresPermission")
setCurrentUser(int userId)123     protected void setCurrentUser(int userId) {}
124 
125     /**
126      * @param userId is equivalent to the result of ActivityManager.getCurrentUser()
127      */
128     // Suppressed since this is called from framework
129     @SuppressLint("AndroidFrameworkRequiresPermission")
setUserUnlocked(int userId)130     protected void setUserUnlocked(int userId) {}
131 
132     /**
133      * @param testEnabled if the profile should enter or exit a testing mode
134      */
135     // Suppressed since this is called from framework
136     @SuppressLint("AndroidFrameworkRequiresPermission")
setTestModeEnabled(boolean testModeEnabled)137     protected void setTestModeEnabled(boolean testModeEnabled) {
138         mTestModeEnabled = testModeEnabled;
139     }
140 
ProfileService()141     protected ProfileService() {
142         mName = getName();
143     }
144 
145     @Override
146     // Suppressed since this is called from framework
147     @SuppressLint("AndroidFrameworkRequiresPermission")
onCreate()148     public void onCreate() {
149         if (DBG) {
150             Log.d(mName, "onCreate");
151         }
152         super.onCreate();
153         mAdapter = BluetoothAdapter.getDefaultAdapter();
154         mBinder = initBinder();
155         create();
156     }
157 
158     @Override
159     // Suppressed since this is called from framework
160     @SuppressLint("AndroidFrameworkRequiresPermission")
onStartCommand(Intent intent, int flags, int startId)161     public int onStartCommand(Intent intent, int flags, int startId) {
162         if (DBG) {
163             Log.d(mName, "onStartCommand()");
164         }
165 
166         if (checkCallingOrSelfPermission(BLUETOOTH_CONNECT)
167                 != PackageManager.PERMISSION_GRANTED) {
168             Log.e(mName, "Permission denied!");
169             return PROFILE_SERVICE_MODE;
170         }
171 
172         if (intent == null) {
173             Log.d(mName, "onStartCommand ignoring null intent.");
174             return PROFILE_SERVICE_MODE;
175         }
176 
177         String action = intent.getStringExtra(AdapterService.EXTRA_ACTION);
178         if (AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
179             int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
180             if (state == BluetoothAdapter.STATE_OFF) {
181                 doStop();
182             } else if (state == BluetoothAdapter.STATE_ON) {
183                 doStart();
184             }
185         }
186         return PROFILE_SERVICE_MODE;
187     }
188 
189     @Override
190     // Suppressed since this is called from framework
191     @SuppressLint("AndroidFrameworkRequiresPermission")
onBind(Intent intent)192     public IBinder onBind(Intent intent) {
193         if (DBG) {
194             Log.d(mName, "onBind");
195         }
196         if (mAdapter != null && mBinder == null) {
197             // initBinder returned null, you can't bind
198             throw new UnsupportedOperationException("Cannot bind to " + mName);
199         }
200         return mBinder;
201     }
202 
203     @Override
204     // Suppressed since this is called from framework
205     @SuppressLint("AndroidFrameworkRequiresPermission")
onUnbind(Intent intent)206     public boolean onUnbind(Intent intent) {
207         if (DBG) {
208             Log.d(mName, "onUnbind");
209         }
210         return super.onUnbind(intent);
211     }
212 
213     /**
214      * Support dumping profile-specific information for dumpsys
215      *
216      * @param sb StringBuilder from the profile.
217      */
218     // Suppressed since this is called from framework
219     @SuppressLint("AndroidFrameworkRequiresPermission")
dump(StringBuilder sb)220     public void dump(StringBuilder sb) {
221         sb.append("\nProfile: ");
222         sb.append(mName);
223         sb.append("\n");
224     }
225 
226     /**
227      * Support dumping scan events from GattService
228      *
229      * @param builder metrics proto builder
230      */
231     // Suppressed since this is called from framework
232     @SuppressLint("AndroidFrameworkRequiresPermission")
dumpProto(BluetoothMetricsProto.BluetoothLog.Builder builder)233     public void dumpProto(BluetoothMetricsProto.BluetoothLog.Builder builder) {
234         // Do nothing
235     }
236 
237     /**
238      * Append an indented String for adding dumpsys support to subclasses.
239      *
240      * @param sb StringBuilder from the profile.
241      * @param s String to indent and append.
242      */
println(StringBuilder sb, String s)243     public static void println(StringBuilder sb, String s) {
244         sb.append("  ");
245         sb.append(s);
246         sb.append("\n");
247     }
248 
249     @Override
250     // Suppressed since this is called from framework
251     @SuppressLint("AndroidFrameworkRequiresPermission")
onDestroy()252     public void onDestroy() {
253         cleanup();
254         if (mBinder != null) {
255             mBinder.cleanup();
256             mBinder = null;
257         }
258         mAdapter = null;
259         super.onDestroy();
260     }
261 
doStart()262     private void doStart() {
263         if (mAdapter == null) {
264             Log.w(mName, "Can't start profile service: device does not have BT");
265             return;
266         }
267 
268         mAdapterService = AdapterService.getAdapterService();
269         if (mAdapterService == null) {
270             Log.w(mName, "Could not add this profile because AdapterService is null.");
271             return;
272         }
273         if (!mAdapterService.isStartedProfile(mName)) {
274             Log.w(mName, "Unexpectedly do Start, don't start");
275             return;
276         }
277         mAdapterService.addProfile(this);
278 
279         IntentFilter filter = new IntentFilter();
280         filter.addAction(Intent.ACTION_USER_SWITCHED);
281         filter.addAction(Intent.ACTION_USER_UNLOCKED);
282         mUserSwitchedReceiver = new BroadcastReceiver() {
283             @Override
284             public void onReceive(Context context, Intent intent) {
285                 final String action = intent.getAction();
286                 final int userId =
287                         intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
288                 if (userId == UserHandle.USER_NULL) {
289                     Log.e(mName, "userChangeReceiver received an invalid EXTRA_USER_HANDLE");
290                     return;
291                 }
292                 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
293                     Log.d(mName, "User switched to userId " + userId);
294                     setCurrentUser(userId);
295                 } else if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
296                     Log.d(mName, "Unlocked userId " + userId);
297                     setUserUnlocked(userId);
298                 }
299             }
300         };
301 
302         getApplicationContext().registerReceiver(mUserSwitchedReceiver, filter);
303         int currentUserId = ActivityManager.getCurrentUser();
304         setCurrentUser(currentUserId);
305         UserManager userManager = UserManager.get(getApplicationContext());
306         if (userManager.isUserUnlocked(currentUserId)) {
307             setUserUnlocked(currentUserId);
308         }
309         mProfileStarted = start();
310         if (!mProfileStarted) {
311             Log.e(mName, "Error starting profile. start() returned false.");
312             return;
313         }
314         mAdapterService.onProfileServiceStateChanged(this, BluetoothAdapter.STATE_ON);
315     }
316 
doStop()317     private void doStop() {
318         if (mAdapterService == null || mAdapterService.isStartedProfile(mName)) {
319             Log.w(mName, "Unexpectedly do Stop, don't stop.");
320             return;
321         }
322         if (!mProfileStarted) {
323             Log.w(mName, "doStop() called, but the profile is not running.");
324         }
325         mProfileStarted = false;
326         if (mAdapterService != null) {
327             mAdapterService.onProfileServiceStateChanged(this, BluetoothAdapter.STATE_OFF);
328         }
329         if (!stop()) {
330             Log.e(mName, "Unable to stop profile");
331         }
332         if (mAdapterService != null) {
333             mAdapterService.removeProfile(this);
334         }
335         if (mUserSwitchedReceiver != null) {
336             getApplicationContext().unregisterReceiver(mUserSwitchedReceiver);
337             mUserSwitchedReceiver = null;
338         }
339         stopSelf();
340     }
341 
342     /**
343      * Returns a {@link BluetoothDevice} instance for the given address, but
344      * with any {@link AttributionSource} details removed, making it "anonymous"
345      * and not suitable for local use within this process.
346      * <p>
347      * The returned object is intended to be returned to a remote caller for
348      * actual use, where {@link Attributable#setAttributionSource} will populate
349      * it accurately.
350      */
getAnonymousDevice(String address)351     protected BluetoothDevice getAnonymousDevice(String address) {
352         return Attributable.setAttributionSource(
353                 BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address), null);
354     }
355 
356     /**
357      * Returns a {@link BluetoothDevice} instance for the given address, but
358      * with any {@link AttributionSource} details removed, making it "anonymous"
359      * and not suitable for local use within this process.
360      * <p>
361      * The returned object is intended to be returned to a remote caller for
362      * actual use, where {@link Attributable#setAttributionSource} will populate
363      * it accurately.
364      */
getAnonymousDevice(byte[] address)365     protected BluetoothDevice getAnonymousDevice(byte[] address) {
366         return Attributable.setAttributionSource(
367                 BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address), null);
368     }
369 }
370