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 java.util.HashMap;
20 
21 import com.android.bluetooth.Utils;
22 
23 import android.app.Service;
24 import android.bluetooth.BluetoothAdapter;
25 import android.bluetooth.BluetoothDevice;
26 import android.bluetooth.BluetoothProfile;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.pm.PackageManager;
30 import android.os.IBinder;
31 import android.util.Log;
32 
33 public abstract class ProfileService extends Service {
34     private static final boolean DBG = false;
35     private static final String TAG = "BluetoothProfileService";
36 
37     //For Debugging only
38     private static HashMap<String, Integer> sReferenceCount = new HashMap<String,Integer>();
39 
40     public static final String BLUETOOTH_ADMIN_PERM =
41             android.Manifest.permission.BLUETOOTH_ADMIN;
42     public static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
43     public static final String BLUETOOTH_PRIVILEGED =
44         android.Manifest.permission.BLUETOOTH_PRIVILEGED;
45 
46     public static interface IProfileServiceBinder extends IBinder {
cleanup()47         public boolean cleanup();
48     }
49     //Profile services will not be automatically restarted.
50     //They must be explicitly restarted by AdapterService
51     private static final int PROFILE_SERVICE_MODE=Service.START_NOT_STICKY;
52     protected String mName;
53     protected BluetoothAdapter mAdapter;
54     protected IProfileServiceBinder mBinder;
55     protected boolean mStartError=false;
56     private boolean mCleaningUp = false;
57 
58     private AdapterService mAdapterService;
59 
getName()60     protected String getName() {
61         return getClass().getSimpleName();
62     }
63 
isAvailable()64     protected boolean isAvailable() {
65         return !mStartError && !mCleaningUp;
66     }
67 
initBinder()68     protected abstract IProfileServiceBinder initBinder();
start()69     protected abstract boolean start();
stop()70     protected abstract boolean stop();
cleanup()71     protected boolean cleanup() {
72         return true;
73     }
74 
ProfileService()75     protected ProfileService() {
76         mName = getName();
77         if (DBG) {
78             synchronized (sReferenceCount) {
79                 Integer refCount = sReferenceCount.get(mName);
80                 if (refCount==null) {
81                     refCount = 1;
82                 } else {
83                     refCount = refCount+1;
84                 }
85                 sReferenceCount.put(mName, refCount);
86                 if (DBG) log("REFCOUNT: CREATED. INSTANCE_COUNT=" +refCount);
87             }
88         }
89     }
90 
finalize()91     protected void finalize() {
92         if (DBG) {
93             synchronized (sReferenceCount) {
94                 Integer refCount = sReferenceCount.get(mName);
95                 if (refCount!=null) {
96                     refCount = refCount-1;
97                 } else {
98                     refCount = 0;
99                 }
100                 sReferenceCount.put(mName, refCount);
101                 log("REFCOUNT: FINALIZED. INSTANCE_COUNT=" +refCount);
102             }
103         }
104     }
105 
106     @Override
onCreate()107     public void onCreate() {
108         if (DBG) log("onCreate");
109         super.onCreate();
110         mAdapter = BluetoothAdapter.getDefaultAdapter();
111         mBinder = initBinder();
112         mAdapterService = AdapterService.getAdapterService();
113         if (mAdapterService != null) {
114             mAdapterService.addProfile(this);
115         } else {
116             Log.w(TAG, "onCreate, null mAdapterService");
117         }
118     }
119 
onStartCommand(Intent intent, int flags, int startId)120     public int onStartCommand(Intent intent, int flags, int startId) {
121         if (DBG) log("onStartCommand()");
122         if (mStartError || mAdapter == null) {
123             Log.w(mName, "Stopping profile service: device does not have BT");
124             doStop(intent);
125             return PROFILE_SERVICE_MODE;
126         }
127 
128         if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM)!=PackageManager.PERMISSION_GRANTED) {
129             Log.e(mName, "Permission denied!");
130             return PROFILE_SERVICE_MODE;
131         }
132 
133         if (intent == null) {
134             Log.d(mName, "Restarting profile service...");
135             return PROFILE_SERVICE_MODE;
136         } else {
137             String action = intent.getStringExtra(AdapterService.EXTRA_ACTION);
138             if (AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
139                 int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
140                 if(state==BluetoothAdapter.STATE_OFF) {
141                     Log.d(mName, "Received stop request...Stopping profile...");
142                     doStop(intent);
143                 } else if (state == BluetoothAdapter.STATE_ON) {
144                     Log.d(mName, "Received start request. Starting profile...");
145                     doStart(intent);
146                 }
147             }
148         }
149         return PROFILE_SERVICE_MODE;
150     }
151 
onBind(Intent intent)152     public IBinder onBind(Intent intent) {
153         if (DBG) log("onBind");
154         return mBinder;
155     }
156 
onUnbind(Intent intent)157     public boolean onUnbind(Intent intent) {
158         if (DBG) log("onUnbind");
159         return super.onUnbind(intent);
160     }
161 
162     // for dumpsys support
dump(StringBuilder sb)163     public void dump(StringBuilder sb) {
164         sb.append("Profile: " + mName + "\n");
165     }
166 
167     // with indenting for subclasses
println(StringBuilder sb, String s)168     public static void println(StringBuilder sb, String s) {
169         sb.append("  ");
170         sb.append(s);
171         sb.append("\n");
172     }
173 
174     @Override
onDestroy()175     public void onDestroy() {
176         if (DBG) log("Destroying service.");
177         if (mAdapterService != null) mAdapterService.removeProfile(this);
178 
179         if (mCleaningUp) {
180             if (DBG) log("Cleanup already started... Skipping cleanup()...");
181         } else {
182             if (DBG) log("cleanup()");
183             mCleaningUp = true;
184             cleanup();
185             if (mBinder != null) {
186                 mBinder.cleanup();
187                 mBinder= null;
188             }
189         }
190         super.onDestroy();
191         mAdapter = null;
192     }
193 
doStart(Intent intent)194     private void doStart(Intent intent) {
195         //Start service
196         if (mAdapter == null) {
197             Log.e(mName, "Error starting profile. BluetoothAdapter is null");
198         } else {
199             if (DBG) log("start()");
200             mStartError = !start();
201             if (!mStartError) {
202                 notifyProfileServiceStateChanged(BluetoothAdapter.STATE_ON);
203             } else {
204                 Log.e(mName, "Error starting profile. BluetoothAdapter is null");
205             }
206         }
207     }
208 
doStop(Intent intent)209     private void doStop(Intent intent) {
210         if (stop()) {
211             if (DBG) log("stop()");
212             notifyProfileServiceStateChanged(BluetoothAdapter.STATE_OFF);
213             stopSelf();
214         } else {
215             Log.e(mName, "Unable to stop profile");
216         }
217     }
218 
notifyProfileServiceStateChanged(int state)219     protected void notifyProfileServiceStateChanged(int state) {
220         //Notify adapter service
221         if (mAdapterService != null) {
222             mAdapterService.onProfileServiceStateChanged(getClass().getName(), state);
223         }
224     }
225 
notifyProfileConnectionStateChanged(BluetoothDevice device, int profileId, int newState, int prevState)226     public void notifyProfileConnectionStateChanged(BluetoothDevice device,
227             int profileId, int newState, int prevState) {
228         if (mAdapterService != null) {
229             mAdapterService.onProfileConnectionStateChanged(device, profileId, newState, prevState);
230         }
231     }
232 
getDevice(byte[] address)233     protected BluetoothDevice getDevice(byte[] address) {
234         return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
235     }
236 
log(String msg)237     protected void log(String msg) {
238         Log.d(mName, msg);
239     }
240 }
241