/* * Copyright (C) 2012 Google Inc. */ /** * @hide */ package com.android.bluetooth.btservice; import android.app.Application; import android.app.Service; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothCallback; import android.bluetooth.IBluetoothManager; import android.bluetooth.IBluetoothManagerCallback; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.ParcelUuid; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.provider.Settings; import android.util.Log; import android.util.Pair; import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.hid.HidService; import com.android.bluetooth.hfp.HeadsetService; import com.android.bluetooth.hdp.HealthService; import com.android.bluetooth.pan.PanService; import com.android.bluetooth.R; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties; import java.io.FileDescriptor; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Set; import java.util.Map; import java.util.Iterator; import java.util.Map.Entry; import android.content.pm.PackageManager; import android.os.ServiceManager; public class AdapterService extends Service { private static final String TAG = "BluetoothAdapterService"; private static final boolean DBG = true; private static final boolean TRACE_REF = true; //For Debugging only private static int sRefCount=0; public static final String ACTION_LOAD_ADAPTER_PROPERTIES="com.android.bluetooth.btservice.action.LOAD_ADAPTER_PROPERTIES"; public static final String ACTION_SERVICE_STATE_CHANGED="com.android.bluetooth.btservice.action.STATE_CHANGED"; public static final String EXTRA_ACTION="action"; static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; private static final int ADAPTER_SERVICE_TYPE=Service.START_STICKY; static { classInitNative(); } private static AdapterService sAdapterService; public static synchronized AdapterService getAdapterService(){ if (sAdapterService != null && !sAdapterService.mCleaningUp) { if (DBG) Log.d(TAG, "getAdapterService(): returning " + sAdapterService); return sAdapterService; } if (DBG) { if (sAdapterService == null) { Log.d(TAG, "getAdapterService(): service not available"); } else if (sAdapterService.mCleaningUp) { Log.d(TAG,"getAdapterService(): service is cleaning up"); } } return null; } private static synchronized void setAdapterService(AdapterService instance) { if (instance != null && !instance.mCleaningUp) { if (DBG) Log.d(TAG, "setAdapterService(): set to: " + sAdapterService); sAdapterService = instance; } else { if (DBG) { if (sAdapterService == null) { Log.d(TAG, "setAdapterService(): service not available"); } else if (sAdapterService.mCleaningUp) { Log.d(TAG,"setAdapterService(): service is cleaning up"); } } } } private static synchronized void clearAdapterService() { sAdapterService = null; } private AdapterProperties mAdapterProperties; private AdapterState mAdapterStateMachine; private BondStateMachine mBondStateMachine; private JniCallbacks mJniCallbacks; private RemoteDevices mRemoteDevices; private boolean mProfilesStarted; private boolean mNativeAvailable; private boolean mCleaningUp; private HashMap mProfileServicesState = new HashMap(); private RemoteCallbackList mCallbacks;//Only BluetoothManagerService should be registered private int mCurrentRequestId; public AdapterService() { super(); if (TRACE_REF) { synchronized (AdapterService.class) { sRefCount++; Log.d(TAG, "REFCOUNT: CREATED. INSTANCE_COUNT" + sRefCount); } } } public void onProfileConnectionStateChanged(BluetoothDevice device, int profileId, int newState, int prevState) { Message m = mHandler.obtainMessage(MESSAGE_PROFILE_CONNECTION_STATE_CHANGED); m.obj = device; m.arg1 = profileId; m.arg2 = newState; Bundle b = new Bundle(1); b.putInt("prevState", prevState); m.setData(b); mHandler.sendMessage(m); } private void processProfileStateChanged(BluetoothDevice device, int profileId, int newState, int prevState) { IBluetooth.Stub binder = mBinder; if (binder != null) { try { binder.sendConnectionStateChange(device, profileId, newState,prevState); } catch (RemoteException re) { Log.e(TAG, "",re); } } } public void onProfileServiceStateChanged(String serviceName, int state) { Message m = mHandler.obtainMessage(MESSAGE_PROFILE_SERVICE_STATE_CHANGED); m.obj=serviceName; m.arg1 = state; mHandler.sendMessage(m); } private void processProfileServiceStateChanged(String serviceName, int state) { boolean doUpdate=false; boolean isTurningOn; boolean isTurningOff; synchronized (mProfileServicesState) { Integer prevState = mProfileServicesState.get(serviceName); if (prevState != null && prevState != state) { mProfileServicesState.put(serviceName,state); doUpdate=true; } } if (DBG) Log.d(TAG,"onProfileServiceStateChange: serviceName=" + serviceName + ", state = " + state +", doUpdate = " + doUpdate); if (!doUpdate) { return; } synchronized (mAdapterStateMachine) { isTurningOff = mAdapterStateMachine.isTurningOff(); isTurningOn = mAdapterStateMachine.isTurningOn(); } if (isTurningOff) { //Process stop or disable pending //Check if all services are stopped if so, do cleanup //if (DBG) Log.d(TAG,"Checking if all profiles are stopped..."); synchronized (mProfileServicesState) { Iterator> i = mProfileServicesState.entrySet().iterator(); while (i.hasNext()) { Map.Entry entry = i.next(); if (BluetoothAdapter.STATE_OFF != entry.getValue()) { Log.d(TAG, "Profile still running: " + entry.getKey()); return; } } } if (DBG) Log.d(TAG, "All profile services stopped..."); //Send message to state machine mProfilesStarted=false; mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.STOPPED)); } else if (isTurningOn) { //Process start pending //Check if all services are started if so, update state //if (DBG) Log.d(TAG,"Checking if all profiles are running..."); synchronized (mProfileServicesState) { Iterator> i = mProfileServicesState.entrySet().iterator(); while (i.hasNext()) { Map.Entry entry = i.next(); if (BluetoothAdapter.STATE_ON != entry.getValue()) { Log.d(TAG, "Profile still not running:" + entry.getKey()); return; } } } if (DBG) Log.d(TAG, "All profile services started."); mProfilesStarted=true; //Send message to state machine mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.STARTED)); } } @Override public void onCreate() { super.onCreate(); if (DBG) debugLog("onCreate"); mBinder = new AdapterServiceBinder(this); mAdapterProperties = new AdapterProperties(this); mAdapterStateMachine = new AdapterState(this, mAdapterProperties); mJniCallbacks = new JniCallbacks(mAdapterStateMachine, mAdapterProperties); initNative(); mNativeAvailable=true; mAdapterStateMachine.start(); mCallbacks = new RemoteCallbackList(); //Load the name and address getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_BDADDR); getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_BDNAME); } @Override public IBinder onBind(Intent intent) { if (DBG) debugLog("onBind"); return mBinder; } public boolean onUnbind(Intent intent) { if (DBG) debugLog("onUnbind"); return super.onUnbind(intent); } public void onDestroy() { debugLog("****onDestroy()********"); mHandler.removeMessages(MESSAGE_SHUTDOWN); cleanup(); } public int onStartCommand(Intent intent ,int flags, int startId) { mCurrentRequestId = startId; if (mCleaningUp) { Log.e(TAG,"*************Received new request while service is cleaning up****************************"); } if (DBG) debugLog("onStartCommand: flags = " + flags + ", startId = " + startId); if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM)!=PackageManager.PERMISSION_GRANTED) { Log.e(TAG, "Permission denied!"); return ADAPTER_SERVICE_TYPE; } //Check if we are restarting if (intent == null) { debugLog("Restarting AdapterService"); return ADAPTER_SERVICE_TYPE; } //Get action and check if valid. If invalid, ignore and return String action = intent.getStringExtra(EXTRA_ACTION); debugLog("onStartCommand(): action = " + action); if (!ACTION_SERVICE_STATE_CHANGED.equals(action)) { Log.w(TAG,"Unknown action: " + action); return ADAPTER_SERVICE_TYPE; } //Check state of request. If invalid, ignore and return int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.ERROR); debugLog("onStartCommand(): state = " + Utils.debugGetAdapterStateString(state)); //Cancel any pending shutdown requests synchronized (mHandler) { mHandler.removeMessages(MESSAGE_SHUTDOWN); } if (state == BluetoothAdapter.STATE_OFF) { Message m = mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_OFF); m.arg1= startId; mAdapterStateMachine.sendMessage(m); } else if (state == BluetoothAdapter.STATE_ON) { Message m = mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_ON); m.arg1= startId; mAdapterStateMachine.sendMessage(m); } else { Log.w(TAG,"Invalid state: " + action); return ADAPTER_SERVICE_TYPE; } return ADAPTER_SERVICE_TYPE; } void processStart() { if (DBG) debugLog("processStart()"); Class[] SUPPORTED_PROFILE_SERVICES = Config.getSupportedProfiles(); //Initialize data objects for (int i=0; i < SUPPORTED_PROFILE_SERVICES.length;i++) { mProfileServicesState.put(SUPPORTED_PROFILE_SERVICES[i].getName(),BluetoothAdapter.STATE_OFF); } mRemoteDevices = new RemoteDevices(this); mBondStateMachine = new BondStateMachine(this, mAdapterProperties, mRemoteDevices); mAdapterProperties.init(mRemoteDevices); mJniCallbacks.init(mBondStateMachine,mRemoteDevices); //Start Bond State Machine if (DBG) {debugLog("processStart(): Starting Bond State Machine");} mBondStateMachine.start(); //FIXME: Set static instance here??? setAdapterService(this); //Start profile services if (!mProfilesStarted && SUPPORTED_PROFILE_SERVICES.length >0) { //Startup all profile services setProfileServiceState(SUPPORTED_PROFILE_SERVICES,BluetoothAdapter.STATE_ON); }else { if (DBG) {debugLog("processStart(): Profile Services alreay started");} mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.STARTED)); } } void startBluetoothDisable() { mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.BEGIN_DISABLE)); } boolean stopProfileServices() { Class[] SUPPORTED_PROFILE_SERVICES = Config.getSupportedProfiles(); if (mProfilesStarted && SUPPORTED_PROFILE_SERVICES.length>0) { setProfileServiceState(SUPPORTED_PROFILE_SERVICES,BluetoothAdapter.STATE_OFF); return true; } else { if (DBG) {debugLog("stopProfileServices(): No profiles services to stop or already stopped.");} return false; } } void startShutdown(int requestId) { debugLog("startShutdown(): requestId = " + requestId + ", currentRequestId=" + mCurrentRequestId); if (requestId <0) { Log.w(TAG, "Ignoring shutdown request. Invalid requestId"); return; } Message m = mHandler.obtainMessage(MESSAGE_SHUTDOWN); synchronized(mHandler) { mHandler.sendMessageDelayed(m, SHUTDOWN_TIMEOUT); } stopSelfResult(requestId); } void updateAdapterState(int prevState, int newState){ if (mCallbacks !=null) { int n=mCallbacks.beginBroadcast(); Log.d(TAG,"Broadcasting updateAdapterState() to " + n + " receivers."); for (int i=0; i