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