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