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 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