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