Home | History | Annotate | Download | only in opp
      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 
     33 package com.android.bluetooth.opp;
     34 
     35 import com.android.bluetooth.R;
     36 
     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;
     48 
     49 import 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  */
     56 public 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 mMimeTypeOfSendingFile;
     72 
     73     private String mUriOfSendingFile;
     74 
     75     private String mMimeTypeOfSendingFiles;
     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         mMimeTypeOfSendingFile = settings.getString(MIME_TYPE, null);
    151         mUriOfSendingFile = settings.getString(FILE_URI, null);
    152         mMimeTypeOfSendingFiles = settings.getString(MIME_TYPE_MULTIPLE, null);
    153         mMultipleFlag = settings.getBoolean(MULTIPLE_FLAG, false);
    154 
    155         if (V) Log.v(TAG, "restoreApplicationData! " + mSendingFlag + mMultipleFlag
    156                     + mMimeTypeOfSendingFile + 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().apply();
    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);
    178         editor.putBoolean(MULTIPLE_FLAG, mMultipleFlag);
    179         if (mMultipleFlag) {
    180             editor.putString(MIME_TYPE_MULTIPLE, mMimeTypeOfSendingFiles);
    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);
    189 
    190             editor.remove(MIME_TYPE);
    191             editor.remove(FILE_URI);
    192         } else {
    193             editor.putString(MIME_TYPE, mMimeTypeOfSendingFile);
    194             editor.putString(FILE_URI, mUriOfSendingFile);
    195 
    196             editor.remove(MIME_TYPE_MULTIPLE);
    197             editor.remove(FILE_URIS);
    198         }
    199         editor.apply();
    200         if (V) Log.v(TAG, "Application data stored to SharedPreference! ");
    201     }
    202 
    203     public void saveSendingFileInfo(String mimeType, String uri) {
    204         synchronized (BluetoothOppManager.this) {
    205             mMultipleFlag = false;
    206             mMimeTypeOfSendingFile = mimeType;
    207             mUriOfSendingFile = uri;
    208             storeApplicationData();
    209         }
    210     }
    211 
    212     public void saveSendingFileInfo(String mimeType, ArrayList<Uri> uris) {
    213         synchronized (BluetoothOppManager.this) {
    214             mMultipleFlag = true;
    215             mMimeTypeOfSendingFiles = mimeType;
    216             mUrisOfSendingFiles = uris;
    217             storeApplicationData();
    218         }
    219     }
    220 
    221     /**
    222      * Get the current status of Bluetooth hardware.
    223      * @return true if Bluetooth enabled, false otherwise.
    224      */
    225     public boolean isEnabled() {
    226         if (mAdapter != null) {
    227             return mAdapter.isEnabled();
    228         } else {
    229             if (V) Log.v(TAG, "BLUETOOTH_SERVICE is not available! ");
    230             return false;
    231         }
    232     }
    233 
    234     /**
    235      * Enable Bluetooth hardware.
    236      */
    237     public void enableBluetooth() {
    238         if (mAdapter != null) {
    239             mAdapter.enable();
    240         }
    241     }
    242 
    243     /**
    244      * Disable Bluetooth hardware.
    245      */
    246     public void disableBluetooth() {
    247         if (mAdapter != null) {
    248             mAdapter.disable();
    249         }
    250     }
    251 
    252     /**
    253      * Get device name per bluetooth address.
    254      */
    255     public String getDeviceName(BluetoothDevice device) {
    256         String deviceName;
    257 
    258         deviceName = BluetoothOppPreference.getInstance(mContext).getName(device);
    259 
    260         if (deviceName == null && mAdapter != null) {
    261             deviceName = device.getName();
    262         }
    263 
    264         if (deviceName == null) {
    265             deviceName = mContext.getString(R.string.unknown_device);
    266         }
    267 
    268         return deviceName;
    269     }
    270 
    271     public int getBatchSize() {
    272         synchronized (BluetoothOppManager.this) {
    273             return mfileNumInBatch;
    274         }
    275     }
    276 
    277     /**
    278      * Fork a thread to insert share info to db.
    279      */
    280     public void startTransfer(BluetoothDevice device) {
    281         if (V) Log.v(TAG, "Active InsertShareThread number is : " + mInsertShareThreadNum);
    282         InsertShareInfoThread insertThread;
    283         synchronized (BluetoothOppManager.this) {
    284             if (mInsertShareThreadNum > ALLOWED_INSERT_SHARE_THREAD_NUMBER) {
    285                 Log.e(TAG, "Too many shares user triggered concurrently!");
    286 
    287                 // Notice user
    288                 Intent in = new Intent(mContext, BluetoothOppBtErrorActivity.class);
    289                 in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    290                 in.putExtra("title", mContext.getString(R.string.enabling_progress_title));
    291                 in.putExtra("content", mContext.getString(R.string.ErrorTooManyRequests));
    292                 mContext.startActivity(in);
    293 
    294                 return;
    295             }
    296             insertThread = new InsertShareInfoThread(device, mMultipleFlag, mMimeTypeOfSendingFile,
    297                     mUriOfSendingFile, mMimeTypeOfSendingFiles, mUrisOfSendingFiles);
    298             if (mMultipleFlag) {
    299                 mfileNumInBatch = mUrisOfSendingFiles.size();
    300             }
    301         }
    302 
    303         insertThread.start();
    304     }
    305 
    306     /**
    307      * Thread to insert share info to db. In multiple files (say 100 files)
    308      * share case, the inserting share info to db operation would be a time
    309      * consuming operation, so need a thread to handle it. This thread allows
    310      * multiple instances to support below case: User select multiple files to
    311      * share to one device (say device 1), and then right away share to second
    312      * device (device 2), we need insert all these share info to db.
    313      */
    314     private class InsertShareInfoThread extends Thread {
    315         private final BluetoothDevice mRemoteDevice;
    316 
    317         private final String mTypeOfSingleFile;
    318 
    319         private final String mUri;
    320 
    321         private final String mTypeOfMultipleFiles;
    322 
    323         private final ArrayList<Uri> mUris;
    324 
    325         private final boolean mIsMultiple;
    326 
    327         public InsertShareInfoThread(BluetoothDevice device, boolean multiple,
    328                 String typeOfSingleFile, String uri, String typeOfMultipleFiles, ArrayList<Uri> uris) {
    329             super("Insert ShareInfo Thread");
    330             this.mRemoteDevice = device;
    331             this.mIsMultiple = multiple;
    332             this.mTypeOfSingleFile = typeOfSingleFile;
    333             this.mUri = uri;
    334             this.mTypeOfMultipleFiles = typeOfMultipleFiles;
    335             this.mUris = uris;
    336 
    337             synchronized (BluetoothOppManager.this) {
    338                 mInsertShareThreadNum++;
    339             }
    340 
    341             if (V) Log.v(TAG, "Thread id is: " + this.getId());
    342         }
    343 
    344         @Override
    345         public void run() {
    346             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    347             if (mRemoteDevice == null) {
    348                 Log.e(TAG, "Target bt device is null!");
    349                 return;
    350             }
    351             if (mIsMultiple) {
    352                 insertMultipleShare();
    353             } else {
    354                 insertSingleShare();
    355             }
    356             synchronized (BluetoothOppManager.this) {
    357                 mInsertShareThreadNum--;
    358             }
    359         }
    360 
    361         /**
    362          * Insert multiple sending sessions to db, only used by Opp application.
    363          */
    364         private void insertMultipleShare() {
    365             int count = mUris.size();
    366             Long ts = System.currentTimeMillis();
    367             for (int i = 0; i < count; i++) {
    368                 Uri fileUri = mUris.get(i);
    369                 ContentResolver contentResolver = mContext.getContentResolver();
    370                 String contentType = contentResolver.getType(fileUri);
    371                 if (V) Log.v(TAG, "Got mimetype: " + contentType + "  Got uri: " + fileUri);
    372                 if (TextUtils.isEmpty(contentType)) {
    373                     contentType = mTypeOfMultipleFiles;
    374                 }
    375 
    376                 ContentValues values = new ContentValues();
    377                 values.put(BluetoothShare.URI, fileUri.toString());
    378                 values.put(BluetoothShare.MIMETYPE, contentType);
    379                 values.put(BluetoothShare.DESTINATION, mRemoteDevice.getAddress());
    380                 values.put(BluetoothShare.TIMESTAMP, ts);
    381 
    382                 final Uri contentUri = mContext.getContentResolver().insert(
    383                         BluetoothShare.CONTENT_URI, values);
    384                 if (V) Log.v(TAG, "Insert contentUri: " + contentUri + "  to device: "
    385                             + getDeviceName(mRemoteDevice));
    386             }
    387         }
    388 
    389          /**
    390          * Insert single sending session to db, only used by Opp application.
    391          */
    392         private void insertSingleShare() {
    393             ContentValues values = new ContentValues();
    394             values.put(BluetoothShare.URI, mUri);
    395             values.put(BluetoothShare.MIMETYPE, mTypeOfSingleFile);
    396             values.put(BluetoothShare.DESTINATION, mRemoteDevice.getAddress());
    397 
    398             final Uri contentUri = mContext.getContentResolver().insert(BluetoothShare.CONTENT_URI,
    399                     values);
    400             if (V) Log.v(TAG, "Insert contentUri: " + contentUri + "  to device: "
    401                                 + getDeviceName(mRemoteDevice));
    402         }
    403     }
    404 
    405 }
    406