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 android.app.ActivityManager; 20 import android.app.Service; 21 import android.bluetooth.BluetoothAdapter; 22 import android.bluetooth.BluetoothDevice; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.pm.PackageManager; 28 import android.os.IBinder; 29 import android.os.UserHandle; 30 import android.os.UserManager; 31 import android.util.Log; 32 33 import com.android.bluetooth.BluetoothMetricsProto; 34 import com.android.bluetooth.Utils; 35 36 /** 37 * Base class for a background service that runs a Bluetooth profile 38 */ 39 public abstract class ProfileService extends Service { 40 private static final boolean DBG = false; 41 42 public static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; 43 public static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; 44 public static final String BLUETOOTH_PRIVILEGED = 45 android.Manifest.permission.BLUETOOTH_PRIVILEGED; 46 47 public interface IProfileServiceBinder extends IBinder { 48 /** 49 * Called in {@link #onDestroy()} 50 */ cleanup()51 void cleanup(); 52 } 53 54 //Profile services will not be automatically restarted. 55 //They must be explicitly restarted by AdapterService 56 private static final int PROFILE_SERVICE_MODE = Service.START_NOT_STICKY; 57 private BluetoothAdapter mAdapter; 58 private IProfileServiceBinder mBinder; 59 private final String mName; 60 private AdapterService mAdapterService; 61 private BroadcastReceiver mUserSwitchedReceiver; 62 private boolean mProfileStarted = false; 63 getName()64 public String getName() { 65 return getClass().getSimpleName(); 66 } 67 isAvailable()68 protected boolean isAvailable() { 69 return mProfileStarted; 70 } 71 72 /** 73 * Called in {@link #onCreate()} to init binder interface for this profile service 74 * 75 * @return initialized binder interface for this profile service 76 */ initBinder()77 protected abstract IProfileServiceBinder initBinder(); 78 79 /** 80 * Called in {@link #onCreate()} to init basic stuff in this service 81 */ create()82 protected void create() {} 83 84 /** 85 * Called in {@link #onStartCommand(Intent, int, int)} when the service is started by intent 86 * 87 * @return True in successful condition, False otherwise 88 */ start()89 protected abstract boolean start(); 90 91 /** 92 * Called in {@link #onStartCommand(Intent, int, int)} when the service is stopped by intent 93 * 94 * @return True in successful condition, False otherwise 95 */ stop()96 protected abstract boolean stop(); 97 98 /** 99 * Called in {@link #onDestroy()} when this object is completely discarded 100 */ cleanup()101 protected void cleanup() {} 102 103 /** 104 * @param userId is equivalent to the result of ActivityManager.getCurrentUser() 105 */ setCurrentUser(int userId)106 protected void setCurrentUser(int userId) {} 107 108 /** 109 * @param userId is equivalent to the result of ActivityManager.getCurrentUser() 110 */ setUserUnlocked(int userId)111 protected void setUserUnlocked(int userId) {} 112 ProfileService()113 protected ProfileService() { 114 mName = getName(); 115 } 116 117 @Override onCreate()118 public void onCreate() { 119 if (DBG) { 120 Log.d(mName, "onCreate"); 121 } 122 super.onCreate(); 123 mAdapter = BluetoothAdapter.getDefaultAdapter(); 124 mBinder = initBinder(); 125 create(); 126 } 127 128 @Override onStartCommand(Intent intent, int flags, int startId)129 public int onStartCommand(Intent intent, int flags, int startId) { 130 if (DBG) { 131 Log.d(mName, "onStartCommand()"); 132 } 133 134 if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM) 135 != PackageManager.PERMISSION_GRANTED) { 136 Log.e(mName, "Permission denied!"); 137 return PROFILE_SERVICE_MODE; 138 } 139 140 if (intent == null) { 141 Log.d(mName, "onStartCommand ignoring null intent."); 142 return PROFILE_SERVICE_MODE; 143 } 144 145 String action = intent.getStringExtra(AdapterService.EXTRA_ACTION); 146 if (AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) { 147 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); 148 if (state == BluetoothAdapter.STATE_OFF) { 149 doStop(); 150 } else if (state == BluetoothAdapter.STATE_ON) { 151 doStart(); 152 } 153 } 154 return PROFILE_SERVICE_MODE; 155 } 156 157 @Override onBind(Intent intent)158 public IBinder onBind(Intent intent) { 159 if (DBG) { 160 Log.d(mName, "onBind"); 161 } 162 if (mAdapter != null && mBinder == null) { 163 // initBinder returned null, you can't bind 164 throw new UnsupportedOperationException("Cannot bind to " + mName); 165 } 166 return mBinder; 167 } 168 169 @Override onUnbind(Intent intent)170 public boolean onUnbind(Intent intent) { 171 if (DBG) { 172 Log.d(mName, "onUnbind"); 173 } 174 return super.onUnbind(intent); 175 } 176 177 /** 178 * Support dumping profile-specific information for dumpsys 179 * 180 * @param sb StringBuilder from the profile. 181 */ dump(StringBuilder sb)182 public void dump(StringBuilder sb) { 183 sb.append("\nProfile: "); 184 sb.append(mName); 185 sb.append("\n"); 186 } 187 188 /** 189 * Support dumping scan events from GattService 190 * 191 * @param builder metrics proto builder 192 */ dumpProto(BluetoothMetricsProto.BluetoothLog.Builder builder)193 public void dumpProto(BluetoothMetricsProto.BluetoothLog.Builder builder) { 194 // Do nothing 195 } 196 197 /** 198 * Append an indented String for adding dumpsys support to subclasses. 199 * 200 * @param sb StringBuilder from the profile. 201 * @param s String to indent and append. 202 */ println(StringBuilder sb, String s)203 public static void println(StringBuilder sb, String s) { 204 sb.append(" "); 205 sb.append(s); 206 sb.append("\n"); 207 } 208 209 @Override onDestroy()210 public void onDestroy() { 211 cleanup(); 212 if (mBinder != null) { 213 mBinder.cleanup(); 214 mBinder = null; 215 } 216 mAdapter = null; 217 super.onDestroy(); 218 } 219 doStart()220 private void doStart() { 221 if (mAdapter == null) { 222 Log.w(mName, "Can't start profile service: device does not have BT"); 223 return; 224 } 225 226 mAdapterService = AdapterService.getAdapterService(); 227 if (mAdapterService == null) { 228 Log.w(mName, "Could not add this profile because AdapterService is null."); 229 return; 230 } 231 mAdapterService.addProfile(this); 232 233 IntentFilter filter = new IntentFilter(); 234 filter.addAction(Intent.ACTION_USER_SWITCHED); 235 filter.addAction(Intent.ACTION_USER_UNLOCKED); 236 mUserSwitchedReceiver = new BroadcastReceiver() { 237 @Override 238 public void onReceive(Context context, Intent intent) { 239 final String action = intent.getAction(); 240 final int userId = 241 intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 242 if (userId == UserHandle.USER_NULL) { 243 Log.e(mName, "userChangeReceiver received an invalid EXTRA_USER_HANDLE"); 244 return; 245 } 246 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 247 Log.d(mName, "User switched to userId " + userId); 248 setCurrentUser(userId); 249 } else if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { 250 Log.d(mName, "Unlocked userId " + userId); 251 setUserUnlocked(userId); 252 } 253 } 254 }; 255 256 getApplicationContext().registerReceiver(mUserSwitchedReceiver, filter); 257 int currentUserId = ActivityManager.getCurrentUser(); 258 setCurrentUser(currentUserId); 259 UserManager userManager = UserManager.get(getApplicationContext()); 260 if (userManager.isUserUnlocked(currentUserId)) { 261 setUserUnlocked(currentUserId); 262 } 263 mProfileStarted = start(); 264 if (!mProfileStarted) { 265 Log.e(mName, "Error starting profile. start() returned false."); 266 return; 267 } 268 mAdapterService.onProfileServiceStateChanged(this, BluetoothAdapter.STATE_ON); 269 } 270 doStop()271 private void doStop() { 272 if (!mProfileStarted) { 273 Log.w(mName, "doStop() called, but the profile is not running."); 274 } 275 mProfileStarted = false; 276 if (mAdapterService != null) { 277 mAdapterService.onProfileServiceStateChanged(this, BluetoothAdapter.STATE_OFF); 278 } 279 if (!stop()) { 280 Log.e(mName, "Unable to stop profile"); 281 } 282 if (mAdapterService != null) { 283 mAdapterService.removeProfile(this); 284 } 285 if (mUserSwitchedReceiver != null) { 286 getApplicationContext().unregisterReceiver(mUserSwitchedReceiver); 287 mUserSwitchedReceiver = null; 288 } 289 stopSelf(); 290 } 291 getDevice(byte[] address)292 protected BluetoothDevice getDevice(byte[] address) { 293 if (mAdapter != null) { 294 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 295 } 296 return null; 297 } 298 } 299