ProfileService.java revision 970d01527efa5606701c5b1f4942a56c47814eab
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 android.app.Service;
20import android.bluetooth.BluetoothAdapter;
21import android.bluetooth.BluetoothDevice;
22import android.content.Intent;
23import android.content.pm.PackageManager;
24import android.os.IBinder;
25import android.util.Log;
26
27import com.android.bluetooth.Utils;
28
29import java.util.HashMap;
30
31/**
32 * Base class for a background service that runs a Bluetooth profile
33 */
34public abstract class ProfileService extends Service {
35    private static final boolean DBG = false;
36    private static final String TAG = "BluetoothProfileService";
37
38    //For Debugging only
39    private static HashMap<String, Integer> sReferenceCount = new HashMap<String, Integer>();
40
41    public static final String BLUETOOTH_ADMIN_PERM = 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 interface IProfileServiceBinder extends IBinder {
47        /**
48         * Called in {@link #onDestroy()}
49         */
50        void cleanup();
51    }
52
53    //Profile services will not be automatically restarted.
54    //They must be explicitly restarted by AdapterService
55    private static final int PROFILE_SERVICE_MODE = Service.START_NOT_STICKY;
56    protected String mName;
57    protected BluetoothAdapter mAdapter;
58    protected IProfileServiceBinder mBinder;
59    protected boolean mStartError = false;
60    private boolean mCleaningUp = false;
61
62    protected String getName() {
63        return getClass().getSimpleName();
64    }
65
66    protected boolean isAvailable() {
67        return !mStartError && !mCleaningUp;
68    }
69
70    /**
71     * Called in {@link #onCreate()} to init binder interface for this profile service
72     *
73     * @return initialized binder interface for this profile service
74     */
75    protected abstract IProfileServiceBinder initBinder();
76
77    /**
78     * Called in {@link #onCreate()} to init basic stuff in this service
79     */
80    protected void create() {}
81
82    /**
83     * Called in {@link #onStartCommand(Intent, int, int)} when the service is started by intent
84     *
85     * @return True in successful condition, False otherwise
86     */
87    protected abstract boolean start();
88
89    /**
90     * Called in {@link #onStartCommand(Intent, int, int)} when the service is stopped by intent
91     *
92     * @return True in successful condition, False otherwise
93     */
94    protected abstract boolean stop();
95
96    /**
97     * Called in {@link #onDestroy()} when this object is completely discarded
98     */
99    protected void cleanup() {}
100
101    protected ProfileService() {
102        mName = getName();
103        if (DBG) {
104            synchronized (sReferenceCount) {
105                Integer refCount = sReferenceCount.get(mName);
106                if (refCount == null) {
107                    refCount = 1;
108                } else {
109                    refCount = refCount + 1;
110                }
111                sReferenceCount.put(mName, refCount);
112                if (DBG) {
113                    log("REFCOUNT: CREATED. INSTANCE_COUNT=" + refCount);
114                }
115            }
116        }
117    }
118
119    @Override
120    protected void finalize() {
121        if (DBG) {
122            synchronized (sReferenceCount) {
123                Integer refCount = sReferenceCount.get(mName);
124                if (refCount != null) {
125                    refCount = refCount - 1;
126                } else {
127                    refCount = 0;
128                }
129                sReferenceCount.put(mName, refCount);
130                log("REFCOUNT: FINALIZED. INSTANCE_COUNT=" + refCount);
131            }
132        }
133    }
134
135    @Override
136    public void onCreate() {
137        if (DBG) {
138            log("onCreate");
139        }
140        super.onCreate();
141        mAdapter = BluetoothAdapter.getDefaultAdapter();
142        mBinder = initBinder();
143        create();
144    }
145
146    @Override
147    public int onStartCommand(Intent intent, int flags, int startId) {
148        if (DBG) {
149            log("onStartCommand()");
150        }
151        AdapterService adapterService = AdapterService.getAdapterService();
152        if (adapterService != null) {
153            adapterService.addProfile(this);
154        } else {
155            Log.w(TAG, "Could not add this profile because AdapterService is null.");
156        }
157
158        if (mStartError || mAdapter == null) {
159            Log.w(mName, "Stopping profile service: device does not have BT");
160            doStop(intent);
161            return PROFILE_SERVICE_MODE;
162        }
163
164        if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM)
165                != PackageManager.PERMISSION_GRANTED) {
166            Log.e(mName, "Permission denied!");
167            return PROFILE_SERVICE_MODE;
168        }
169
170        if (intent == null) {
171            Log.d(mName, "Restarting profile service...");
172            return PROFILE_SERVICE_MODE;
173        } else {
174            String action = intent.getStringExtra(AdapterService.EXTRA_ACTION);
175            if (AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
176                int state =
177                        intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
178                if (state == BluetoothAdapter.STATE_OFF) {
179                    Log.d(mName, "Received stop request...Stopping profile...");
180                    doStop(intent);
181                } else if (state == BluetoothAdapter.STATE_ON) {
182                    Log.d(mName, "Received start request. Starting profile...");
183                    doStart(intent);
184                }
185            }
186        }
187        return PROFILE_SERVICE_MODE;
188    }
189
190    @Override
191    public IBinder onBind(Intent intent) {
192        if (DBG) {
193            log("onBind");
194        }
195        if (mAdapter != null && mBinder == null) {
196            // initBinder returned null, you can't bind
197            throw new UnsupportedOperationException("Cannot bind to " + mName);
198        }
199        return mBinder;
200    }
201
202    @Override
203    public boolean onUnbind(Intent intent) {
204        if (DBG) {
205            log("onUnbind");
206        }
207        return super.onUnbind(intent);
208    }
209
210    // for dumpsys support
211    public void dump(StringBuilder sb) {
212        sb.append("\nProfile: " + mName + "\n");
213    }
214
215    public void dumpProto(BluetoothProto.BluetoothLog proto) {
216        // Do nothing
217    }
218
219    // with indenting for subclasses
220    public static void println(StringBuilder sb, String s) {
221        sb.append("  ");
222        sb.append(s);
223        sb.append("\n");
224    }
225
226    @Override
227    public void onDestroy() {
228        if (DBG) {
229            log("Destroying service.");
230        }
231        AdapterService adapterService = AdapterService.getAdapterService();
232        if (adapterService != null) {
233            adapterService.removeProfile(this);
234        }
235
236        if (mCleaningUp) {
237            if (DBG) {
238                log("Cleanup already started... Skipping cleanup()...");
239            }
240        } else {
241            if (DBG) {
242                log("cleanup()");
243            }
244            mCleaningUp = true;
245            cleanup();
246            if (mBinder != null) {
247                mBinder.cleanup();
248                mBinder = null;
249            }
250        }
251        super.onDestroy();
252        mAdapter = null;
253    }
254
255    private void doStart(Intent intent) {
256        //Start service
257        if (mAdapter == null) {
258            Log.e(mName, "Error starting profile. BluetoothAdapter is null");
259        } else {
260            if (DBG) {
261                log("start()");
262            }
263            mStartError = !start();
264            if (!mStartError) {
265                notifyProfileServiceStateChanged(BluetoothAdapter.STATE_ON);
266            } else {
267                Log.e(mName, "Error starting profile. BluetoothAdapter is null");
268            }
269        }
270    }
271
272    private void doStop(Intent intent) {
273        if (stop()) {
274            if (DBG) {
275                log("stop()");
276            }
277            notifyProfileServiceStateChanged(BluetoothAdapter.STATE_OFF);
278            stopSelf();
279        } else {
280            Log.e(mName, "Unable to stop profile");
281        }
282    }
283
284    protected void notifyProfileServiceStateChanged(int state) {
285        //Notify adapter service
286        AdapterService adapterService = AdapterService.getAdapterService();
287        if (adapterService != null) {
288            adapterService.onProfileServiceStateChanged(getClass().getName(), state);
289        }
290    }
291
292    protected BluetoothDevice getDevice(byte[] address) {
293        if (mAdapter != null) {
294            return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
295        }
296        return null;
297    }
298
299    protected void log(String msg) {
300        Log.d(mName, msg);
301    }
302}
303