From: Lixin Yue Date: Wed, 23 Dec 2009 07:37:27 +0000 (+0800) Subject: Move insert share info operation to a thread to avoid Receiver timeout X-Git-Tag: android-7.1.2_r17~1442 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=c6f1bacf1d4c39d7efd5dc98004d936c18ed51f1;p=android-x86%2Fpackages-apps-Bluetooth.git Move insert share info operation to a thread to avoid Receiver timeout In OppReceiver, the function mOppManager.startTransfer(remoteDevice), which is used to insert share records to DB, can be really a time-consuming operation in case of 300 or more objects sharing case. For onReceive() function in BroadcastReceiver, there are below specification: The function is normally called within the main thread of its process, so you should never perform long-running operations in it (there is a timeout of 10 seconds that the system allows before considering the receiver to be blocked and a candidate to be killed) Add handle to concurrent sending case In Gellary, user can multiple select files (say 50files), and share via bluetooth to device1, and then right away share via bluetooth to device2, and also can share to device3. In such extream case, we need ensure all share info are saved to db. Complete the TODO in OppManager Store application data to SharedPreferences and restore them when service restart. --- diff --git a/res/values/strings.xml b/res/values/strings.xml index 1c8c498b..7a80cdb2 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -173,6 +173,7 @@ Locked memory card. You must unlock the memory card to save files. There is a file being transferred to a different device. Please wait\u2026 Bluetooth is currently busy. Consider turning off another Bluetooth feature and trying again. + Too many requests are being processed. Try again later. File transfer not started yet diff --git a/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java b/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java index d0f9ff30..413f8968 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java +++ b/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java @@ -68,7 +68,7 @@ public class BluetoothOppLauncherActivity extends Activity { * an EXTRA_STREAM with the data to attach. */ if (action.equals(Intent.ACTION_SEND)) { - // TODO(Moto): handle type == null case + // TODO: handle type == null case String type = intent.getType(); Uri stream = (Uri)intent.getParcelableExtra(Intent.EXTRA_STREAM); if (stream != null && type != null) { @@ -109,7 +109,7 @@ public class BluetoothOppLauncherActivity extends Activity { return; } - // TODO(Moto): In the future, we may send intent to DevicePickerActivity + // TODO: In the future, we may send intent to DevicePickerActivity // directly, // and let DevicePickerActivity to handle Bluetooth Enable. if (!BluetoothOppManager.getInstance(this).isEnabled()) { @@ -120,7 +120,7 @@ public class BluetoothOppLauncherActivity extends Activity { } else { if (V) Log.v(TAG, "BT already enabled!! "); Intent in1 = new Intent(BluetoothDevicePicker.ACTION_LAUNCH); - in1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + in1.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); in1.putExtra(BluetoothDevicePicker.EXTRA_NEED_AUTH, false); in1.putExtra(BluetoothDevicePicker.EXTRA_FILTER_TYPE, BluetoothDevicePicker.FILTER_TYPE_TRANSFER); diff --git a/src/com/android/bluetooth/opp/BluetoothOppManager.java b/src/com/android/bluetooth/opp/BluetoothOppManager.java index 75e9fd94..2dd0acb8 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppManager.java +++ b/src/com/android/bluetooth/opp/BluetoothOppManager.java @@ -39,8 +39,11 @@ import android.bluetooth.BluetoothDevice; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; +import android.os.Process; +import android.text.TextUtils; import android.util.Log; import java.util.ArrayList; @@ -74,8 +77,6 @@ public class BluetoothOppManager { private ArrayList mUrisOfSendingFiles; - private boolean mCanStartTransfer = false; - private static final String OPP_PREFERENCE_FILE = "OPPMGR"; private static final String SENDING_FLAG = "SENDINGFLAG"; @@ -90,7 +91,9 @@ public class BluetoothOppManager { private static final String MULTIPLE_FLAG = "MULTIPLE_FLAG"; - private static final String ARRAYLIST_ITEM_SEPERATOR = "!"; + private static final String ARRAYLIST_ITEM_SEPERATOR = ";"; + + private static final int ALLOWED_INSERT_SHARE_THREAD_NUMBER = 3; // used to judge if need continue sending process after received a // ENABLED_ACTION @@ -98,7 +101,9 @@ public class BluetoothOppManager { public boolean mMultipleFlag; - public int mfileNumInBatch; + private int mfileNumInBatch; + + private int mInsertShareThreadNum = 0; /** * Get singleton instance. @@ -142,6 +147,7 @@ public class BluetoothOppManager { private void restoreApplicationData() { SharedPreferences settings = mContext.getSharedPreferences(OPP_PREFERENCE_FILE, 0); + // All member vars are not initialized till now mSendingFlag = settings.getBoolean(SENDING_FLAG, false); mMimeTypeOfSendigFile = settings.getString(MIME_TYPE, null); mUriOfSendingFile = settings.getString(FILE_URI, null); @@ -152,57 +158,67 @@ public class BluetoothOppManager { + mMimeTypeOfSendigFile + mUriOfSendingFile); String strUris = settings.getString(FILE_URIS, null); - // TODO(Moto): restore mUrisOfSendingFiles from strUris. + mUrisOfSendingFiles = new ArrayList(); + if (strUris != null) { + String[] splitUri = strUris.split(ARRAYLIST_ITEM_SEPERATOR); + for (int i = 0; i < splitUri.length; i++) { + mUrisOfSendingFiles.add(Uri.parse(splitUri[i])); + if (V) Log.v(TAG, "Uri in batch: " + Uri.parse(splitUri[i])); + } + } + + mContext.getSharedPreferences(OPP_PREFERENCE_FILE, 0).edit().clear().commit(); } /** - * Save application data to preference, need restore these data later + * Save application data to preference, need restore these data when service restart */ - private void onDestroy() { + private void storeApplicationData() { SharedPreferences.Editor editor = mContext.getSharedPreferences(OPP_PREFERENCE_FILE, 0) .edit(); editor.putBoolean(SENDING_FLAG, mSendingFlag).commit(); - editor.putString(MIME_TYPE, mMimeTypeOfSendigFile).commit(); - editor.putString(FILE_URI, mUriOfSendingFile).commit(); - editor.putString(MIME_TYPE_MULTIPLE, mMimeTypeOfSendigFiles).commit(); editor.putBoolean(MULTIPLE_FLAG, mMultipleFlag).commit(); - String strUris; - StringBuilder sb = new StringBuilder(); - for (int i = 0, count = mUrisOfSendingFiles.size(); i < count; i++) { - Uri uriContent = mUrisOfSendingFiles.get(i); - sb.append(uriContent); - sb.append(ARRAYLIST_ITEM_SEPERATOR); - } - strUris = sb.toString(); - editor.putString(FILE_URIS, strUris).commit(); - if (V) Log.v(TAG, "finalize is called and application data saved by SharedPreference! "); - } + if (mMultipleFlag) { + editor.putString(MIME_TYPE_MULTIPLE, mMimeTypeOfSendigFiles).commit(); + StringBuilder sb = new StringBuilder(); + for (int i = 0, count = mUrisOfSendingFiles.size(); i < count; i++) { + Uri uriContent = mUrisOfSendingFiles.get(i); + sb.append(uriContent); + sb.append(ARRAYLIST_ITEM_SEPERATOR); + } + String strUris = sb.toString(); + editor.putString(FILE_URIS, strUris).commit(); - /** - * Save data to preference when this class is destroyed by system due to - * memory lack - */ - protected void finalize() throws Throwable { - try { - onDestroy(); - } finally { - super.finalize(); + editor.remove(MIME_TYPE).commit(); + editor.remove(FILE_URI).commit(); + } else { + editor.putString(MIME_TYPE, mMimeTypeOfSendigFile).commit(); + editor.putString(FILE_URI, mUriOfSendingFile).commit(); + + editor.remove(MIME_TYPE_MULTIPLE).commit(); + editor.remove(FILE_URIS).commit(); } + if (V) Log.v(TAG, "Application data stored to SharedPreference! "); } public void saveSendingFileInfo(String mimeType, String uri) { - mMultipleFlag = false; - mMimeTypeOfSendigFile = mimeType; - mUriOfSendingFile = uri; - mCanStartTransfer = true; + synchronized (BluetoothOppManager.this) { + mMultipleFlag = false; + mMimeTypeOfSendigFile = mimeType; + mUriOfSendingFile = uri; + storeApplicationData(); + } } public void saveSendingFileInfo(String mimeType, ArrayList uris) { - mMultipleFlag = true; - mMimeTypeOfSendigFiles = mimeType; - mUrisOfSendingFiles = uris; - mCanStartTransfer = true; + synchronized (BluetoothOppManager.this) { + mMultipleFlag = true; + mMimeTypeOfSendigFiles = mimeType; + mUrisOfSendingFiles = uris; + storeApplicationData(); + } } + /** * Get the current status of Bluetooth hardware. * @return true if Bluetooth enabled, false otherwise. @@ -253,59 +269,138 @@ public class BluetoothOppManager { return deviceName; } + public int getBatchSize() { + synchronized (BluetoothOppManager.this) { + return mfileNumInBatch; + } + } + /** - * insert sending sessions to db, only used by Opp application. + * Fork a thread to insert share info to db. */ public void startTransfer(BluetoothDevice device) { - if (device == null) { - Log.e(TAG, "Target bt device is null!"); - return; + if (V) Log.v(TAG, "Active InsertShareThread number is : " + mInsertShareThreadNum); + InsertShareInfoThread insertThread; + synchronized (BluetoothOppManager.this) { + if (mInsertShareThreadNum > ALLOWED_INSERT_SHARE_THREAD_NUMBER) { + Log.e(TAG, "Too many shares user triggered concurrently!"); + + // Notice user + Intent in = new Intent(mContext, BluetoothOppBtErrorActivity.class); + in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + in.putExtra("title", mContext.getString(R.string.enabling_progress_title)); + in.putExtra("content", mContext.getString(R.string.ErrorTooManyRequests)); + mContext.startActivity(in); + + return; + } + insertThread = new InsertShareInfoThread(device, mMultipleFlag, mMimeTypeOfSendigFile, + mUriOfSendingFile, mMimeTypeOfSendigFiles, mUrisOfSendingFiles); + if (mMultipleFlag) { + mfileNumInBatch = mUrisOfSendingFiles.size(); + } } - if (!mCanStartTransfer) { - if (V) Log.v(TAG, "No transfer info restored: fileType&fileName"); - return; + insertThread.start(); + } + + /** + * Thread to insert share info to db. In multiple files (say 100 files) + * share case, the inserting share info to db operation would be a time + * consuming operation, so need a thread to handle it. This thread allows + * multiple instances to support below case: User select multiple files to + * share to one device (say device 1), and then right away share to second + * device (device 2), we need insert all these share info to db. + */ + private class InsertShareInfoThread extends Thread { + private final BluetoothDevice mRemoteDevice; + + private final String mTypeOfSingleFile; + + private final String mUri; + + private final String mTypeOfMultipleFiles; + + private final ArrayList mUris; + + private final boolean mIsMultiple; + + public InsertShareInfoThread(BluetoothDevice device, boolean multiple, + String typeOfSingleFile, String uri, String typeOfMultipleFiles, ArrayList uris) { + super("Insert ShareInfo Thread"); + this.mRemoteDevice = device; + this.mIsMultiple = multiple; + this.mTypeOfSingleFile = typeOfSingleFile; + this.mUri = uri; + this.mTypeOfMultipleFiles = typeOfMultipleFiles; + this.mUris = uris; + + synchronized (BluetoothOppManager.this) { + mInsertShareThreadNum++; + } + + if (V) Log.v(TAG, "Thread id is: " + this.getId()); } - if (mMultipleFlag == true) { - int count = mUrisOfSendingFiles.size(); - mfileNumInBatch = count; + @Override + public void run() { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + if (mRemoteDevice == null) { + Log.e(TAG, "Target bt device is null!"); + return; + } + if (mIsMultiple) { + insertMultipleShare(); + } else { + insertSingleShare(); + } + synchronized (BluetoothOppManager.this) { + mInsertShareThreadNum--; + } + } + /** + * Insert multiple sending sessions to db, only used by Opp application. + */ + private void insertMultipleShare() { + int count = mUris.size(); Long ts = System.currentTimeMillis(); for (int i = 0; i < count; i++) { - Uri fileUri = mUrisOfSendingFiles.get(i); + Uri fileUri = mUris.get(i); ContentResolver contentResolver = mContext.getContentResolver(); String contentType = contentResolver.getType(fileUri); if (V) Log.v(TAG, "Got mimetype: " + contentType + " Got uri: " + fileUri); + if (TextUtils.isEmpty(contentType)) { + contentType = mTypeOfMultipleFiles; + } ContentValues values = new ContentValues(); values.put(BluetoothShare.URI, fileUri.toString()); values.put(BluetoothShare.MIMETYPE, contentType); - values.put(BluetoothShare.DESTINATION, device.getAddress()); + values.put(BluetoothShare.DESTINATION, mRemoteDevice.getAddress()); values.put(BluetoothShare.TIMESTAMP, ts); final Uri contentUri = mContext.getContentResolver().insert( BluetoothShare.CONTENT_URI, values); if (V) Log.v(TAG, "Insert contentUri: " + contentUri + " to device: " - + getDeviceName(device)); + + getDeviceName(mRemoteDevice)); } - } else { + } + + /** + * Insert single sending session to db, only used by Opp application. + */ + private void insertSingleShare() { ContentValues values = new ContentValues(); - values.put(BluetoothShare.URI, mUriOfSendingFile); - values.put(BluetoothShare.MIMETYPE, mMimeTypeOfSendigFile); - values.put(BluetoothShare.DESTINATION, device.getAddress()); + values.put(BluetoothShare.URI, mUri); + values.put(BluetoothShare.MIMETYPE, mTypeOfSingleFile); + values.put(BluetoothShare.DESTINATION, mRemoteDevice.getAddress()); final Uri contentUri = mContext.getContentResolver().insert(BluetoothShare.CONTENT_URI, values); if (V) Log.v(TAG, "Insert contentUri: " + contentUri + " to device: " - + getDeviceName(device)); + + getDeviceName(mRemoteDevice)); } - - // reset vars - mMimeTypeOfSendigFile = null; - mUriOfSendingFile = null; - mUrisOfSendingFiles = null; - mMultipleFlag = false; - mCanStartTransfer = false; } + } diff --git a/src/com/android/bluetooth/opp/BluetoothOppNotification.java b/src/com/android/bluetooth/opp/BluetoothOppNotification.java index 345ec89a..0076c8e0 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppNotification.java +++ b/src/com/android/bluetooth/opp/BluetoothOppNotification.java @@ -114,7 +114,7 @@ class BluetoothOppNotification { } public void finishNotification() { - synchronized (this) { + synchronized (BluetoothOppNotification.this) { mFinised = true; } } @@ -123,7 +123,7 @@ class BluetoothOppNotification { * Update the notification ui. */ public void updateNotification() { - synchronized (this) { + synchronized (BluetoothOppNotification.this) { mPendingUpdate = true; if (mUpdateNotificationThread == null) { mUpdateNotificationThread = new NotificationUpdateThread(); @@ -143,7 +143,7 @@ class BluetoothOppNotification { public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); for (;;) { - synchronized (this) { + synchronized (BluetoothOppNotification.this) { if (mUpdateNotificationThread != this) { throw new IllegalStateException( "multiple UpdateThreads in BluetoothOppNotification"); diff --git a/src/com/android/bluetooth/opp/BluetoothOppReceiver.java b/src/com/android/bluetooth/opp/BluetoothOppReceiver.java index 7ce5cbc8..a0735d0c 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppReceiver.java +++ b/src/com/android/bluetooth/opp/BluetoothOppReceiver.java @@ -104,9 +104,10 @@ public class BluetoothOppReceiver extends BroadcastReceiver { // Display toast message String deviceName = mOppManager.getDeviceName(remoteDevice); String toastMsg; + int batchSize = mOppManager.getBatchSize(); if (mOppManager.mMultipleFlag) { - toastMsg = context.getString(R.string.bt_toast_5, Integer - .toString(mOppManager.mfileNumInBatch), deviceName); + toastMsg = context.getString(R.string.bt_toast_5, Integer.toString(batchSize), + deviceName); } else { toastMsg = context.getString(R.string.bt_toast_4, deviceName); }