Home | History | Annotate | Download | only in btservice
      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          */
     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 
     64     public String getName() {
     65         return getClass().getSimpleName();
     66     }
     67 
     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      */
     77     protected abstract IProfileServiceBinder initBinder();
     78 
     79     /**
     80      * Called in {@link #onCreate()} to init basic stuff in this service
     81      */
     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      */
     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      */
     96     protected abstract boolean stop();
     97 
     98     /**
     99      * Called in {@link #onDestroy()} when this object is completely discarded
    100      */
    101     protected void cleanup() {}
    102 
    103     /**
    104      * @param userId is equivalent to the result of ActivityManager.getCurrentUser()
    105      */
    106     protected void setCurrentUser(int userId) {}
    107 
    108     /**
    109      * @param userId is equivalent to the result of ActivityManager.getCurrentUser()
    110      */
    111     protected void setUserUnlocked(int userId) {}
    112 
    113     protected ProfileService() {
    114         mName = getName();
    115     }
    116 
    117     @Override
    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
    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
    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
    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      */
    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      */
    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      */
    203     public static void println(StringBuilder sb, String s) {
    204         sb.append("  ");
    205         sb.append(s);
    206         sb.append("\n");
    207     }
    208 
    209     @Override
    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 
    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 
    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 
    292     protected BluetoothDevice getDevice(byte[] address) {
    293         if (mAdapter != null) {
    294             return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
    295         }
    296         return null;
    297     }
    298 }
    299