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    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 {
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
60    protected String getName() {
61        return getClass().getSimpleName();
62    }
63
64    protected boolean isAvailable() {
65        return !mStartError && !mCleaningUp;
66    }
67
68    protected abstract IProfileServiceBinder initBinder();
69    protected abstract boolean start();
70    protected abstract boolean stop();
71    protected boolean cleanup() {
72        return true;
73    }
74
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
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
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
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
152    public IBinder onBind(Intent intent) {
153        if (DBG) log("onBind");
154        return mBinder;
155    }
156
157    public boolean onUnbind(Intent intent) {
158        if (DBG) log("onUnbind");
159        return super.onUnbind(intent);
160    }
161
162    // for dumpsys support
163    public void dump(StringBuilder sb) {
164        sb.append("Profile: " + mName + "\n");
165    }
166
167    // with indenting for subclasses
168    public static void println(StringBuilder sb, String s) {
169        sb.append("  ");
170        sb.append(s);
171        sb.append("\n");
172    }
173
174    @Override
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
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
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
219    protected void notifyProfileServiceStateChanged(int state) {
220        //Notify adapter service
221        if (mAdapterService != null) {
222            mAdapterService.onProfileServiceStateChanged(getClass().getName(), state);
223        }
224    }
225
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
233    protected BluetoothDevice getDevice(byte[] address) {
234        return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
235    }
236
237    protected void log(String msg) {
238        Log.d(mName, msg);
239    }
240}
241