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