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    protected String getName() {
59        return getClass().getSimpleName();
60    }
61
62    protected boolean isAvailable() {
63        return !mStartError && !mCleaningUp;
64    }
65
66    protected abstract IProfileServiceBinder initBinder();
67    protected abstract boolean start();
68    protected abstract boolean stop();
69    protected boolean cleanup() {
70        return true;
71    }
72
73    protected ProfileService() {
74        mName = getName();
75        if (DBG) {
76            synchronized (sReferenceCount) {
77                Integer refCount = sReferenceCount.get(mName);
78                if (refCount==null) {
79                    refCount = 1;
80                } else {
81                    refCount = refCount+1;
82                }
83                sReferenceCount.put(mName, refCount);
84                if (DBG) log("REFCOUNT: CREATED. INSTANCE_COUNT=" +refCount);
85            }
86        }
87    }
88
89    protected void finalize() {
90        if (DBG) {
91            synchronized (sReferenceCount) {
92                Integer refCount = sReferenceCount.get(mName);
93                if (refCount!=null) {
94                    refCount = refCount-1;
95                } else {
96                    refCount = 0;
97                }
98                sReferenceCount.put(mName, refCount);
99                log("REFCOUNT: FINALIZED. INSTANCE_COUNT=" +refCount);
100            }
101        }
102    }
103
104    @Override
105    public void onCreate() {
106        if (DBG) log("onCreate");
107        super.onCreate();
108        mAdapter = BluetoothAdapter.getDefaultAdapter();
109        mBinder = initBinder();
110    }
111
112    public int onStartCommand(Intent intent, int flags, int startId) {
113        if (DBG) log("onStartCommand()");
114        AdapterService adapterService = AdapterService.getAdapterService();
115        if (adapterService != null) {
116            adapterService.addProfile(this);
117        } else {
118            Log.w(TAG, "Could not add this profile because AdapterService is null.");
119        }
120
121        if (mStartError || mAdapter == null) {
122            Log.w(mName, "Stopping profile service: device does not have BT");
123            doStop(intent);
124            return PROFILE_SERVICE_MODE;
125        }
126
127        if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM)!=PackageManager.PERMISSION_GRANTED) {
128            Log.e(mName, "Permission denied!");
129            return PROFILE_SERVICE_MODE;
130        }
131
132        if (intent == null) {
133            Log.d(mName, "Restarting profile service...");
134            return PROFILE_SERVICE_MODE;
135        } else {
136            String action = intent.getStringExtra(AdapterService.EXTRA_ACTION);
137            if (AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
138                int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
139                if(state==BluetoothAdapter.STATE_OFF) {
140                    Log.d(mName, "Received stop request...Stopping profile...");
141                    doStop(intent);
142                } else if (state == BluetoothAdapter.STATE_ON) {
143                    Log.d(mName, "Received start request. Starting profile...");
144                    doStart(intent);
145                }
146            }
147        }
148        return PROFILE_SERVICE_MODE;
149    }
150
151    public IBinder onBind(Intent intent) {
152        if (DBG) log("onBind");
153        return mBinder;
154    }
155
156    public boolean onUnbind(Intent intent) {
157        if (DBG) log("onUnbind");
158        return super.onUnbind(intent);
159    }
160
161    // for dumpsys support
162    public void dump(StringBuilder sb) {
163        sb.append("\nProfile: " + mName + "\n");
164    }
165
166    public void dumpProto(BluetoothProto.BluetoothLog proto) {
167        // Do nothing
168    }
169
170    // with indenting for subclasses
171    public static void println(StringBuilder sb, String s) {
172        sb.append("  ");
173        sb.append(s);
174        sb.append("\n");
175    }
176
177    @Override
178    public void onDestroy() {
179        if (DBG) log("Destroying service.");
180        AdapterService adapterService = AdapterService.getAdapterService();
181        if (adapterService != null) adapterService.removeProfile(this);
182
183        if (mCleaningUp) {
184            if (DBG) log("Cleanup already started... Skipping cleanup()...");
185        } else {
186            if (DBG) log("cleanup()");
187            mCleaningUp = true;
188            cleanup();
189            if (mBinder != null) {
190                mBinder.cleanup();
191                mBinder= null;
192            }
193        }
194        super.onDestroy();
195        mAdapter = null;
196    }
197
198    private void doStart(Intent intent) {
199        //Start service
200        if (mAdapter == null) {
201            Log.e(mName, "Error starting profile. BluetoothAdapter is null");
202        } else {
203            if (DBG) log("start()");
204            mStartError = !start();
205            if (!mStartError) {
206                notifyProfileServiceStateChanged(BluetoothAdapter.STATE_ON);
207            } else {
208                Log.e(mName, "Error starting profile. BluetoothAdapter is null");
209            }
210        }
211    }
212
213    private void doStop(Intent intent) {
214        if (stop()) {
215            if (DBG) log("stop()");
216            notifyProfileServiceStateChanged(BluetoothAdapter.STATE_OFF);
217            stopSelf();
218        } else {
219            Log.e(mName, "Unable to stop profile");
220        }
221    }
222
223    protected void notifyProfileServiceStateChanged(int state) {
224        //Notify adapter service
225        AdapterService adapterService = AdapterService.getAdapterService();
226        if (adapterService != null) {
227            adapterService.onProfileServiceStateChanged(getClass().getName(), state);
228        }
229    }
230
231    public void notifyProfileConnectionStateChanged(BluetoothDevice device,
232            int profileId, int newState, int prevState) {
233        AdapterService adapterService = AdapterService.getAdapterService();
234        if (adapterService != null) {
235            adapterService.onProfileConnectionStateChanged(device, profileId, newState, prevState);
236        }
237    }
238
239    protected BluetoothDevice getDevice(byte[] address) {
240        if(mAdapter != null){
241            return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
242        }
243        return null;
244    }
245
246    protected void log(String msg) {
247        Log.d(mName, msg);
248    }
249}
250