2 * Copyright (c) 2008-2009, Motorola, Inc.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * - Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
16 * - Neither the name of the Motorola, Inc. nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
33 package com.android.bluetooth.opp;
35 import com.android.bluetooth.R;
37 import android.bluetooth.BluetoothAdapter;
38 import android.bluetooth.BluetoothDevice;
39 import android.content.ContentResolver;
40 import android.content.ContentValues;
41 import android.content.Context;
42 import android.content.Intent;
43 import android.content.SharedPreferences;
44 import android.net.Uri;
45 import android.os.Process;
46 import android.text.TextUtils;
47 import android.util.Log;
49 import java.util.ArrayList;
52 * This class provides a simplified interface on top of other Bluetooth service
53 * layer components; Also it handles some Opp application level variables. It's
54 * a singleton got from BluetoothOppManager.getInstance(context);
56 public class BluetoothOppManager {
57 private static final String TAG = "BluetoothOppManager";
58 private static final boolean D = Constants.DEBUG;
59 private static final boolean V = Constants.VERBOSE;
61 private static BluetoothOppManager INSTANCE;
63 /** Used when obtaining a reference to the singleton instance. */
64 private static Object INSTANCE_LOCK = new Object();
66 private boolean mInitialized;
68 private Context mContext;
70 private BluetoothAdapter mAdapter;
72 private String mMimeTypeOfSendingFile;
74 private String mUriOfSendingFile;
76 private String mMimeTypeOfSendingFiles;
78 private ArrayList<Uri> mUrisOfSendingFiles;
80 private static final String OPP_PREFERENCE_FILE = "OPPMGR";
82 private static final String SENDING_FLAG = "SENDINGFLAG";
84 private static final String MIME_TYPE = "MIMETYPE";
86 private static final String FILE_URI = "FILE_URI";
88 private static final String MIME_TYPE_MULTIPLE = "MIMETYPE_MULTIPLE";
90 private static final String FILE_URIS = "FILE_URIS";
92 private static final String MULTIPLE_FLAG = "MULTIPLE_FLAG";
94 private static final String ARRAYLIST_ITEM_SEPERATOR = ";";
96 private static final int ALLOWED_INSERT_SHARE_THREAD_NUMBER = 3;
98 // used to judge if need continue sending process after received a
100 public boolean mSendingFlag;
102 public boolean mMultipleFlag;
104 private int mfileNumInBatch;
106 private int mInsertShareThreadNum = 0;
109 * Get singleton instance.
111 public static BluetoothOppManager getInstance(Context context) {
112 synchronized (INSTANCE_LOCK) {
113 if (INSTANCE == null) {
114 INSTANCE = new BluetoothOppManager();
116 INSTANCE.init(context);
125 private boolean init(Context context) {
132 mAdapter = BluetoothAdapter.getDefaultAdapter();
133 if (mAdapter == null) {
134 if (V) Log.v(TAG, "BLUETOOTH_SERVICE is not started! ");
137 // Restore data from preference
138 restoreApplicationData();
144 * Restore data from preference
146 private void restoreApplicationData() {
147 SharedPreferences settings = mContext.getSharedPreferences(OPP_PREFERENCE_FILE, 0);
149 // All member vars are not initialized till now
150 mSendingFlag = settings.getBoolean(SENDING_FLAG, false);
151 mMimeTypeOfSendingFile = settings.getString(MIME_TYPE, null);
152 mUriOfSendingFile = settings.getString(FILE_URI, null);
153 mMimeTypeOfSendingFiles = settings.getString(MIME_TYPE_MULTIPLE, null);
154 mMultipleFlag = settings.getBoolean(MULTIPLE_FLAG, false);
156 if (V) Log.v(TAG, "restoreApplicationData! " + mSendingFlag + mMultipleFlag
157 + mMimeTypeOfSendingFile + mUriOfSendingFile);
159 String strUris = settings.getString(FILE_URIS, null);
160 mUrisOfSendingFiles = new ArrayList<Uri>();
161 if (strUris != null) {
162 String[] splitUri = strUris.split(ARRAYLIST_ITEM_SEPERATOR);
163 for (int i = 0; i < splitUri.length; i++) {
164 mUrisOfSendingFiles.add(Uri.parse(splitUri[i]));
165 if (V) Log.v(TAG, "Uri in batch: " + Uri.parse(splitUri[i]));
169 mContext.getSharedPreferences(OPP_PREFERENCE_FILE, 0).edit().clear().apply();
173 * Save application data to preference, need restore these data when service restart
175 private void storeApplicationData() {
176 SharedPreferences.Editor editor = mContext.getSharedPreferences(OPP_PREFERENCE_FILE, 0)
178 editor.putBoolean(SENDING_FLAG, mSendingFlag);
179 editor.putBoolean(MULTIPLE_FLAG, mMultipleFlag);
181 editor.putString(MIME_TYPE_MULTIPLE, mMimeTypeOfSendingFiles);
182 StringBuilder sb = new StringBuilder();
183 for (int i = 0, count = mUrisOfSendingFiles.size(); i < count; i++) {
184 Uri uriContent = mUrisOfSendingFiles.get(i);
185 sb.append(uriContent);
186 sb.append(ARRAYLIST_ITEM_SEPERATOR);
188 String strUris = sb.toString();
189 editor.putString(FILE_URIS, strUris);
191 editor.remove(MIME_TYPE);
192 editor.remove(FILE_URI);
194 editor.putString(MIME_TYPE, mMimeTypeOfSendingFile);
195 editor.putString(FILE_URI, mUriOfSendingFile);
197 editor.remove(MIME_TYPE_MULTIPLE);
198 editor.remove(FILE_URIS);
201 if (V) Log.v(TAG, "Application data stored to SharedPreference! ");
204 public void saveSendingFileInfo(String mimeType, String uri) {
205 synchronized (BluetoothOppManager.this) {
206 mMultipleFlag = false;
207 mMimeTypeOfSendingFile = mimeType;
208 mUriOfSendingFile = uri;
209 storeApplicationData();
213 public void saveSendingFileInfo(String mimeType, ArrayList<Uri> uris) {
214 synchronized (BluetoothOppManager.this) {
215 mMultipleFlag = true;
216 mMimeTypeOfSendingFiles = mimeType;
217 mUrisOfSendingFiles = uris;
218 storeApplicationData();
223 * Get the current status of Bluetooth hardware.
224 * @return true if Bluetooth enabled, false otherwise.
226 public boolean isEnabled() {
227 if (mAdapter != null) {
228 return mAdapter.isEnabled();
230 if (V) Log.v(TAG, "BLUETOOTH_SERVICE is not available! ");
236 * Enable Bluetooth hardware.
238 public void enableBluetooth() {
239 if (mAdapter != null) {
245 * Disable Bluetooth hardware.
247 public void disableBluetooth() {
248 if (mAdapter != null) {
254 * Get device name per bluetooth address.
256 public String getDeviceName(BluetoothDevice device) {
259 deviceName = BluetoothOppPreference.getInstance(mContext).getName(device);
261 if (deviceName == null && mAdapter != null) {
262 deviceName = device.getName();
265 if (deviceName == null) {
266 deviceName = mContext.getString(R.string.unknown_device);
272 public int getBatchSize() {
273 synchronized (BluetoothOppManager.this) {
274 return mfileNumInBatch;
279 * Fork a thread to insert share info to db.
281 public void startTransfer(BluetoothDevice device) {
282 if (V) Log.v(TAG, "Active InsertShareThread number is : " + mInsertShareThreadNum);
283 InsertShareInfoThread insertThread;
284 synchronized (BluetoothOppManager.this) {
285 if (mInsertShareThreadNum > ALLOWED_INSERT_SHARE_THREAD_NUMBER) {
286 Log.e(TAG, "Too many shares user triggered concurrently!");
289 Intent in = new Intent(mContext, BluetoothOppBtErrorActivity.class);
290 in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
291 in.putExtra("title", mContext.getString(R.string.enabling_progress_title));
292 in.putExtra("content", mContext.getString(R.string.ErrorTooManyRequests));
293 mContext.startActivity(in);
297 insertThread = new InsertShareInfoThread(device, mMultipleFlag, mMimeTypeOfSendingFile,
298 mUriOfSendingFile, mMimeTypeOfSendingFiles, mUrisOfSendingFiles);
300 mfileNumInBatch = mUrisOfSendingFiles.size();
304 insertThread.start();
308 * Thread to insert share info to db. In multiple files (say 100 files)
309 * share case, the inserting share info to db operation would be a time
310 * consuming operation, so need a thread to handle it. This thread allows
311 * multiple instances to support below case: User select multiple files to
312 * share to one device (say device 1), and then right away share to second
313 * device (device 2), we need insert all these share info to db.
315 private class InsertShareInfoThread extends Thread {
316 private final BluetoothDevice mRemoteDevice;
318 private final String mTypeOfSingleFile;
320 private final String mUri;
322 private final String mTypeOfMultipleFiles;
324 private final ArrayList<Uri> mUris;
326 private final boolean mIsMultiple;
328 public InsertShareInfoThread(BluetoothDevice device, boolean multiple,
329 String typeOfSingleFile, String uri, String typeOfMultipleFiles, ArrayList<Uri> uris) {
330 super("Insert ShareInfo Thread");
331 this.mRemoteDevice = device;
332 this.mIsMultiple = multiple;
333 this.mTypeOfSingleFile = typeOfSingleFile;
335 this.mTypeOfMultipleFiles = typeOfMultipleFiles;
338 synchronized (BluetoothOppManager.this) {
339 mInsertShareThreadNum++;
342 if (V) Log.v(TAG, "Thread id is: " + this.getId());
347 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
348 if (mRemoteDevice == null) {
349 Log.e(TAG, "Target bt device is null!");
353 insertMultipleShare();
357 synchronized (BluetoothOppManager.this) {
358 mInsertShareThreadNum--;
363 * Insert multiple sending sessions to db, only used by Opp application.
365 private void insertMultipleShare() {
366 int count = mUris.size();
367 Long ts = System.currentTimeMillis();
368 for (int i = 0; i < count; i++) {
369 Uri fileUri = mUris.get(i);
370 ContentResolver contentResolver = mContext.getContentResolver();
371 String contentType = contentResolver.getType(fileUri);
372 if (V) Log.v(TAG, "Got mimetype: " + contentType + " Got uri: " + fileUri);
373 if (TextUtils.isEmpty(contentType)) {
374 contentType = mTypeOfMultipleFiles;
377 ContentValues values = new ContentValues();
378 values.put(BluetoothShare.URI, fileUri.toString());
379 values.put(BluetoothShare.MIMETYPE, contentType);
380 values.put(BluetoothShare.DESTINATION, mRemoteDevice.getAddress());
381 values.put(BluetoothShare.TIMESTAMP, ts);
383 final Uri contentUri = mContext.getContentResolver().insert(
384 BluetoothShare.CONTENT_URI, values);
385 if (V) Log.v(TAG, "Insert contentUri: " + contentUri + " to device: "
386 + getDeviceName(mRemoteDevice));
391 * Insert single sending session to db, only used by Opp application.
393 private void insertSingleShare() {
394 ContentValues values = new ContentValues();
395 values.put(BluetoothShare.URI, mUri);
396 values.put(BluetoothShare.MIMETYPE, mTypeOfSingleFile);
397 values.put(BluetoothShare.DESTINATION, mRemoteDevice.getAddress());
399 final Uri contentUri = mContext.getContentResolver().insert(BluetoothShare.CONTENT_URI,
401 if (V) Log.v(TAG, "Insert contentUri: " + contentUri + " to device: "
402 + getDeviceName(mRemoteDevice));