ProfileService.java revision 42c9d3c51f91159172c4a601fc4b27628adf2a4a
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.ActivityManager;
20import android.app.Service;
21import android.bluetooth.BluetoothAdapter;
22import android.bluetooth.BluetoothDevice;
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.content.pm.PackageManager;
28import android.os.IBinder;
29import android.os.UserHandle;
30import android.os.UserManager;
31import android.util.Log;
32
33import com.android.bluetooth.Utils;
34
35/**
36 * Base class for a background service that runs a Bluetooth profile
37 */
38public abstract class ProfileService extends Service {
39    private static final boolean DBG = false;
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 final String mName;
57    protected BluetoothAdapter mAdapter;
58    protected AdapterService mAdapterService;
59    protected IProfileServiceBinder mBinder;
60    private BroadcastReceiver mUserSwitchedReceiver;
61    private boolean mProfileStarted = false;
62
63    public String getName() {
64        return getClass().getSimpleName();
65    }
66
67    protected boolean isAvailable() {
68        return mProfileStarted;
69    }
70
71    /**
72     * Called in {@link #onCreate()} to init binder interface for this profile service
73     *
74     * @return initialized binder interface for this profile service
75     */
76    protected abstract IProfileServiceBinder initBinder();
77
78    /**
79     * Called in {@link #onCreate()} to init basic stuff in this service
80     */
81    protected void create() {}
82
83    /**
84     * Called in {@link #onStartCommand(Intent, int, int)} when the service is started by intent
85     *
86     * @return True in successful condition, False otherwise
87     */
88    protected abstract boolean start();
89
90    /**
91     * Called in {@link #onStartCommand(Intent, int, int)} when the service is stopped by intent
92     *
93     * @return True in successful condition, False otherwise
94     */
95    protected abstract boolean stop();
96
97    /**
98     * Called in {@link #onDestroy()} when this object is completely discarded
99     */
100    protected void cleanup() {}
101
102    /**
103     * @param userId is equivalent to the result of ActivityManager.getCurrentUser()
104     */
105    protected void setCurrentUser(int userId) {}
106
107    /**
108     * @param userId is equivalent to the result of ActivityManager.getCurrentUser()
109     */
110    protected void setUserUnlocked(int userId) {}
111
112    protected ProfileService() {
113        mName = getName();
114    }
115
116    @Override
117    public void onCreate() {
118        if (DBG) {
119            log("onCreate");
120        }
121        super.onCreate();
122        mAdapter = BluetoothAdapter.getDefaultAdapter();
123        mBinder = initBinder();
124        create();
125    }
126
127    @Override
128    public int onStartCommand(Intent intent, int flags, int startId) {
129        if (DBG) {
130            log("onStartCommand()");
131        }
132
133        if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM)
134                != PackageManager.PERMISSION_GRANTED) {
135            Log.e(mName, "Permission denied!");
136            return PROFILE_SERVICE_MODE;
137        }
138
139        if (intent == null) {
140            Log.d(mName, "onStartCommand ignoring null intent.");
141            return PROFILE_SERVICE_MODE;
142        }
143
144        String action = intent.getStringExtra(AdapterService.EXTRA_ACTION);
145        if (AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
146            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
147            if (state == BluetoothAdapter.STATE_OFF) {
148                doStop();
149            } else if (state == BluetoothAdapter.STATE_ON) {
150                doStart();
151            }
152        }
153        return PROFILE_SERVICE_MODE;
154    }
155
156    @Override
157    public IBinder onBind(Intent intent) {
158        if (DBG) {
159            log("onBind");
160        }
161        if (mAdapter != null && mBinder == null) {
162            // initBinder returned null, you can't bind
163            throw new UnsupportedOperationException("Cannot bind to " + mName);
164        }
165        return mBinder;
166    }
167
168    @Override
169    public boolean onUnbind(Intent intent) {
170        if (DBG) {
171            log("onUnbind");
172        }
173        return super.onUnbind(intent);
174    }
175
176    /**
177     * Support dumping profile-specific information for dumpsys
178     *
179     * @param sb StringBuilder from the profile.
180     */
181    public void dump(StringBuilder sb) {
182        sb.append("\nProfile: ");
183        sb.append(mName);
184        sb.append("\n");
185    }
186
187    /**
188     * Support dumping scan events from GattService
189     *
190     * @param proto
191     */
192    public void dumpProto(BluetoothProto.BluetoothLog proto) {
193        // Do nothing
194    }
195
196    /**
197     * Append an indented String for adding dumpsys support to subclasses.
198     *
199     * @param sb StringBuilder from the profile.
200     * @param s String to indent and append.
201     */
202    public static void println(StringBuilder sb, String s) {
203        sb.append("  ");
204        sb.append(s);
205        sb.append("\n");
206    }
207
208    @Override
209    public void onDestroy() {
210        if (mAdapterService != null) {
211            mAdapterService.removeProfile(this);
212        }
213
214        cleanup();
215        if (mBinder != null) {
216            mBinder.cleanup();
217            mBinder = null;
218        }
219        super.onDestroy();
220        mAdapter = null;
221    }
222
223    private void doStart() {
224        if (mAdapter == null) {
225            Log.w(mName, "Can't start profile service: device does not have BT");
226            return;
227        }
228
229        mAdapterService = AdapterService.getAdapterService();
230        if (mAdapterService == null) {
231            Log.w(mName, "Could not add this profile because AdapterService is null.");
232            return;
233        }
234        mAdapterService.addProfile(this);
235
236        IntentFilter filter = new IntentFilter();
237        filter.addAction(Intent.ACTION_USER_SWITCHED);
238        filter.addAction(Intent.ACTION_USER_UNLOCKED);
239        mUserSwitchedReceiver = new BroadcastReceiver() {
240            @Override
241            public void onReceive(Context context, Intent intent) {
242                final String action = intent.getAction();
243                final int userId =
244                        intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
245                if (userId == UserHandle.USER_NULL) {
246                    Log.e(mName, "userChangeReceiver received an invalid EXTRA_USER_HANDLE");
247                    return;
248                }
249                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
250                    Log.d(mName, "User switched to userId " + userId);
251                    setCurrentUser(userId);
252                } else if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
253                    Log.d(mName, "Unlocked userId " + userId);
254                    setUserUnlocked(userId);
255                }
256            }
257        };
258
259        getApplicationContext().registerReceiver(mUserSwitchedReceiver, filter);
260        int currentUserId = ActivityManager.getCurrentUser();
261        setCurrentUser(currentUserId);
262        UserManager userManager = UserManager.get(getApplicationContext());
263        if (userManager.isUserUnlocked(currentUserId)) {
264            setUserUnlocked(currentUserId);
265        }
266        mProfileStarted = start();
267        if (!mProfileStarted) {
268            Log.e(mName, "Error starting profile. start() returned false.");
269            return;
270        }
271        mAdapterService.onProfileServiceStateChanged(getClass().getName(),
272                BluetoothAdapter.STATE_ON);
273    }
274
275    private void doStop() {
276        if (!mProfileStarted) {
277            Log.w(mName, "doStop() called, but the profile is not running.");
278        }
279        mProfileStarted = false;
280        if (mAdapterService != null) {
281            mAdapterService.onProfileServiceStateChanged(getClass().getName(),
282                    BluetoothAdapter.STATE_OFF);
283        }
284        if (!stop()) {
285            Log.e(mName, "Unable to stop profile");
286        }
287        if (mUserSwitchedReceiver != null) {
288            getApplicationContext().unregisterReceiver(mUserSwitchedReceiver);
289            mUserSwitchedReceiver = null;
290        }
291        stopSelf();
292    }
293
294    protected BluetoothDevice getDevice(byte[] address) {
295        if (mAdapter != null) {
296            return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
297        }
298        return null;
299    }
300
301    protected void log(String msg) {
302        Log.d(mName, msg);
303    }
304}
305