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