BluetoothOppManager.java revision a930b6831d0c70b6c5d34e548e6b1dceaa6529a0
1/* 2 * Copyright (c) 2008-2009, Motorola, Inc. 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * - Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 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. 15 * 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. 19 * 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. 31 */ 32 33package com.android.bluetooth.opp; 34 35import com.android.bluetooth.R; 36 37import android.bluetooth.BluetoothAdapter; 38import android.bluetooth.BluetoothDevice; 39import android.content.ContentResolver; 40import android.content.ContentValues; 41import android.content.Context; 42import android.content.Intent; 43import android.content.SharedPreferences; 44import android.net.Uri; 45import android.os.Process; 46import android.text.TextUtils; 47import android.util.Log; 48 49import java.util.ArrayList; 50 51/** 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); 55 */ 56public class BluetoothOppManager { 57 private static final String TAG = "BluetoothOppManager"; 58 private static final boolean V = Constants.VERBOSE; 59 60 private static BluetoothOppManager INSTANCE; 61 62 /** Used when obtaining a reference to the singleton instance. */ 63 private static Object INSTANCE_LOCK = new Object(); 64 65 private boolean mInitialized; 66 67 private Context mContext; 68 69 private BluetoothAdapter mAdapter; 70 71 private String mMimeTypeOfSendigFile; 72 73 private String mUriOfSendingFile; 74 75 private String mMimeTypeOfSendigFiles; 76 77 private ArrayList<Uri> mUrisOfSendingFiles; 78 79 private static final String OPP_PREFERENCE_FILE = "OPPMGR"; 80 81 private static final String SENDING_FLAG = "SENDINGFLAG"; 82 83 private static final String MIME_TYPE = "MIMETYPE"; 84 85 private static final String FILE_URI = "FILE_URI"; 86 87 private static final String MIME_TYPE_MULTIPLE = "MIMETYPE_MULTIPLE"; 88 89 private static final String FILE_URIS = "FILE_URIS"; 90 91 private static final String MULTIPLE_FLAG = "MULTIPLE_FLAG"; 92 93 private static final String ARRAYLIST_ITEM_SEPERATOR = ";"; 94 95 private static final int ALLOWED_INSERT_SHARE_THREAD_NUMBER = 3; 96 97 // used to judge if need continue sending process after received a 98 // ENABLED_ACTION 99 public boolean mSendingFlag; 100 101 public boolean mMultipleFlag; 102 103 private int mfileNumInBatch; 104 105 private int mInsertShareThreadNum = 0; 106 107 /** 108 * Get singleton instance. 109 */ 110 public static BluetoothOppManager getInstance(Context context) { 111 synchronized (INSTANCE_LOCK) { 112 if (INSTANCE == null) { 113 INSTANCE = new BluetoothOppManager(); 114 } 115 INSTANCE.init(context); 116 117 return INSTANCE; 118 } 119 } 120 121 /** 122 * init 123 */ 124 private boolean init(Context context) { 125 if (mInitialized) 126 return true; 127 mInitialized = true; 128 129 mContext = context; 130 131 mAdapter = BluetoothAdapter.getDefaultAdapter(); 132 if (mAdapter == null) { 133 if (V) Log.v(TAG, "BLUETOOTH_SERVICE is not started! "); 134 } 135 136 // Restore data from preference 137 restoreApplicationData(); 138 139 return true; 140 } 141 142 /** 143 * Restore data from preference 144 */ 145 private void restoreApplicationData() { 146 SharedPreferences settings = mContext.getSharedPreferences(OPP_PREFERENCE_FILE, 0); 147 148 // All member vars are not initialized till now 149 mSendingFlag = settings.getBoolean(SENDING_FLAG, false); 150 mMimeTypeOfSendigFile = settings.getString(MIME_TYPE, null); 151 mUriOfSendingFile = settings.getString(FILE_URI, null); 152 mMimeTypeOfSendigFiles = settings.getString(MIME_TYPE_MULTIPLE, null); 153 mMultipleFlag = settings.getBoolean(MULTIPLE_FLAG, false); 154 155 if (V) Log.v(TAG, "restoreApplicationData! " + mSendingFlag + mMultipleFlag 156 + mMimeTypeOfSendigFile + mUriOfSendingFile); 157 158 String strUris = settings.getString(FILE_URIS, null); 159 mUrisOfSendingFiles = new ArrayList<Uri>(); 160 if (strUris != null) { 161 String[] splitUri = strUris.split(ARRAYLIST_ITEM_SEPERATOR); 162 for (int i = 0; i < splitUri.length; i++) { 163 mUrisOfSendingFiles.add(Uri.parse(splitUri[i])); 164 if (V) Log.v(TAG, "Uri in batch: " + Uri.parse(splitUri[i])); 165 } 166 } 167 168 mContext.getSharedPreferences(OPP_PREFERENCE_FILE, 0).edit().clear().commit(); 169 } 170 171 /** 172 * Save application data to preference, need restore these data when service restart 173 */ 174 private void storeApplicationData() { 175 SharedPreferences.Editor editor = mContext.getSharedPreferences(OPP_PREFERENCE_FILE, 0) 176 .edit(); 177 editor.putBoolean(SENDING_FLAG, mSendingFlag).commit(); 178 editor.putBoolean(MULTIPLE_FLAG, mMultipleFlag).commit(); 179 if (mMultipleFlag) { 180 editor.putString(MIME_TYPE_MULTIPLE, mMimeTypeOfSendigFiles).commit(); 181 StringBuilder sb = new StringBuilder(); 182 for (int i = 0, count = mUrisOfSendingFiles.size(); i < count; i++) { 183 Uri uriContent = mUrisOfSendingFiles.get(i); 184 sb.append(uriContent); 185 sb.append(ARRAYLIST_ITEM_SEPERATOR); 186 } 187 String strUris = sb.toString(); 188 editor.putString(FILE_URIS, strUris).commit(); 189 190 editor.remove(MIME_TYPE).commit(); 191 editor.remove(FILE_URI).commit(); 192 } else { 193 editor.putString(MIME_TYPE, mMimeTypeOfSendigFile).commit(); 194 editor.putString(FILE_URI, mUriOfSendingFile).commit(); 195 196 editor.remove(MIME_TYPE_MULTIPLE).commit(); 197 editor.remove(FILE_URIS).commit(); 198 } 199 if (V) Log.v(TAG, "Application data stored to SharedPreference! "); 200 } 201 202 public void saveSendingFileInfo(String mimeType, String uri) { 203 synchronized (BluetoothOppManager.this) { 204 mMultipleFlag = false; 205 mMimeTypeOfSendigFile = mimeType; 206 mUriOfSendingFile = uri; 207 storeApplicationData(); 208 } 209 } 210 211 public void saveSendingFileInfo(String mimeType, ArrayList<Uri> uris) { 212 synchronized (BluetoothOppManager.this) { 213 mMultipleFlag = true; 214 mMimeTypeOfSendigFiles = mimeType; 215 mUrisOfSendingFiles = uris; 216 storeApplicationData(); 217 } 218 } 219 220 /** 221 * Get the current status of Bluetooth hardware. 222 * @return true if Bluetooth enabled, false otherwise. 223 */ 224 public boolean isEnabled() { 225 if (mAdapter != null) { 226 return mAdapter.isEnabled(); 227 } else { 228 if (V) Log.v(TAG, "BLUETOOTH_SERVICE is not available! "); 229 return false; 230 } 231 } 232 233 /** 234 * Enable Bluetooth hardware. 235 */ 236 public void enableBluetooth() { 237 if (mAdapter != null) { 238 mAdapter.enable(); 239 } 240 } 241 242 /** 243 * Disable Bluetooth hardware. 244 */ 245 public void disableBluetooth() { 246 if (mAdapter != null) { 247 mAdapter.disable(); 248 } 249 } 250 251 /** 252 * Get device name per bluetooth address. 253 */ 254 public String getDeviceName(BluetoothDevice device) { 255 String deviceName; 256 257 deviceName = BluetoothOppPreference.getInstance(mContext).getName(device); 258 259 if (deviceName == null && mAdapter != null) { 260 deviceName = device.getName(); 261 } 262 263 if (deviceName == null) { 264 deviceName = mContext.getString(R.string.unknown_device); 265 } 266 267 return deviceName; 268 } 269 270 public int getBatchSize() { 271 synchronized (BluetoothOppManager.this) { 272 return mfileNumInBatch; 273 } 274 } 275 276 /** 277 * Fork a thread to insert share info to db. 278 */ 279 public void startTransfer(BluetoothDevice device) { 280 if (V) Log.v(TAG, "Active InsertShareThread number is : " + mInsertShareThreadNum); 281 InsertShareInfoThread insertThread; 282 synchronized (BluetoothOppManager.this) { 283 if (mInsertShareThreadNum > ALLOWED_INSERT_SHARE_THREAD_NUMBER) { 284 Log.e(TAG, "Too many shares user triggered concurrently!"); 285 286 // Notice user 287 Intent in = new Intent(mContext, BluetoothOppBtErrorActivity.class); 288 in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 289 in.putExtra("title", mContext.getString(R.string.enabling_progress_title)); 290 in.putExtra("content", mContext.getString(R.string.ErrorTooManyRequests)); 291 mContext.startActivity(in); 292 293 return; 294 } 295 insertThread = new InsertShareInfoThread(device, mMultipleFlag, mMimeTypeOfSendigFile, 296 mUriOfSendingFile, mMimeTypeOfSendigFiles, mUrisOfSendingFiles); 297 if (mMultipleFlag) { 298 mfileNumInBatch = mUrisOfSendingFiles.size(); 299 } 300 } 301 302 insertThread.start(); 303 } 304 305 /** 306 * Thread to insert share info to db. In multiple files (say 100 files) 307 * share case, the inserting share info to db operation would be a time 308 * consuming operation, so need a thread to handle it. This thread allows 309 * multiple instances to support below case: User select multiple files to 310 * share to one device (say device 1), and then right away share to second 311 * device (device 2), we need insert all these share info to db. 312 */ 313 private class InsertShareInfoThread extends Thread { 314 private final BluetoothDevice mRemoteDevice; 315 316 private final String mTypeOfSingleFile; 317 318 private final String mUri; 319 320 private final String mTypeOfMultipleFiles; 321 322 private final ArrayList<Uri> mUris; 323 324 private final boolean mIsMultiple; 325 326 public InsertShareInfoThread(BluetoothDevice device, boolean multiple, 327 String typeOfSingleFile, String uri, String typeOfMultipleFiles, ArrayList<Uri> uris) { 328 super("Insert ShareInfo Thread"); 329 this.mRemoteDevice = device; 330 this.mIsMultiple = multiple; 331 this.mTypeOfSingleFile = typeOfSingleFile; 332 this.mUri = uri; 333 this.mTypeOfMultipleFiles = typeOfMultipleFiles; 334 this.mUris = uris; 335 336 synchronized (BluetoothOppManager.this) { 337 mInsertShareThreadNum++; 338 } 339 340 if (V) Log.v(TAG, "Thread id is: " + this.getId()); 341 } 342 343 @Override 344 public void run() { 345 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 346 if (mRemoteDevice == null) { 347 Log.e(TAG, "Target bt device is null!"); 348 return; 349 } 350 if (mIsMultiple) { 351 insertMultipleShare(); 352 } else { 353 insertSingleShare(); 354 } 355 synchronized (BluetoothOppManager.this) { 356 mInsertShareThreadNum--; 357 } 358 } 359 360 /** 361 * Insert multiple sending sessions to db, only used by Opp application. 362 */ 363 private void insertMultipleShare() { 364 int count = mUris.size(); 365 Long ts = System.currentTimeMillis(); 366 for (int i = 0; i < count; i++) { 367 Uri fileUri = mUris.get(i); 368 ContentResolver contentResolver = mContext.getContentResolver(); 369 String contentType = contentResolver.getType(fileUri); 370 if (V) Log.v(TAG, "Got mimetype: " + contentType + " Got uri: " + fileUri); 371 if (TextUtils.isEmpty(contentType)) { 372 contentType = mTypeOfMultipleFiles; 373 } 374 375 ContentValues values = new ContentValues(); 376 values.put(BluetoothShare.URI, fileUri.toString()); 377 values.put(BluetoothShare.MIMETYPE, contentType); 378 values.put(BluetoothShare.DESTINATION, mRemoteDevice.getAddress()); 379 values.put(BluetoothShare.TIMESTAMP, ts); 380 381 final Uri contentUri = mContext.getContentResolver().insert( 382 BluetoothShare.CONTENT_URI, values); 383 if (V) Log.v(TAG, "Insert contentUri: " + contentUri + " to device: " 384 + getDeviceName(mRemoteDevice)); 385 } 386 } 387 388 /** 389 * Insert single sending session to db, only used by Opp application. 390 */ 391 private void insertSingleShare() { 392 ContentValues values = new ContentValues(); 393 values.put(BluetoothShare.URI, mUri); 394 values.put(BluetoothShare.MIMETYPE, mTypeOfSingleFile); 395 values.put(BluetoothShare.DESTINATION, mRemoteDevice.getAddress()); 396 397 final Uri contentUri = mContext.getContentResolver().insert(BluetoothShare.CONTENT_URI, 398 values); 399 if (V) Log.v(TAG, "Insert contentUri: " + contentUri + " to device: " 400 + getDeviceName(mRemoteDevice)); 401 } 402 } 403 404} 405