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