BluetoothOppManager.java revision 55470710c2624bde2aa56275d29b60055638c06c
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 D = Constants.DEBUG; 59 private static final boolean V = Constants.VERBOSE; 60 61 private static BluetoothOppManager INSTANCE; 62 63 /** Used when obtaining a reference to the singleton instance. */ 64 private static Object INSTANCE_LOCK = new Object(); 65 66 private boolean mInitialized; 67 68 private Context mContext; 69 70 private BluetoothAdapter mAdapter; 71 72 private String mMimeTypeOfSendingFile; 73 74 private String mUriOfSendingFile; 75 76 private String mMimeTypeOfSendingFiles; 77 78 private ArrayList<Uri> mUrisOfSendingFiles; 79 80 private static final String OPP_PREFERENCE_FILE = "OPPMGR"; 81 82 private static final String SENDING_FLAG = "SENDINGFLAG"; 83 84 private static final String MIME_TYPE = "MIMETYPE"; 85 86 private static final String FILE_URI = "FILE_URI"; 87 88 private static final String MIME_TYPE_MULTIPLE = "MIMETYPE_MULTIPLE"; 89 90 private static final String FILE_URIS = "FILE_URIS"; 91 92 private static final String MULTIPLE_FLAG = "MULTIPLE_FLAG"; 93 94 private static final String ARRAYLIST_ITEM_SEPERATOR = ";"; 95 96 private static final int ALLOWED_INSERT_SHARE_THREAD_NUMBER = 3; 97 98 // used to judge if need continue sending process after received a 99 // ENABLED_ACTION 100 public boolean mSendingFlag; 101 102 public boolean mMultipleFlag; 103 104 private int mfileNumInBatch; 105 106 private int mInsertShareThreadNum = 0; 107 108 /** 109 * Get singleton instance. 110 */ 111 public static BluetoothOppManager getInstance(Context context) { 112 synchronized (INSTANCE_LOCK) { 113 if (INSTANCE == null) { 114 INSTANCE = new BluetoothOppManager(); 115 } 116 INSTANCE.init(context); 117 118 return INSTANCE; 119 } 120 } 121 122 /** 123 * init 124 */ 125 private boolean init(Context context) { 126 if (mInitialized) 127 return true; 128 mInitialized = true; 129 130 mContext = context; 131 132 mAdapter = BluetoothAdapter.getDefaultAdapter(); 133 if (mAdapter == null) { 134 if (V) Log.v(TAG, "BLUETOOTH_SERVICE is not started! "); 135 } 136 137 // Restore data from preference 138 restoreApplicationData(); 139 140 return true; 141 } 142 143 /** 144 * Restore data from preference 145 */ 146 private void restoreApplicationData() { 147 SharedPreferences settings = mContext.getSharedPreferences(OPP_PREFERENCE_FILE, 0); 148 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); 155 156 if (V) Log.v(TAG, "restoreApplicationData! " + mSendingFlag + mMultipleFlag 157 + mMimeTypeOfSendingFile + mUriOfSendingFile); 158 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])); 166 } 167 } 168 169 mContext.getSharedPreferences(OPP_PREFERENCE_FILE, 0).edit().clear().apply(); 170 } 171 172 /** 173 * Save application data to preference, need restore these data when service restart 174 */ 175 private void storeApplicationData() { 176 SharedPreferences.Editor editor = mContext.getSharedPreferences(OPP_PREFERENCE_FILE, 0) 177 .edit(); 178 editor.putBoolean(SENDING_FLAG, mSendingFlag); 179 editor.putBoolean(MULTIPLE_FLAG, mMultipleFlag); 180 if (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); 187 } 188 String strUris = sb.toString(); 189 editor.putString(FILE_URIS, strUris); 190 191 editor.remove(MIME_TYPE); 192 editor.remove(FILE_URI); 193 } else { 194 editor.putString(MIME_TYPE, mMimeTypeOfSendingFile); 195 editor.putString(FILE_URI, mUriOfSendingFile); 196 197 editor.remove(MIME_TYPE_MULTIPLE); 198 editor.remove(FILE_URIS); 199 } 200 editor.apply(); 201 if (V) Log.v(TAG, "Application data stored to SharedPreference! "); 202 } 203 204 public void saveSendingFileInfo(String mimeType, String uri) { 205 synchronized (BluetoothOppManager.this) { 206 mMultipleFlag = false; 207 mMimeTypeOfSendingFile = mimeType; 208 mUriOfSendingFile = uri; 209 storeApplicationData(); 210 } 211 } 212 213 public void saveSendingFileInfo(String mimeType, ArrayList<Uri> uris) { 214 synchronized (BluetoothOppManager.this) { 215 mMultipleFlag = true; 216 mMimeTypeOfSendingFiles = mimeType; 217 mUrisOfSendingFiles = uris; 218 storeApplicationData(); 219 } 220 } 221 222 /** 223 * Get the current status of Bluetooth hardware. 224 * @return true if Bluetooth enabled, false otherwise. 225 */ 226 public boolean isEnabled() { 227 if (mAdapter != null) { 228 return mAdapter.isEnabled(); 229 } else { 230 if (V) Log.v(TAG, "BLUETOOTH_SERVICE is not available! "); 231 return false; 232 } 233 } 234 235 /** 236 * Enable Bluetooth hardware. 237 */ 238 public void enableBluetooth() { 239 if (mAdapter != null) { 240 mAdapter.enable(); 241 } 242 } 243 244 /** 245 * Disable Bluetooth hardware. 246 */ 247 public void disableBluetooth() { 248 if (mAdapter != null) { 249 mAdapter.disable(); 250 } 251 } 252 253 /** 254 * Get device name per bluetooth address. 255 */ 256 public String getDeviceName(BluetoothDevice device) { 257 String deviceName; 258 259 deviceName = BluetoothOppPreference.getInstance(mContext).getName(device); 260 261 if (deviceName == null && mAdapter != null) { 262 deviceName = device.getName(); 263 } 264 265 if (deviceName == null) { 266 deviceName = mContext.getString(R.string.unknown_device); 267 } 268 269 return deviceName; 270 } 271 272 public int getBatchSize() { 273 synchronized (BluetoothOppManager.this) { 274 return mfileNumInBatch; 275 } 276 } 277 278 /** 279 * Fork a thread to insert share info to db. 280 */ 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!"); 287 288 // Notice user 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); 294 295 return; 296 } 297 insertThread = new InsertShareInfoThread(device, mMultipleFlag, mMimeTypeOfSendingFile, 298 mUriOfSendingFile, mMimeTypeOfSendingFiles, mUrisOfSendingFiles); 299 if (mMultipleFlag) { 300 mfileNumInBatch = mUrisOfSendingFiles.size(); 301 } 302 } 303 304 insertThread.start(); 305 } 306 307 /** 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. 314 */ 315 private class InsertShareInfoThread extends Thread { 316 private final BluetoothDevice mRemoteDevice; 317 318 private final String mTypeOfSingleFile; 319 320 private final String mUri; 321 322 private final String mTypeOfMultipleFiles; 323 324 private final ArrayList<Uri> mUris; 325 326 private final boolean mIsMultiple; 327 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; 334 this.mUri = uri; 335 this.mTypeOfMultipleFiles = typeOfMultipleFiles; 336 this.mUris = uris; 337 338 synchronized (BluetoothOppManager.this) { 339 mInsertShareThreadNum++; 340 } 341 342 if (V) Log.v(TAG, "Thread id is: " + this.getId()); 343 } 344 345 @Override 346 public void run() { 347 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 348 if (mRemoteDevice == null) { 349 Log.e(TAG, "Target bt device is null!"); 350 return; 351 } 352 if (mIsMultiple) { 353 insertMultipleShare(); 354 } else { 355 insertSingleShare(); 356 } 357 synchronized (BluetoothOppManager.this) { 358 mInsertShareThreadNum--; 359 } 360 } 361 362 /** 363 * Insert multiple sending sessions to db, only used by Opp application. 364 */ 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; 375 } 376 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); 382 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)); 387 } 388 } 389 390 /** 391 * Insert single sending session to db, only used by Opp application. 392 */ 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()); 398 399 final Uri contentUri = mContext.getContentResolver().insert(BluetoothShare.CONTENT_URI, 400 values); 401 if (V) Log.v(TAG, "Insert contentUri: " + contentUri + " to device: " 402 + getDeviceName(mRemoteDevice)); 403 } 404 } 405 406} 407