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