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 import com.google.android.collect.Lists;
     37 
     38 
     39 import android.bluetooth.BluetoothAdapter;
     40 import android.bluetooth.BluetoothDevice;
     41 import android.net.Uri;
     42 import android.content.ContentValues;
     43 import android.content.Context;
     44 import android.content.ActivityNotFoundException;
     45 import android.content.Intent;
     46 import android.content.pm.PackageManager;
     47 import android.content.pm.ResolveInfo;
     48 import android.database.Cursor;
     49 import android.util.Log;
     50 
     51 import java.io.File;
     52 import java.io.IOException;
     53 import java.util.ArrayList;
     54 import java.util.List;
     55 import java.util.concurrent.ConcurrentHashMap;
     56 
     57 /**
     58  * This class has some utilities for Opp application;
     59  */
     60 public class BluetoothOppUtility {
     61     private static final String TAG = "BluetoothOppUtility";
     62     private static final boolean D = Constants.DEBUG;
     63     private static final boolean V = Constants.VERBOSE;
     64 
     65     private static final ConcurrentHashMap<Uri, BluetoothOppSendFileInfo> sSendFileMap
     66             = new ConcurrentHashMap<Uri, BluetoothOppSendFileInfo>();
     67 
     68     public static BluetoothOppTransferInfo queryRecord(Context context, Uri uri) {
     69         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
     70         BluetoothOppTransferInfo info = new BluetoothOppTransferInfo();
     71         Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
     72         if (cursor != null) {
     73             if (cursor.moveToFirst()) {
     74                 info.mID = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID));
     75                 info.mStatus = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.STATUS));
     76                 info.mDirection = cursor.getInt(cursor
     77                         .getColumnIndexOrThrow(BluetoothShare.DIRECTION));
     78                 info.mTotalBytes = cursor.getInt(cursor
     79                         .getColumnIndexOrThrow(BluetoothShare.TOTAL_BYTES));
     80                 info.mCurrentBytes = cursor.getInt(cursor
     81                         .getColumnIndexOrThrow(BluetoothShare.CURRENT_BYTES));
     82                 info.mTimeStamp = cursor.getLong(cursor
     83                         .getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
     84                 info.mDestAddr = cursor.getString(cursor
     85                         .getColumnIndexOrThrow(BluetoothShare.DESTINATION));
     86 
     87                 info.mFileName = cursor.getString(cursor
     88                         .getColumnIndexOrThrow(BluetoothShare._DATA));
     89                 if (info.mFileName == null) {
     90                     info.mFileName = cursor.getString(cursor
     91                             .getColumnIndexOrThrow(BluetoothShare.FILENAME_HINT));
     92                 }
     93                 if (info.mFileName == null) {
     94                     info.mFileName = context.getString(R.string.unknown_file);
     95                 }
     96 
     97                 info.mFileUri = cursor.getString(cursor.getColumnIndexOrThrow(BluetoothShare.URI));
     98 
     99                 if (info.mFileUri != null) {
    100                     Uri u = originalUri(Uri.parse(info.mFileUri));
    101                     info.mFileType = context.getContentResolver().getType(u);
    102                 } else {
    103                     Uri u = Uri.parse(info.mFileName);
    104                     info.mFileType = context.getContentResolver().getType(u);
    105                 }
    106                 if (info.mFileType == null) {
    107                     info.mFileType = cursor.getString(cursor
    108                             .getColumnIndexOrThrow(BluetoothShare.MIMETYPE));
    109                 }
    110 
    111                 BluetoothDevice remoteDevice = adapter.getRemoteDevice(info.mDestAddr);
    112                 info.mDeviceName =
    113                         BluetoothOppManager.getInstance(context).getDeviceName(remoteDevice);
    114 
    115                 int confirmationType = cursor.getInt(
    116                         cursor.getColumnIndexOrThrow(BluetoothShare.USER_CONFIRMATION));
    117                 info.mHandoverInitiated =
    118                         confirmationType == BluetoothShare.USER_CONFIRMATION_HANDOVER_CONFIRMED;
    119 
    120                 if (V) Log.v(TAG, "Get data from db:" + info.mFileName + info.mFileType
    121                             + info.mDestAddr);
    122             }
    123             cursor.close();
    124         } else {
    125             info = null;
    126             if (V) Log.v(TAG, "BluetoothOppManager Error: not got data from db for uri:" + uri);
    127         }
    128         return info;
    129     }
    130 
    131     /**
    132      * Organize Array list for transfers in one batch
    133      */
    134     // This function is used when UI show batch transfer. Currently only show single transfer.
    135     public static ArrayList<String> queryTransfersInBatch(Context context, Long timeStamp) {
    136         ArrayList<String> uris = Lists.newArrayList();
    137         final String WHERE = BluetoothShare.TIMESTAMP + " == " + timeStamp;
    138 
    139         Cursor metadataCursor = context.getContentResolver().query(BluetoothShare.CONTENT_URI,
    140                 new String[] {
    141                     BluetoothShare._DATA
    142                 }, WHERE, null, BluetoothShare._ID);
    143 
    144         if (metadataCursor == null) {
    145             return null;
    146         }
    147 
    148         for (metadataCursor.moveToFirst(); !metadataCursor.isAfterLast(); metadataCursor
    149                 .moveToNext()) {
    150             String fileName = metadataCursor.getString(0);
    151             Uri path = Uri.parse(fileName);
    152             // If there is no scheme, then it must be a file
    153             if (path.getScheme() == null) {
    154                 path = Uri.fromFile(new File(fileName));
    155             }
    156             uris.add(path.toString());
    157             if (V) Log.d(TAG, "Uri in this batch: " + path.toString());
    158         }
    159         metadataCursor.close();
    160         return uris;
    161     }
    162 
    163     /**
    164      * Open the received file with appropriate application, if can not find
    165      * application to handle, display error dialog.
    166      */
    167     public static void openReceivedFile(Context context, String fileName, String mimetype,
    168             Long timeStamp, Uri uri) {
    169         if (fileName == null || mimetype == null) {
    170             Log.e(TAG, "ERROR: Para fileName ==null, or mimetype == null");
    171             return;
    172         }
    173 
    174         File f = new File(fileName);
    175         if (!f.exists()) {
    176             Intent in = new Intent(context, BluetoothOppBtErrorActivity.class);
    177             in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    178             in.putExtra("title", context.getString(R.string.not_exist_file));
    179             in.putExtra("content", context.getString(R.string.not_exist_file_desc));
    180             context.startActivity(in);
    181 
    182             // Due to the file is not existing, delete related info in btopp db
    183             // to prevent this file from appearing in live folder
    184             if (V) Log.d(TAG, "This uri will be deleted: " + uri);
    185             context.getContentResolver().delete(uri, null, null);
    186             return;
    187         }
    188 
    189         Uri path = Uri.parse(fileName);
    190         // If there is no scheme, then it must be a file
    191         if (path.getScheme() == null) {
    192             path = Uri.fromFile(new File(fileName));
    193         }
    194 
    195         if (isRecognizedFileType(context, path, mimetype)) {
    196             Intent activityIntent = new Intent(Intent.ACTION_VIEW);
    197             activityIntent.setDataAndTypeAndNormalize(path, mimetype);
    198 
    199             activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    200             try {
    201                 if (V) Log.d(TAG, "ACTION_VIEW intent sent out: " + path + " / " + mimetype);
    202                 context.startActivity(activityIntent);
    203             } catch (ActivityNotFoundException ex) {
    204                 if (V) Log.d(TAG, "no activity for handling ACTION_VIEW intent:  " + mimetype, ex);
    205             }
    206         } else {
    207             Intent in = new Intent(context, BluetoothOppBtErrorActivity.class);
    208             in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    209             in.putExtra("title", context.getString(R.string.unknown_file));
    210             in.putExtra("content", context.getString(R.string.unknown_file_desc));
    211             context.startActivity(in);
    212         }
    213     }
    214 
    215     /**
    216      * To judge if the file type supported (can be handled by some app) by phone
    217      * system.
    218      */
    219     public static boolean isRecognizedFileType(Context context, Uri fileUri, String mimetype) {
    220         boolean ret = true;
    221 
    222         if (D) Log.d(TAG, "RecognizedFileType() fileUri: " + fileUri + " mimetype: " + mimetype);
    223 
    224         Intent mimetypeIntent = new Intent(Intent.ACTION_VIEW);
    225         mimetypeIntent.setDataAndTypeAndNormalize(fileUri, mimetype);
    226         List<ResolveInfo> list = context.getPackageManager().queryIntentActivities(mimetypeIntent,
    227                 PackageManager.MATCH_DEFAULT_ONLY);
    228 
    229         if (list.size() == 0) {
    230             if (D) Log.d(TAG, "NO application to handle MIME type " + mimetype);
    231             ret = false;
    232         }
    233         return ret;
    234     }
    235 
    236     /**
    237      * update visibility to Hidden
    238      */
    239     public static void updateVisibilityToHidden(Context context, Uri uri) {
    240         ContentValues updateValues = new ContentValues();
    241         updateValues.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_HIDDEN);
    242         context.getContentResolver().update(uri, updateValues, null, null);
    243     }
    244 
    245     /**
    246      * Helper function to build the progress text.
    247      */
    248     public static String formatProgressText(long totalBytes, long currentBytes) {
    249         if (totalBytes <= 0) {
    250             return "0%";
    251         }
    252         long progress = currentBytes * 100 / totalBytes;
    253         StringBuilder sb = new StringBuilder();
    254         sb.append(progress);
    255         sb.append('%');
    256         return sb.toString();
    257     }
    258 
    259     /**
    260      * Get status description according to status code.
    261      */
    262     public static String getStatusDescription(Context context, int statusCode, String deviceName) {
    263         String ret;
    264         if (statusCode == BluetoothShare.STATUS_PENDING) {
    265             ret = context.getString(R.string.status_pending);
    266         } else if (statusCode == BluetoothShare.STATUS_RUNNING) {
    267             ret = context.getString(R.string.status_running);
    268         } else if (statusCode == BluetoothShare.STATUS_SUCCESS) {
    269             ret = context.getString(R.string.status_success);
    270         } else if (statusCode == BluetoothShare.STATUS_NOT_ACCEPTABLE) {
    271             ret = context.getString(R.string.status_not_accept);
    272         } else if (statusCode == BluetoothShare.STATUS_FORBIDDEN) {
    273             ret = context.getString(R.string.status_forbidden);
    274         } else if (statusCode == BluetoothShare.STATUS_CANCELED) {
    275             ret = context.getString(R.string.status_canceled);
    276         } else if (statusCode == BluetoothShare.STATUS_FILE_ERROR) {
    277             ret = context.getString(R.string.status_file_error);
    278         } else if (statusCode == BluetoothShare.STATUS_ERROR_NO_SDCARD) {
    279             ret = context.getString(R.string.status_no_sd_card);
    280         } else if (statusCode == BluetoothShare.STATUS_CONNECTION_ERROR) {
    281             ret = context.getString(R.string.status_connection_error);
    282         } else if (statusCode == BluetoothShare.STATUS_ERROR_SDCARD_FULL) {
    283             ret = context.getString(R.string.bt_sm_2_1, deviceName);
    284         } else if ((statusCode == BluetoothShare.STATUS_BAD_REQUEST)
    285                 || (statusCode == BluetoothShare.STATUS_LENGTH_REQUIRED)
    286                 || (statusCode == BluetoothShare.STATUS_PRECONDITION_FAILED)
    287                 || (statusCode == BluetoothShare.STATUS_UNHANDLED_OBEX_CODE)
    288                 || (statusCode == BluetoothShare.STATUS_OBEX_DATA_ERROR)) {
    289             ret = context.getString(R.string.status_protocol_error);
    290         } else {
    291             ret = context.getString(R.string.status_unknown_error);
    292         }
    293         return ret;
    294     }
    295 
    296     /**
    297      * Retry the failed transfer: Will insert a new transfer session to db
    298      */
    299     public static void retryTransfer(Context context, BluetoothOppTransferInfo transInfo) {
    300         ContentValues values = new ContentValues();
    301         values.put(BluetoothShare.URI, transInfo.mFileUri);
    302         values.put(BluetoothShare.MIMETYPE, transInfo.mFileType);
    303         values.put(BluetoothShare.DESTINATION, transInfo.mDestAddr);
    304 
    305         final Uri contentUri = context.getContentResolver().insert(BluetoothShare.CONTENT_URI,
    306                 values);
    307         if (V) Log.v(TAG, "Insert contentUri: " + contentUri + "  to device: " +
    308                 transInfo.mDeviceName);
    309     }
    310 
    311     static Uri originalUri(Uri uri) {
    312         String mUri = uri.toString();
    313         int atIndex = mUri.lastIndexOf("@");
    314         if (atIndex != -1) {
    315             mUri = mUri.substring(0, atIndex);
    316             uri = Uri.parse(mUri);
    317         }
    318         if (V) Log.v(TAG, "originalUri: " + uri);
    319         return uri;
    320     }
    321 
    322     static Uri generateUri(Uri uri, BluetoothOppSendFileInfo sendFileInfo) {
    323         String fileInfo = sendFileInfo.toString();
    324         int atIndex = fileInfo.lastIndexOf("@");
    325         fileInfo = fileInfo.substring(atIndex);
    326         uri = Uri.parse(uri + fileInfo);
    327         if (V) Log.v(TAG, "generateUri: " + uri);
    328         return uri;
    329     }
    330 
    331     static void putSendFileInfo(Uri uri, BluetoothOppSendFileInfo sendFileInfo) {
    332         if (D) Log.d(TAG, "putSendFileInfo: uri=" + uri + " sendFileInfo=" + sendFileInfo);
    333         sSendFileMap.put(uri, sendFileInfo);
    334     }
    335 
    336     static BluetoothOppSendFileInfo getSendFileInfo(Uri uri) {
    337         if (D) Log.d(TAG, "getSendFileInfo: uri=" + uri);
    338         BluetoothOppSendFileInfo info = sSendFileMap.get(uri);
    339         return (info != null) ? info : BluetoothOppSendFileInfo.SEND_FILE_INFO_ERROR;
    340     }
    341 
    342     static void closeSendFileInfo(Uri uri) {
    343         if (D) Log.d(TAG, "closeSendFileInfo: uri=" + uri);
    344         BluetoothOppSendFileInfo info = sSendFileMap.remove(uri);
    345         if (info != null && info.mInputStream != null) {
    346             try {
    347                 info.mInputStream.close();
    348             } catch (IOException ignored) {
    349             }
    350         }
    351     }
    352 }
    353