summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonathan Bensen <cybertronic@gmail.com>2011-12-04 19:31:49 -0800
committerEmilio López <turl@tuxfamily.org>2011-12-05 04:37:39 +0000
commitf6e080b7d269bd6dfbca36134d1c2d12cf8177ac (patch)
tree72bc0a02fe031c6869faa49568fae197d498f31c
parentb6cf743fe3994eda247c42d933c4256d044e6923 (diff)
downloadandroid_packages_apps_Bluetooth-f6e080b7d269bd6dfbca36134d1c2d12cf8177ac.tar.gz
android_packages_apps_Bluetooth-f6e080b7d269bd6dfbca36134d1c2d12cf8177ac.tar.bz2
android_packages_apps_Bluetooth-f6e080b7d269bd6dfbca36134d1c2d12cf8177ac.zip
Bluetooth: Add Map profile and features (#3875)
Change-Id: Ibe0595b99ce1634aaec47effe4874cda72df302e
-rw-r--r--AndroidManifest.xml27
-rw-r--r--src/com/android/bluetooth/map/BluetoothMapAuthenticator.java102
-rw-r--r--src/com/android/bluetooth/map/BluetoothMapRfcommTransport.java87
-rw-r--r--src/com/android/bluetooth/map/BluetoothMasActivity.java291
-rw-r--r--src/com/android/bluetooth/map/BluetoothMasAppIf.java3396
-rw-r--r--src/com/android/bluetooth/map/BluetoothMasAppParams.java54
-rw-r--r--src/com/android/bluetooth/map/BluetoothMasMsg.java38
-rw-r--r--src/com/android/bluetooth/map/BluetoothMasObexServer.java1122
-rw-r--r--src/com/android/bluetooth/map/BluetoothMasReceiver.java87
-rw-r--r--src/com/android/bluetooth/map/BluetoothMasService.java629
-rw-r--r--src/com/android/bluetooth/map/BluetoothMasSpecParams.java132
-rw-r--r--src/com/android/bluetooth/map/BluetoothMasTestActivity.java291
-rw-r--r--src/com/android/bluetooth/map/BluetoothMns.java3477
-rw-r--r--src/com/android/bluetooth/map/BluetoothMnsObexSession.java286
-rw-r--r--src/com/android/bluetooth/map/BluetoothMnsPreference.java166
-rw-r--r--src/com/android/bluetooth/map/BluetoothMnsRfcommTransport.java94
-rw-r--r--src/com/android/bluetooth/map/MapUtils/BmessageConsts.java210
-rw-r--r--src/com/android/bluetooth/map/MapUtils/CommonUtils.java83
-rw-r--r--src/com/android/bluetooth/map/MapUtils/EmailUtils.java561
-rw-r--r--src/com/android/bluetooth/map/MapUtils/MapUtils.java1623
-rw-r--r--src/com/android/bluetooth/map/MapUtils/MapUtilsConsts.java41
-rw-r--r--src/com/android/bluetooth/map/MapUtils/MsgListingConsts.java212
-rw-r--r--src/com/android/bluetooth/map/MapUtils/SmsMmsUtils.java187
-rw-r--r--src/com/android/bluetooth/map/MapUtils/SortMsgListByDate.java41
24 files changed, 13237 insertions, 0 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a96b8ee23..b6219f965 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,6 +20,14 @@
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <uses-permission android:name="android.permission.READ_SMS"></uses-permission>
+ <uses-permission android:name="android.permission.WRITE_SMS"></uses-permission>
+ <uses-permission android:name="android.permission.BROADCAST_SMS"></uses-permission>
+ <uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
+ <uses-permission android:name="android.permission.SEND_SMS"></uses-permission>
+ <uses-permission android:name="com.android.email.permission.ACCESS_PROVIDER"></uses-permission>
+ <uses-permission android:name="com.android.email.permission.READ_ATTACHMENT"></uses-permission>
+
<application
android:icon="@drawable/bt_share"
android:label="@string/app_name">
@@ -137,5 +145,24 @@
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED"/>
</intent-filter>
</receiver>
+ <activity android:name=".map.BluetoothMasActivity"
+ android:process="@string/process"
+ android:excludeFromRecents="true"
+ android:theme="@*android:style/Theme.Dialog.Alert">
+ <intent-filter>
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ <service
+ android:process="@string/process"
+ android:name=".map.BluetoothMasService" >
+ </service>
+ <receiver
+ android:process="@string/process"
+ android:name=".map.BluetoothMasReceiver">
+ <intent-filter>
+ <action android:name="android.bluetooth.adapter.action.STATE_CHANGED"/>
+ </intent-filter>
+ </receiver>
</application>
</manifest>
diff --git a/src/com/android/bluetooth/map/BluetoothMapAuthenticator.java b/src/com/android/bluetooth/map/BluetoothMapAuthenticator.java
new file mode 100644
index 000000000..64e0a9277
--- /dev/null
+++ b/src/com/android/bluetooth/map/BluetoothMapAuthenticator.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map;
+
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import javax.obex.*;
+
+/**
+ * BluetoothMapAuthenticator is a used by BluetoothObexServer for obex
+ * authentication procedure.
+ */
+public class BluetoothMapAuthenticator implements Authenticator {
+ private static final String TAG = "BluetoothMapAuthenticator";
+
+ private boolean mChallenged;
+
+ private boolean mAuthCancelled;
+
+ private String mSessionKey;
+
+ private Handler mCallback;
+
+ public BluetoothMapAuthenticator(final Handler callback) {
+ mCallback = callback;
+ mChallenged = false;
+ mAuthCancelled = false;
+ mSessionKey = null;
+ }
+
+ public final synchronized void setChallenged(final boolean bool) {
+ mChallenged = bool;
+ }
+
+ public final synchronized void setCancelled(final boolean bool) {
+ mAuthCancelled = bool;
+ }
+
+ public final synchronized void setSessionKey(final String string) {
+ mSessionKey = string;
+ }
+
+ private void waitUserConfirmation() {
+ Message msg = Message.obtain(mCallback);
+ msg.what = BluetoothMasService.MSG_OBEX_AUTH_CHALL;
+ msg.sendToTarget();
+ synchronized (this) {
+ while (!mChallenged && !mAuthCancelled) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted while waiting on isChalled");
+ }
+ }
+ }
+ }
+
+ public PasswordAuthentication onAuthenticationChallenge(final String description,
+ final boolean isUserIdRequired, final boolean isFullAccess) {
+ waitUserConfirmation();
+ if (mSessionKey.trim().length() != 0) {
+ PasswordAuthentication pa = new PasswordAuthentication(null, mSessionKey.getBytes());
+ return pa;
+ }
+ return null;
+ }
+
+ // TODO: Reserved for future use only, in case PSE challenge PCE
+ public byte[] onAuthenticationResponse(final byte[] userName) {
+ byte[] b = null;
+ return b;
+ }
+}
+
diff --git a/src/com/android/bluetooth/map/BluetoothMapRfcommTransport.java b/src/com/android/bluetooth/map/BluetoothMapRfcommTransport.java
new file mode 100644
index 000000000..acef3c57d
--- /dev/null
+++ b/src/com/android/bluetooth/map/BluetoothMapRfcommTransport.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+
+import javax.obex.*;
+
+import android.bluetooth.BluetoothSocket;
+
+public class BluetoothMapRfcommTransport implements ObexTransport {
+ private BluetoothSocket mSocket = null;
+
+ public BluetoothMapRfcommTransport(BluetoothSocket rfs) {
+ super();
+ this.mSocket = rfs;
+ }
+
+ public void close() throws IOException {
+ mSocket.close();
+ }
+
+ public DataInputStream openDataInputStream() throws IOException {
+ return new DataInputStream(openInputStream());
+ }
+
+ public DataOutputStream openDataOutputStream() throws IOException {
+ return new DataOutputStream(openOutputStream());
+ }
+
+ public InputStream openInputStream() throws IOException {
+ return mSocket.getInputStream();
+ }
+
+ public OutputStream openOutputStream() throws IOException {
+ return mSocket.getOutputStream();
+ }
+
+ public void connect() throws IOException {
+ }
+
+ public void create() throws IOException {
+ }
+
+ public void disconnect() throws IOException {
+ }
+
+ public void listen() throws IOException {
+ }
+
+ public boolean isConnected() throws IOException {
+ return true;
+ }
+
+}
+
diff --git a/src/com/android/bluetooth/map/BluetoothMasActivity.java b/src/com/android/bluetooth/map/BluetoothMasActivity.java
new file mode 100644
index 000000000..78e8ebb60
--- /dev/null
+++ b/src/com/android/bluetooth/map/BluetoothMasActivity.java
@@ -0,0 +1,291 @@
+ /*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map;
+
+import com.android.bluetooth.R;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.Preference;
+import android.text.InputFilter;
+import android.text.TextWatcher;
+import android.text.InputFilter.LengthFilter;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.Toast;
+
+
+/**
+ * MapActivity shows two dialogues: One for accepting incoming ftp request and
+ * the other prompts the user to enter a session key for authentication with a
+ * remote Bluetooth device.
+ */
+public class BluetoothMasActivity extends Activity implements
+ DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener, TextWatcher {
+ private static final String TAG = "BluetoothMasActivity";
+
+ private static final boolean V = BluetoothMasService.VERBOSE;
+
+ private static final int BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH = 16;
+
+ private static final int DIALOG_YES_NO_CONNECT = 1;
+
+ private static final int DIALOG_YES_NO_AUTH = 2;
+
+ private static final String KEY_USER_TIMEOUT = "user_timeout";
+
+ private View mView;
+
+ private EditText mKeyView;
+
+ private TextView messageView;
+
+ private String mSessionKey = "";
+
+ private int mCurrentDialog;
+
+ private Button mOkButton;
+
+ private CheckBox mAlwaysAllowed;
+
+ private boolean mTimeout = false;
+
+ private boolean mAlwaysAllowedValue = true;
+
+ private static final int DISMISS_TIMEOUT_DIALOG = 0;
+
+ private static final int DISMISS_TIMEOUT_DIALOG_VALUE = 2000;
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!BluetoothMasService.USER_CONFIRM_TIMEOUT_ACTION.equals(intent.getAction())) {
+ return;
+ }
+ onTimeout();
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent i = getIntent();
+ String action = i.getAction();
+ if (action.equals(BluetoothMasService.ACCESS_REQUEST_ACTION)) {
+ showMapDialog(DIALOG_YES_NO_CONNECT);
+ mCurrentDialog = DIALOG_YES_NO_CONNECT;
+ } else if (action.equals(BluetoothMasService.AUTH_CHALL_ACTION)) {
+ showMapDialog(DIALOG_YES_NO_AUTH);
+ mCurrentDialog = DIALOG_YES_NO_AUTH;
+ }
+ else {
+ Log.e(TAG, "Error: this activity may be started only with intent "
+ + "MAP_ACCESS_REQUEST");
+ finish();
+ }
+ registerReceiver(mReceiver, new IntentFilter(
+ BluetoothMasService.USER_CONFIRM_TIMEOUT_ACTION));
+ }
+
+ private void showMapDialog(int id) {
+ }
+
+ private String createDisplayText(final int id) {
+ String mRemoteName = BluetoothMasService.getRemoteDeviceName();
+ return null;
+ }
+
+ private View createView(final int id) {
+ switch (id) {
+ case DIALOG_YES_NO_CONNECT:
+ mView = getLayoutInflater().inflate(R.layout.access, null);
+ messageView = (TextView)mView.findViewById(R.id.message);
+ messageView.setText(createDisplayText(id));
+ mAlwaysAllowed = (CheckBox)mView.findViewById(R.id.alwaysallowed);
+ mAlwaysAllowed.setChecked(true);
+ mAlwaysAllowed.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (isChecked) {
+ mAlwaysAllowedValue = true;
+ } else {
+ mAlwaysAllowedValue = false;
+ }
+ }
+ });
+ return mView;
+ case DIALOG_YES_NO_AUTH:
+ mView = getLayoutInflater().inflate(R.layout.auth, null);
+ messageView = (TextView)mView.findViewById(R.id.message);
+ messageView.setText(createDisplayText(id));
+ mKeyView = (EditText)mView.findViewById(R.id.text);
+ mKeyView.addTextChangedListener(this);
+ mKeyView.setFilters(new InputFilter[] {
+ new LengthFilter(BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH)
+ });
+ return mView;
+ default:
+ return null;
+ }
+ }
+
+ private void onPositive() {
+ if (!mTimeout) {
+ if (mCurrentDialog == DIALOG_YES_NO_CONNECT) {
+ sendIntentToReceiver(BluetoothMasService.ACCESS_ALLOWED_ACTION,
+ BluetoothMasService.EXTRA_ALWAYS_ALLOWED, mAlwaysAllowedValue);
+ } else if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
+ sendIntentToReceiver(BluetoothMasService.AUTH_RESPONSE_ACTION,
+ BluetoothMasService.EXTRA_SESSION_KEY, mSessionKey);
+ mKeyView.removeTextChangedListener(this);
+ }
+ }
+ mTimeout = false;
+ finish();
+ }
+
+ private void onNegative() {
+ if (mCurrentDialog == DIALOG_YES_NO_CONNECT) {
+ sendIntentToReceiver(BluetoothMasService.ACCESS_DISALLOWED_ACTION, null, null);
+ } else if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
+ sendIntentToReceiver(BluetoothMasService.AUTH_CANCELLED_ACTION, null, null);
+ mKeyView.removeTextChangedListener(this);
+ }
+ finish();
+ }
+
+ private void sendIntentToReceiver(final String intentName, final String extraName,
+ final String extraValue) {
+ Intent intent = new Intent(intentName);
+ intent.setClassName(BluetoothMasService.THIS_PACKAGE_NAME, BluetoothMasReceiver.class
+ .getName());
+ if (extraName != null) {
+ intent.putExtra(extraName, extraValue);
+ }
+ sendBroadcast(intent);
+ }
+
+ private void sendIntentToReceiver(final String intentName, final String extraName,
+ final boolean extraValue) {
+ Intent intent = new Intent(intentName);
+ intent.setClassName(BluetoothMasService.THIS_PACKAGE_NAME, BluetoothMasReceiver.class
+ .getName());
+ if (extraName != null) {
+ intent.putExtra(extraName, extraValue);
+ }
+ sendBroadcast(intent);
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
+ mSessionKey = mKeyView.getText().toString();
+ }
+ onPositive();
+ break;
+
+ case DialogInterface.BUTTON_NEGATIVE:
+ onNegative();
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void onTimeout() {
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ mTimeout = savedInstanceState.getBoolean(KEY_USER_TIMEOUT);
+ if (V) Log.e(TAG, "onRestoreInstanceState() mTimeout: " + mTimeout);
+
+ if (mTimeout) {
+ onTimeout();
+ }
+
+
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_USER_TIMEOUT, mTimeout);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(mReceiver);
+ }
+
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ return true;
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int before, int after) {
+ }
+
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ public void afterTextChanged(android.text.Editable s) {
+ if (s.length() > 0) {
+ mOkButton.setEnabled(true);
+ }
+ }
+ private final Handler mTimeoutHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case DISMISS_TIMEOUT_DIALOG:
+ if (V) Log.e(TAG, "Received DISMISS_TIMEOUT_DIALOG msg.");
+ finish();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+}
+
diff --git a/src/com/android/bluetooth/map/BluetoothMasAppIf.java b/src/com/android/bluetooth/map/BluetoothMasAppIf.java
new file mode 100644
index 000000000..e95fad353
--- /dev/null
+++ b/src/com/android/bluetooth/map/BluetoothMasAppIf.java
@@ -0,0 +1,3396 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.bluetooth.map;
+
+import com.android.bluetooth.R;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.text.format.Time;
+import android.util.Log;
+import android.util.TimeFormatException;
+
+import com.android.bluetooth.map.MapUtils.BmessageConsts;
+import com.android.bluetooth.map.MapUtils.MapUtils;
+import com.android.bluetooth.map.MapUtils.MsgListingConsts;
+import com.android.bluetooth.map.MapUtils.SmsMmsUtils;
+import com.android.bluetooth.map.MapUtils.SortMsgListByDate;
+import com.android.bluetooth.map.MapUtils.EmailUtils;
+import com.android.bluetooth.map.MapUtils.CommonUtils;
+
+
+import javax.obex.*;
+
+/**
+ * This class provides the application interface for MAS Server It interacts
+ * with the SMS repository using Sms Content Provider to service the MAS
+ * requests. It also initializes BluetoothMns thread which is used for MNS
+ * connection.
+ */
+
+public class BluetoothMasAppIf {
+
+ public Context context;
+ public String mode;
+ public final String TAG = "BluetoothMasAppIf";
+
+ public static final int BIT_SUBJECT = 0x1;
+ public static final int BIT_DATETIME = 0x2;
+ public static final int BIT_SENDER_NAME = 0x4;
+ public static final int BIT_SENDER_ADDRESSING = 0x8;
+
+ public static final int BIT_RECIPIENT_NAME = 0x10;
+ public static final int BIT_RECIPIENT_ADDRESSING = 0x20;
+ public static final int BIT_TYPE = 0x40;
+ public static final int BIT_SIZE = 0x80;
+
+ public static final int BIT_RECEPTION_STATUS = 0x100;
+ public static final int BIT_TEXT = 0x200;
+ public static final int BIT_ATTACHMENT_SIZE = 0x400;
+ public static final int BIT_PRIORITY = 0x800;
+
+ public static final int BIT_READ = 0x1000;
+ public static final int BIT_SENT = 0x2000;
+ public static final int BIT_PROTECTED = 0x4000;
+ public static final int BIT_REPLYTO_ADDRESSING = 0x8000;
+
+ public static final int MMS_HDLR_CONSTANT = 100000;
+ public static final int EMAIL_HDLR_CONSTANT = 200000;
+
+ private final String RootPath = "root";
+
+ private String CurrentPath = null;
+
+ private final String Telecom = "telecom";
+ private final String Msg = "msg";
+
+ private final String Inbox = "inbox";
+ private final String Outbox = "outbox";
+ private final String Sent = "sent";
+ private final String Deleted = "deleted";
+ private final String Draft = "draft";
+ private final String Drafts = "drafts";
+ private final String Undelivered = "undelivered";
+ private final String Failed = "failed";
+ private final String Queued = "queued";
+
+ private final int DELETED_THREAD_ID = -1;
+
+ private final boolean mnsServiceEnabled = false;
+
+
+ // root -> telecom -> msg -> (FolderList) includes inbox, outbox, sent, etc
+ // For SMS/MMS FolderList[] = { Inbox, Outbox, Sent, Deleted, Draft };
+
+ //added email
+ List<String> folderList = new ArrayList<String>();
+ //end email
+
+ private final MapUtils mu;
+
+ private final BluetoothMns mnsClient;
+
+ public BluetoothMasAppIf(Context context, String mode) {
+ this.context = context;
+ this.mode = mode;
+ mu = new MapUtils();
+ mnsClient = new BluetoothMns(context);
+
+ // Clear out deleted items from database
+ clearDeletedItems();
+
+ SmsMmsUtils smu = new SmsMmsUtils();
+ folderList = smu.folderListSmsMms(folderList);
+
+ Log.d(TAG, "Constructor called");
+ }
+
+ /**
+ * Check the path to a given folder. If setPathFlag is set,
+ * set the path to the new value. Else, just check if the path
+ * exists and don't change the current path.
+ *
+ * @return true if the path exists, and could be accessed.
+ */
+ public boolean checkPath(boolean up, String name, boolean setPathFlag) {
+ Log.d(TAG, "setPath called");
+ /*
+ * /* Up and empty string – cd .. Up and name - cd ../name Down and name
+ * - cd name Down and empty string – cd to root
+ */
+
+ List<String> completeFolderList = new ArrayList<String>();
+ EmailUtils eu = new EmailUtils();
+
+ if(mode !=null && (mode.equalsIgnoreCase("SMS")
+ || mode.equalsIgnoreCase("SMS_MMS")
+ || mode.equalsIgnoreCase("SMS_MMS_EMAIL"))){
+ completeFolderList = folderList;
+ }
+ if(mode !=null && (mode.equalsIgnoreCase("SMS_MMS_EMAIL")
+ || mode.equalsIgnoreCase("EMAIL"))){
+ completeFolderList = eu.folderListEmail(folderList, context);
+ }
+
+ Log.d(TAG, "CurrentPath::"+CurrentPath);
+ Log.d(TAG, "name::"+name);
+ if ((up == false)) {
+ if(name == null) {
+ CurrentPath = (setPathFlag) ? null : CurrentPath;
+ return true;
+ } else if(name.equals("")){
+ CurrentPath = (setPathFlag) ? null : CurrentPath;
+ return true;
+ }
+ }
+
+ if (up == true) {
+ if (CurrentPath == null) {
+ // Can't go above root
+ return false;
+ } else {
+ int LastIndex = CurrentPath.lastIndexOf('/');
+ if (LastIndex < 0) {
+ // Reaches root
+ CurrentPath = null;
+ } else {
+ CurrentPath = CurrentPath.substring(0, LastIndex);
+ }
+ }
+ if (name == null) {
+ // Only going up by one
+ return true;
+ }
+ }
+
+ if (CurrentPath == null) {
+ if (name.equals(Telecom)) {
+ CurrentPath = (setPathFlag) ? Telecom : CurrentPath;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ String splitStrings[] = CurrentPath.split("/");
+
+ boolean Result = false;
+ switch (splitStrings.length) {
+ case 1:
+ if (name.equals(Msg)) {
+ CurrentPath += (setPathFlag) ? ("/" + name) : "";
+ Result = true;
+ }
+ break;
+ case 2:
+ for (String FolderName : completeFolderList) {
+ if(FolderName.equalsIgnoreCase(name)
+ || FolderName.toUpperCase().contains(name.toUpperCase())){
+ //added second condition for gmail sent folder
+ CurrentPath += (setPathFlag) ? ("/" + name) : "";
+ Result = true;
+ break;
+ }
+ }
+ break;
+ // TODO SUBFOLDERS: Add check for sub-folders (add more cases)
+
+ default:
+ Result = false;
+ break;
+ }
+ return Result;
+ }
+
+ /**
+ * Set the path to a given folder.
+ *
+ * @return true if the path exists, and could be accessed.
+ */
+ public boolean setPath(boolean up, String name) {
+ return checkPath(up, name, true);
+ }
+
+ /**
+ * Get the number of messages in the folder
+ *
+ * @return number of messages; -1 if error
+ */
+ public int folderListingSize() {
+ Log.d(TAG, "folderListingSize called, current path " + CurrentPath);
+
+ List<String> completeFolderList = new ArrayList<String>();
+ EmailUtils eu = new EmailUtils();
+
+ if(mode !=null && (mode.equalsIgnoreCase("SMS")
+ || mode.equalsIgnoreCase("SMS_MMS")
+ || mode.equalsIgnoreCase("SMS_MMS_EMAIL"))){
+ completeFolderList = folderList;
+ }
+ if(mode !=null && (mode.equalsIgnoreCase("SMS_MMS_EMAIL")
+ || mode.equalsIgnoreCase("EMAIL"))){
+ completeFolderList = eu.folderListEmail(folderList, context);
+ }
+
+ if (CurrentPath == null) {
+ // at root, only telecom folder should be present
+ return 1;
+ }
+
+ if (CurrentPath.equals(Telecom)) {
+ // at root -> telecom, only msg folder should be present
+ return 1;
+ }
+
+ if (CurrentPath.equals(Telecom + "/" + Msg)) {
+ // at root -> telecom -> msg, FolderList should be present
+ return completeFolderList.size();
+ }
+ // TODO SUBFOLDERS: Add check for sub-folders
+
+ return 0;
+ }
+
+ /**
+ * Get the XML listing of the folders at CurrenthPath
+ *
+ * @return XML listing of the folders
+ */
+ public String folderListing(BluetoothMasAppParams appParam) {
+ Log.d(TAG, "folderListing called, current path " + CurrentPath);
+
+ List<String> list = new ArrayList<String>();
+
+ List<String> completeFolderList = new ArrayList<String>();
+ EmailUtils eu = new EmailUtils();
+ if(mode !=null && (mode.equalsIgnoreCase("SMS")
+ || mode.equalsIgnoreCase("SMS_MMS")
+ || mode.equalsIgnoreCase("SMS_MMS_EMAIL"))){
+ completeFolderList = folderList;
+ }
+ if(mode !=null && (mode.equalsIgnoreCase("SMS_MMS_EMAIL")
+ || mode.equalsIgnoreCase("EMAIL"))){
+ completeFolderList = eu.folderListEmail(folderList, context);
+ }
+
+ if (CurrentPath == null) {
+ // at root, only telecom folder should be present
+ if (appParam.ListStartOffset == 0) {
+ list.add(Telecom);
+ }
+ return mu.folderListingXML(list);
+ }
+
+ if (CurrentPath.equals(Telecom)) {
+ // at root -> telecom, only msg folder should be present
+ if (appParam.ListStartOffset == 0) {
+ list.add(Msg);
+ }
+ return mu.folderListingXML(list);
+ }
+
+ if (CurrentPath.equals(Telecom + "/" + Msg)) {
+ int offset = 0;
+ int added = 0;
+ // at root -> telecom -> msg, FolderList should be present
+ for (String Folder : completeFolderList) {
+ offset++;
+ if ((offset > appParam.ListStartOffset)
+ && (added < appParam.MaxListCount)) {
+ list.add(Folder);
+ added++;
+ }
+ }
+ return mu.folderListingXML(list);
+ }
+
+ if (CurrentPath.equals(Telecom + "/" + Msg + "/" + Inbox) ||
+ CurrentPath.equals(Telecom + "/" + Msg + "/" + Outbox) ||
+ CurrentPath.equals(Telecom + "/" + Msg + "/" + Draft) ||
+ CurrentPath.equals(Telecom + "/" + Msg + "/" + Deleted) ||
+ CurrentPath.equals(Telecom + "/" + Msg + "/" + Sent)
+ ) {
+ return mu.folderListingXML(list);
+ }
+
+ return null;
+ }
+
+ /**
+ * Append child folder to the CurrentPath
+ */
+ private String getFullPath(String child) {
+
+ String tempPath = null;
+ List<String> completeFolderList = new ArrayList<String>();
+ EmailUtils eu = new EmailUtils();
+ completeFolderList = eu.folderListEmail(folderList, context);
+
+ if (child != null) {
+ if (CurrentPath == null) {
+ if (child.equals("telecom")) {
+ // Telecom is fine
+ tempPath = new String("telecom");
+ }
+ } else if (CurrentPath.equals("telecom")) {
+ if (child.equals("msg")) {
+ tempPath = CurrentPath + "/" + child;
+ }
+ } else if (CurrentPath.equals("telecom/msg")) {
+ for (String Folder : completeFolderList) { //TODO NEED TO LOOK INTO THIS
+ if (child.equalsIgnoreCase(Folder)) {
+ tempPath = CurrentPath + "/" + Folder;
+ }
+ }
+ }
+ }
+ return tempPath;
+ }
+
+ public class BluetoothMasMessageListingRsp {
+ public File file = null;
+ public int msgListingSize = 0;
+ public byte newMessage = 0;
+ public int rsp = ResponseCodes.OBEX_HTTP_OK;
+ }
+
+ private class VcardContent {
+ public String name = "";
+ public String tel = "";
+ public String email = "";
+ }
+
+ static final int PHONELOOKUP_ID_COLUMN_INDEX = 0;
+ static final int PHONELOOKUP_LOOKUP_KEY_COLUMN_INDEX = 1;
+ static final int PHONELOOKUP_DISPLAY_NAME_COLUMN_INDEX = 2;
+
+ static final int EMAIL_DATA_COLUMN_INDEX = 0;
+
+ private List<VcardContent> list;
+
+ private VcardContent getVcardContent(String phoneAddress) {
+
+ VcardContent vCard = new VcardContent();
+ vCard.tel = phoneAddress;
+
+ Uri uriContacts = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
+ Uri.encode(phoneAddress));
+ Cursor cursorContacts = context.getContentResolver().query(
+ uriContacts,
+ new String[] { PhoneLookup._ID, PhoneLookup.LOOKUP_KEY,
+ PhoneLookup.DISPLAY_NAME }, null, null, null);
+
+ cursorContacts.moveToFirst();
+
+ if (cursorContacts.getCount() > 0) {
+ long contactId = cursorContacts
+ .getLong(PHONELOOKUP_ID_COLUMN_INDEX);
+ String lookupKey = cursorContacts
+ .getString(PHONELOOKUP_LOOKUP_KEY_COLUMN_INDEX);
+
+ Uri lookUpUri = Contacts.getLookupUri(contactId, lookupKey);
+ String Id = lookUpUri.getLastPathSegment();
+
+ Cursor crEm = context.getContentResolver().query(Email.CONTENT_URI,
+ new String[] { Email.DATA }, Email.CONTACT_ID + "=?",
+ new String[] { Id }, null);
+ crEm.moveToFirst();
+
+ vCard.name = cursorContacts
+ .getString(PHONELOOKUP_DISPLAY_NAME_COLUMN_INDEX);
+
+ vCard.email = "";
+ if (crEm.moveToFirst()) {
+ do {
+ vCard.email += crEm.getString(EMAIL_DATA_COLUMN_INDEX)
+ + ";";
+ } while (crEm.moveToNext());
+ }
+ }
+ return vCard;
+ }
+
+ /**
+ * Check if the entry is not to be filtered out (allowed)
+ */
+ private boolean allowEntry(String phoneAddress, String filterString) {
+
+ boolean found = false;
+ VcardContent foundEntry = null;
+ for (VcardContent elem : list) {
+ if (elem.tel.contains(phoneAddress)) {
+ found = true;
+ foundEntry = elem;
+ }
+ }
+ if (found == false) {
+ VcardContent vCard = getVcardContent(phoneAddress);
+ if (vCard != null) {
+ list.add(vCard);
+ found = true;
+ foundEntry = vCard;
+ Log.d(TAG, " NEW VCARD ADDED " + vCard.tel + vCard.name
+ + vCard.email);
+ } else {
+ Log.d(TAG, "VCARD NOT FOUND ERROR");
+ }
+ }
+
+ if (found == true) {
+ if ((foundEntry.tel.contains(filterString))
+ || (foundEntry.name.contains(filterString))
+ || (foundEntry.email.contains(filterString))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the contact name for the given phone number
+ */
+ private String getContactName(String phoneNumber) {
+ // TODO Optimize this (get from list)
+
+ boolean found = false;
+ VcardContent foundEntry = null;
+ if(phoneNumber == null){
+ return null;
+ }
+ for (VcardContent elem : list) {
+ if (elem.tel == null){
+ continue;
+ }
+ if (elem.tel.contains(phoneNumber)) {
+ found = true;
+ foundEntry = elem;
+ break;
+ }
+ }
+ if (found == false) {
+ foundEntry = getVcardContent(phoneNumber);
+ if (foundEntry != null) {
+ list.add(foundEntry);
+ found = true;
+ }
+ }
+ if (found == true) {
+ return foundEntry.name;
+ }
+
+ return null;
+ }
+
+ private class OwnerInfo {
+ public String Name;
+ public String Number;
+ }
+
+ private OwnerInfo getOwnerInfo() {
+ OwnerInfo info = new OwnerInfo();
+ TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
+ if (tm != null) {
+ String sLocalPhoneNum = tm.getLine1Number();
+ String sLocalPhoneName = "";
+
+ Method m;
+ try {
+ m = tm.getClass().getMethod("getLine1AlphaTag", new Class[] { });
+ sLocalPhoneName = (String) m.invoke(tm);
+ } catch (SecurityException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchMethodException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ if (TextUtils.isEmpty(sLocalPhoneNum)) {
+ sLocalPhoneNum = "0000000000";
+ }
+ if (TextUtils.isEmpty(sLocalPhoneName)) {
+ sLocalPhoneName = "QCOM";
+ }
+ info.Name = sLocalPhoneName;
+ info.Number = sLocalPhoneNum;
+ }
+ return info;
+ }
+
+ private OwnerInfo ownerInfo = null;
+
+
+
+ /**
+ * Get the owners name
+ */
+ public String getOwnerName() {
+ // TODO
+ if ( ownerInfo == null ) {
+ ownerInfo = getOwnerInfo();
+ }
+ return ownerInfo.Name;
+ }
+
+ /**
+ * Get the owners phone number
+ */
+ public String getOwnerNumber() {
+ // TODO
+ if ( ownerInfo == null ) {
+ ownerInfo = getOwnerInfo();
+ }
+ return ownerInfo.Number;
+ }
+
+ private boolean isOutgoingSMSMessage(int type) {
+ if (type == 1) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get the list of messages in the given folder
+ *
+ * @return Listing of messages in MAP-msg-listing format
+ */
+ public BluetoothMasMessageListingRsp msgListing(String name,
+ BluetoothMasAppParams appParams) {
+ // TODO Auto-generated method stub
+
+ BluetoothMasMessageListingRsp rsp = new BluetoothMasMessageListingRsp();
+ CommonUtils cu = new CommonUtils();
+ boolean fileGenerated = false;
+
+ // TODO Do this based on the MasInstance
+ final String FILENAME = "msglist";
+
+ int writeCount = 0;
+ int processCount = 0;
+ int messageListingSize = 0;
+
+ List<MsgListingConsts> msgList = new ArrayList<MsgListingConsts>();
+
+ if (appParams == null) {
+ return null;
+ }
+
+ String tempPath = null;
+ if (name != null) {
+ tempPath = cu.getFullPath(name, context, folderList, CurrentPath);
+ if (tempPath == null) {
+ // Child folder not present
+ rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+ return rsp;
+ }
+ } else {
+ tempPath = CurrentPath;
+ }
+
+ FileOutputStream bos = null;
+
+ try {
+ bos = context.openFileOutput(FILENAME, Context.MODE_PRIVATE);
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ Log.d(TAG, "appParams.FilterMessageType ::"+ appParams.FilterMessageType);
+ Log.d(TAG, "Condition result::"+ (appParams.FilterMessageType & 0x09));
+ // TODO: Take care of subfolders
+ // TODO: Check only for SMS_GSM
+ // Look for messages only if both SMS/MMS are not filtered out
+ if ((tempPath != null) && (tempPath.split("/").length == 3 || tempPath.contains("Gmail")
+ || tempPath.split("/").length == 4) && ((appParams.FilterMessageType & 0x13) != 0x13)){
+
+ String splitStrings[] = tempPath.split("/");
+
+ // TODO: Take care of subfolders
+
+ Log.d(TAG, "splitString[2] = " + splitStrings[2]);
+
+ // TODO Assuming only for SMS.
+ String url = "content://sms/";
+ Uri uri = Uri.parse(url);
+ ContentResolver cr = context.getContentResolver();
+ SmsMmsUtils smu = new SmsMmsUtils();
+
+ String whereClause = smu.getConditionStringSms(splitStrings[2], appParams);
+
+ if (appParams.FilterReadStatus > 0x02) {
+ rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+ return rsp;
+ }
+
+ // TODO Filter priority?
+ /*
+ * There is no support for FilterPriority in SMS/MMS. So, we will
+ * assume Filter Priority is always non-high which makes sense for
+ * SMS/MMS.We check the content provider only if the Filter Priority
+ * is "unfiltered" or "non-high". Else, simply return an empty
+ * string. If the Filter priority is greater than 2, return a bad
+ * request.
+ */
+ if (appParams.FilterPriority == 0
+ || appParams.FilterPriority == 0x02) {
+ if(mode !=null && (mode.equalsIgnoreCase("SMS") || mode.equalsIgnoreCase("SMS_MMS") || mode.equalsIgnoreCase("SMS_MMS_EMAIL"))){
+ if ((appParams.FilterMessageType & 0x01) == 0) {
+ Cursor cursor = cr.query(uri, null, whereClause, null,
+ "date desc");
+
+ int idInd = cursor.getColumnIndex("_id");
+ int addressInd = cursor.getColumnIndex("address");
+ int personInd = cursor.getColumnIndex("person");
+ int dateInd = cursor.getColumnIndex("date");
+ int readInd = cursor.getColumnIndex("read");
+ int statusInd = cursor.getColumnIndex("status");
+ int subjectInd = cursor.getColumnIndex("subject");
+ int typeInd = cursor.getColumnIndex("type");
+ int bodyInd = cursor.getColumnIndex("body");
+
+ Log.d(TAG, "move to First" + cursor.moveToFirst());
+ Log.d(TAG,
+ "move to Liststartoffset"
+ + cursor.moveToPosition(appParams.ListStartOffset));
+
+ this.list = new ArrayList<VcardContent>();
+
+ if (cursor.moveToFirst()) {
+
+ do {
+ /*
+ * Apply remaining filters
+ */
+
+ /*
+ * For incoming message, originator is the remote
+ * contact For outgoing message, originator is the
+ * owner.
+ */
+ String filterString = null;
+ String oname = getOwnerName();
+ String onumber = getOwnerNumber();
+
+ int msgType = cursor.getInt(typeInd);
+
+ // TODO Filter Recipient
+ if (isOutgoingSMSMessage(msgType) == true) {
+ if ((appParams.FilterOriginator != null)
+ && (appParams.FilterOriginator.length() != 0)
+ && ((appParams.FilterOriginator
+ .compareTo(oname) != 0) && (appParams.FilterOriginator
+ .compareTo(onumber) != 0))) {
+ continue;
+ }
+ if ((appParams.FilterRecipient != null)
+ && (appParams.FilterRecipient.length() != 0)) {
+ filterString = appParams.FilterRecipient;
+ Log.d(TAG, "appParams.FilterRecipient"
+ + appParams.FilterRecipient);
+ }
+ }
+ if (isOutgoingSMSMessage(msgType) == false) {
+ if ((appParams.FilterRecipient != null)
+ && (appParams.FilterRecipient.length() != 0)
+ && ((appParams.FilterRecipient
+ .compareTo(oname) != 0) && (appParams.FilterRecipient
+ .compareTo(onumber) != 0))) {
+ continue;
+ }
+ if ((appParams.FilterOriginator != null)
+ && (appParams.FilterOriginator.length() != 0)) {
+ filterString = appParams.FilterOriginator;
+ Log.d(TAG, "appParams.FilterOriginator"
+ + appParams.FilterOriginator);
+ }
+ }
+
+ if (filterString != null) {
+ Log.d(TAG, "filterString = " + filterString);
+ if (allowEntry(cursor.getString(addressInd),
+ filterString) == true) {
+ Log.d(TAG,
+ "+++ ALLOWED +++++++++ "
+ + cursor.getString(addressInd)
+ + " - " + cursor.getPosition());
+ } else {
+ Log.d(TAG,
+ "+++ DENIED +++++++++ "
+ + cursor.getString(addressInd)
+ + " - " + cursor.getPosition());
+ continue;
+ }
+ }
+ Log.d(TAG, " msgListSize " + messageListingSize
+ + "write count " + writeCount);
+
+ messageListingSize++;
+
+ /*
+ * Don't want the listing; just send the listing size
+ * after applying all the filters.
+ */
+ if (appParams.MaxListCount == 0) {
+ continue;
+ }
+
+ processCount++;
+
+ MsgListingConsts ml = new MsgListingConsts();
+ ml.setMsg_handle(Integer.valueOf(cursor
+ .getString(idInd)));
+
+ Time time = new Time();
+ time.set(Long.valueOf(cursor.getString(dateInd)));
+
+ String datetimeStr = time.toString().substring(0, 15);
+
+ ml.msgInfo.setDateTime(datetimeStr);
+
+ if ((appParams.ParameterMask & BIT_SUBJECT) != 0) {
+ /* SMS doesn't have subject. Append Body
+ * so that remote client doesn't have to do
+ * GetMessage
+ */
+ ml.setSendSubject(true);
+ String subject = cursor.getString(bodyInd);
+ if (subject != null && subject.length() > appParams.SubjectLength ) {
+ subject = subject.substring(0,
+ appParams.SubjectLength);
+ }
+ ml.setSubject(subject);
+ }
+
+ if ((appParams.ParameterMask & BIT_DATETIME) != 0) {
+ ml.setDatetime(datetimeStr);
+ }
+
+ if ((appParams.ParameterMask & BIT_SENDER_NAME) != 0) {
+ // TODO Query the Contacts database
+ String senderName = null;
+ if (isOutgoingSMSMessage(msgType) == true) {
+ senderName = getOwnerName();
+ } else {
+ senderName = getContactName(cursor
+ .getString(addressInd));
+ }
+ ml.setSender_name(senderName);
+ }
+
+ if ((appParams.ParameterMask & BIT_SENDER_ADDRESSING) != 0) {
+ // TODO In case of a SMS this is
+ // the sender's phone number in canonical form
+ // (chapter
+ // 2.4.1 of [5]).
+ String senderAddressing = null;
+ if (isOutgoingSMSMessage(msgType) == true) {
+ senderAddressing = getOwnerNumber();
+ } else {
+ senderAddressing = cursor.getString(addressInd);
+ }
+ ml.setSender_addressing(senderAddressing);
+ }
+
+ if ((appParams.ParameterMask & BIT_RECIPIENT_NAME) != 0) {
+ // TODO "recipient_name" is the name of the
+ // recipient of
+ // the message, when it is known
+ // by the MSE device.
+ String recipientName = null;
+ if (isOutgoingSMSMessage(msgType) == false) {
+ recipientName = getOwnerName();
+ } else {
+ recipientName = getContactName(cursor
+ .getString(addressInd));
+ }
+ ml.setRecepient_name(recipientName);
+ }
+
+ if ((appParams.ParameterMask & BIT_RECIPIENT_ADDRESSING) != 0) {
+ // TODO In case of a SMS this is the recipient's
+ // phone
+ // number in canonical form (chapter 2.4.1 of [5])
+ String recipientAddressing = null;
+ if (isOutgoingSMSMessage(msgType) == false) {
+ recipientAddressing = getOwnerNumber();
+ } else {
+ recipientAddressing = cursor
+ .getString(addressInd);
+ }
+ ml.setRecepient_addressing(recipientAddressing);
+ ml.setSendRecipient_addressing(true);
+ }
+
+ if ((appParams.ParameterMask & BIT_TYPE) != 0) {
+ // TODO GSM or CDMA SMS?
+ ml.setType("SMS_GSM");
+ }
+
+ if ((appParams.ParameterMask & BIT_SIZE) != 0) {
+ ml.setSize(cursor.getString(bodyInd).length());
+ }
+
+ if ((appParams.ParameterMask & BIT_RECEPTION_STATUS) != 0) {
+ ml.setReception_status("complete");
+ }
+
+ if ((appParams.ParameterMask & BIT_TEXT) != 0) {
+ // TODO Set text to "yes"
+ ml.setContains_text("yes");
+ }
+
+ if ((appParams.ParameterMask & BIT_ATTACHMENT_SIZE) != 0) {
+ ml.setAttachment_size(0);
+ }
+
+ if ((appParams.ParameterMask & BIT_PRIORITY) != 0) {
+ // TODO Get correct priority
+ ml.setPriority("no");
+ }
+
+ if ((appParams.ParameterMask & BIT_READ) != 0) {
+ if (cursor.getString(readInd).equalsIgnoreCase("1")) {
+ ml.setRead("yes");
+ } else {
+ ml.setRead("no");
+ }
+ }
+
+ if ((appParams.ParameterMask & BIT_SENT) != 0) {
+ // TODO Get sent status?
+ if (cursor.getInt(typeInd) == 2) {
+ ml.setSent("yes");
+ } else {
+ ml.setSent("no");
+ }
+ }
+
+ if ((appParams.ParameterMask & BIT_PROTECTED) != 0) {
+ ml.setMsg_protected("no");
+ }
+
+ // TODO replyto_addressing is used only for email
+
+ // New Message?
+ if ((rsp.newMessage == 0)
+ && (cursor.getInt(readInd) == 0)) {
+ rsp.newMessage = 1;
+ }
+
+ msgList.add(ml);
+ writeCount++;
+ } while (cursor.moveToNext());
+
+ cursor.close();
+ }
+ }
+ }
+ if(mode !=null && (mode.equalsIgnoreCase("SMS_MMS") || mode.equalsIgnoreCase("SMS_MMS_EMAIL"))){
+ // Now that all of the SMS messages have been listed. Look for
+ // any
+ // MMS messages and provide them
+ if((appParams.FilterMessageType & 0x08) == 0) {
+
+ String splitStringsMms[] = tempPath.split("/");
+ name = splitStringsMms[2];
+
+ // MMS draft folder is called //mms/drafts not //mms/draft like
+ // SMS
+ if (name.equalsIgnoreCase("draft")) {
+ name = "drafts";
+ }
+
+ if (getNumMmsMsgs(name) != 0) {
+ MsgListingConsts mmsl = new MsgListingConsts();
+ List<Integer> list = getMmsMsgMIDs(bldMmsWhereClause(
+ appParams, getMMSFolderType(name)));
+ for (int msgId : list) {
+ Log.d(TAG, "\n MMS Text message ==> "
+ + getMmsMsgTxt(msgId));
+ Log.d(TAG, "\n MMS message subject ==> "
+ + getMmsMsgSubject(msgId));
+
+ String datetime = getMmsMsgDate(msgId);
+ Time time = new Time();
+ Date dt = new Date(Long.valueOf(datetime));
+ time.set((dt.getTime() * 1000));
+
+ String datetimeStr = time.toString().substring(0, 15);
+
+ mmsl = bldMmsMsgLstItem(msgId, appParams, name, datetimeStr);
+ mmsl.msgInfo.setDateTime(datetimeStr);
+
+ if ((rsp.newMessage == 0)
+ && (getMmsMsgReadStatus(msgId).equalsIgnoreCase("no"))) {
+ rsp.newMessage = 1;
+ }
+
+ msgList.add(mmsl);
+ messageListingSize++;
+ }
+ }
+ }
+ }
+ if(mode !=null && (mode.equalsIgnoreCase("EMAIL")
+ || mode.equalsIgnoreCase("SMS_MMS_EMAIL"))){
+ //Email messages
+ if((appParams.FilterMessageType & 0x04) == 0){
+ EmailUtils eu = new EmailUtils();
+ String folderName;
+
+ String splitStringsEmail[] = tempPath.split("/");
+ Log.d(TAG, "splitStringsEmail[2] = " + splitStringsEmail[2]);
+ // TODO: Take care of subfolders
+
+ folderName = eu.getFolderName(splitStringsEmail);
+ if(folderName != null && folderName.equalsIgnoreCase("draft")){
+ folderName = "Drafts";
+ }
+
+ String urlEmail = "content://com.android.email.provider/message";
+ Uri uriEmail = Uri.parse(urlEmail);
+ ContentResolver crEmail = context.getContentResolver();
+
+ String whereClauseEmail = eu.getConditionString(folderName, context, appParams);
+
+ Log.d(TAG, "## whereClauseEmail ##:" + whereClauseEmail);
+ Cursor cursor = crEmail.query(uriEmail, null, whereClauseEmail, null, "timeStamp desc");
+
+ int idInd = cursor.getColumnIndex("_id");
+ int fromIndex = cursor.getColumnIndex("fromList");
+ int toIndex = cursor.getColumnIndex("toList");
+ int dateInd = cursor.getColumnIndex("timeStamp");
+ int readInd = cursor.getColumnIndex("flagRead");
+ int subjectInd = cursor.getColumnIndex("subject");
+ int replyToInd = cursor.getColumnIndex("replyToList");
+
+ Log.d(TAG, "move to First" + cursor.moveToFirst());
+ Log.d(TAG, "move to Liststartoffset"
+ + cursor.moveToPosition(appParams.ListStartOffset));
+
+
+ if (cursor.moveToFirst()) {
+
+ do {
+ /*
+ * Apply remaining filters
+ */
+
+ Log.d(TAG, " msgListSize " + messageListingSize
+ + "write count " + writeCount);
+
+ messageListingSize++;
+
+ String subject = cursor.getString(subjectInd);
+ String timestamp = cursor.getString(dateInd);
+ String senderName = cursor.getString(fromIndex);
+ String senderAddressing = cursor.getString(fromIndex);
+ String recipientName = cursor.getString(toIndex);
+ String recipientAddressing = cursor.getString(toIndex);
+ String msgId = cursor.getString(idInd);
+ String readStatus = cursor.getString(readInd);
+ String replyToStr = cursor.getString(replyToInd);
+
+ /*
+ * Don't want the listing; just send the listing size after
+ * applying all the filters.
+ */
+
+ processCount++;
+ /*
+ * TODO Skip the first ListStartOffset record(s). Don't write
+ * more than MaxListCount record(s).
+ */
+
+ MsgListingConsts emailMsg = new MsgListingConsts();
+ emailMsg = eu.bldEmailMsgLstItem(context, folderName, appParams,
+ subject, timestamp, senderName, senderAddressing,
+ recipientName, recipientAddressing,
+ msgId, readStatus, replyToStr);
+
+ // New Message?
+ if ((rsp.newMessage == 0) && (cursor.getInt(readInd) != 0)) {
+ rsp.newMessage = 1;
+ }
+ msgList.add(emailMsg);
+ writeCount++;
+ } while (cursor.moveToNext());
+
+ cursor.close();
+ }
+ }//end email Messages if
+ }
+ }
+ else {
+ if (appParams.FilterPriority > 0x02) {
+ rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+ }
+ }
+ }
+
+ // Now that the message list exists, we can sort the list by date
+ Collections.sort(msgList, new SortMsgListByDate());
+
+ // Process the list based on MaxListCount and list offset
+ String str = null;
+ int numOfItems = msgList.size();
+ int msgDelta = numOfItems - appParams.ListStartOffset;
+ int startIdx = appParams.ListStartOffset;
+ int stopIdx = 0;
+ if (msgDelta <= 0) {
+ str = "";
+ } else {
+
+ if (msgDelta <= appParams.MaxListCount) {
+ stopIdx = startIdx + msgDelta;
+ } else {
+ stopIdx = startIdx + appParams.MaxListCount;
+ }
+ List<MsgListingConsts> msgSubList = msgList.subList(startIdx,
+ stopIdx);
+ str = mu.messageListingXML(msgSubList);
+
+ }
+ // TODO Undo the following check
+ int pos = str.indexOf(" msg handle=\"");
+ while (pos > 0) {
+ Log.d(TAG, " Msg listing str modified");
+ String str2 = str.substring(0, pos);
+ str2 += str.substring(pos + 1);
+ str = str2;
+ pos = str.indexOf(" msg handle=\"");
+ }
+
+ // String str = "this is a test for the data file";
+ try {
+ bos.write(str.getBytes());
+ bos.flush();
+ bos.close();
+ } catch (IOException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ msgList.clear();
+
+ Log.d(TAG, "");
+
+ Log.d(TAG, " MESSAGE LISTING FULL ( total length)" + str.length());
+ Log.d(TAG, str);
+
+ byte[] readBytes = new byte[10];
+ try {
+
+ FileInputStream fis = new FileInputStream(context.getFilesDir()
+ + "/" + FILENAME);
+ fis.close();
+ fileGenerated = true;
+
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ if (fileGenerated == true) {
+ File file = new File(context.getFilesDir() + "/" + FILENAME);
+ rsp.file = file;
+ }
+ rsp.msgListingSize = messageListingSize;
+
+ return rsp;
+ }
+
+ public class BluetoothMasMessageRsp {
+ public byte fractionDeliver = 0;
+ public File file = null;
+ public int rsp = ResponseCodes.OBEX_HTTP_OK;
+ }
+
+ /**
+ * Get the folder name (MAP representation) based on the folder type value
+ * in SMS database
+ */
+ private String getMAPFolder(String type, String threadId) {
+ String folder = null;
+ if ( type == null || threadId == null){
+ Log.d(TAG, "getMapFolder cannot parse folder type");
+ return folder;
+ }
+
+ if ( Integer.valueOf(threadId) == DELETED_THREAD_ID){
+ folder = Deleted;
+ } else {
+ switch (Integer.valueOf(type)) {
+ case 1:
+ folder = Inbox;
+ break;
+ case 2:
+ folder = Sent;
+ break;
+ case 3:
+ folder = Draft;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ folder = Outbox;
+ break;
+ default:
+ break;
+ }
+ }
+ return folder;
+ }
+
+ /**
+ * Get the folder name (MAP representation) based on the message Handle
+ */
+ private String getContainingFolder(String msgHandle) {
+ Cursor cr;
+
+ cr = context.getContentResolver().query(
+ Uri.parse("content://sms/" + msgHandle),
+ new String[] { "_id", "type", "thread_id" }, null, null, null);
+ if (cr.getCount() > 0) {
+ cr.moveToFirst();
+ return getMAPFolder(cr.getString(cr.getColumnIndex("type")),
+ cr.getString(cr.getColumnIndex("thread_id")));
+ }
+ return null;
+ }
+
+ /**
+ * Get the SMS Deliver PDU for the given SMS
+ */
+ private String getSMSDeliverPdu(String smsBody, String dateTime, String address){
+
+ Time time = new Time();
+ time.set(Long.valueOf(dateTime));
+
+ String timeStr = time.format3339(false);
+
+ // Extract the YY, MM, DD, HH, MM, SS from time
+ String tempTimeStr = timeStr.substring(2,4) + timeStr.substring(5, 7)
+ + timeStr.substring(8, 10) + timeStr.substring(11, 13) +
+ timeStr.substring(14, 16) + timeStr.substring(17, 19);
+
+ /* Calculate the time zone offset
+ * An offset of 1 indicates 15 min difference between local
+ * time and GMT. MSB of 1 in offset indicates it is negative
+ */
+ String tZoneStr = timeStr.substring(timeStr.length()- 6);
+ int tempInt = Integer.valueOf(tZoneStr.substring(tZoneStr.length()-2));
+ int tZone15offset = tempInt / 15;
+
+ tZone15offset += (Integer.valueOf(tZoneStr.substring(tZoneStr.length()-5, tZoneStr.length()-3)) * 4);
+ if ( timeStr.charAt(timeStr.length()-6) == '-'){
+ tZone15offset = tZone15offset | 0x80;
+ }
+
+ String tZone15OffsetHexStr = "";
+
+ // Add 0 as prefix for single digit offset
+ if(((int) tZone15offset & 0xff) < 0x10){
+ tZone15OffsetHexStr += "0";
+ }
+ tZone15OffsetHexStr += Integer.toHexString(tZone15offset);
+
+ tempTimeStr += tZone15OffsetHexStr;
+
+ // Swap the nibble
+ String encodedTimeStr = "";
+ for ( int i=0; i<tempTimeStr.length(); i=i+2){
+ encodedTimeStr += tempTimeStr.substring(i+1, i+2);
+ encodedTimeStr += tempTimeStr.substring(i, i+1);
+ }
+
+ byte[] byteAddress = address.getBytes();
+
+ // Let the service center number be 0000000000
+ String smsPdu = "0681000000000004";
+
+ // Extract only digits out of the phone address
+ StringBuffer strbufAddress = new StringBuffer(address.length() + 1);
+ for ( int i=0; i<address.length(); i++){
+ Log.d(TAG, " VAL " + address.substring(i, i+1));
+ if ( byteAddress[i] >= 48 && byteAddress[i] <= 57 ){
+ strbufAddress.append(Integer.parseInt(address.substring(i, i+1)));
+ }
+ }
+
+ int addressLength = strbufAddress.length();
+
+ String addressLengthStr = "";
+
+ if(((int) addressLength & 0xff) < 0x10)
+ addressLengthStr += "0";
+ addressLengthStr += Integer.toHexString(addressLength);
+
+ smsPdu = smsPdu + addressLengthStr;
+ smsPdu = smsPdu + "81";
+
+ String strAddress = new String(strbufAddress);
+
+ // Use getSubmitPdu only to obtain the encoded msg and encoded address
+ byte[] msg = SmsMessage.getSubmitPdu(null, strAddress, smsBody, false).encodedMessage;
+
+ int addLength = Integer.valueOf(msg[2]);
+ if ( addLength %2 != 0){
+ addLength++;
+ }
+ addLength = addLength / 2;
+
+ // Extract the message from the SubmitPdu
+ int msgOffset = 7 + addLength;
+ int msgLength = msg.length - msgOffset;
+
+ StringBuffer strbufMessage = new StringBuffer(msgLength * 2);
+
+ // Convert from byte to Hex String
+ for(int i=msgOffset; i<msgLength + msgOffset; i++)
+ {
+ if(((int) msg[i] & 0xff) < 0x10){
+ strbufMessage.append("0");
+ }
+ strbufMessage.append((Long.toString((int) msg[i] & 0xff, 16)));
+ }
+
+ int encodedAddressLength = strAddress.length() / 2;
+ if ( strAddress.length() % 2 != 0){
+ encodedAddressLength++;
+ }
+
+ StringBuffer strbufAddress1 = new StringBuffer(msgLength * 2);
+
+ // Convert from byte to Hex String
+ for(int i=4; i<encodedAddressLength + 4; i++)
+ {
+ if(((int) msg[i] & 0xff) < 0x10)
+ strbufAddress1.append("0");
+ strbufAddress1.append((Long.toString((int) msg[i] & 0xff, 16)));
+ }
+
+ smsPdu += strbufAddress1;
+ smsPdu += "0000";
+ smsPdu += encodedTimeStr;
+
+ int smsBodyLength = smsBody.length();
+ String smsMessageTextLengthStr = "";
+
+ if(((int) smsBodyLength & 0xff) < 0x10){
+ smsMessageTextLengthStr += "0";
+ }
+ smsMessageTextLengthStr += Integer.toHexString(smsBodyLength);
+
+ smsPdu += smsMessageTextLengthStr;
+ smsPdu += strbufMessage;
+ smsPdu = smsPdu.toUpperCase();
+ return smsPdu;
+ }
+
+ /**
+ * Get the message for the given message handle
+ *
+ * @return BMSG object
+ */
+ public BluetoothMasMessageRsp msg(String msgHandle,
+ BluetoothMasAppParams bluetoothMasAppParams) {
+ BluetoothMasMessageRsp rsp = new BluetoothMasMessageRsp();
+ if (msgHandle == null || msgHandle.length() == 0) {
+ rsp.file = null;
+ rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+ return rsp;
+ }
+
+ Cursor cr = null;
+ Uri uri = Uri.parse("content://sms/");
+ String whereClause = " _id = " + msgHandle;
+ try {
+ cr = context.getContentResolver().query(uri, null, whereClause, null,
+ null);
+ } catch (Exception e){
+ rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+ return rsp;
+ }
+
+ if (cr.getCount() > 0) {
+ cr.moveToFirst();
+ String containingFolder = getContainingFolder(msgHandle);
+ BmessageConsts bmsg = new BmessageConsts();
+
+ // Create a bMessage
+
+ // TODO Get Current type
+ bmsg.setType("SMS_GSM");
+
+ bmsg.setBmsg_version("1.0");
+ if (cr.getString(cr.getColumnIndex("read")).equalsIgnoreCase("1")) {
+ bmsg.setStatus("READ");
+ } else {
+ bmsg.setStatus("UNREAD");
+ }
+
+ bmsg.setFolder("TELECOM/MSG/" + containingFolder);
+
+ bmsg.setVcard_version("2.1");
+ VcardContent vcard = getVcardContent(cr.getString(cr
+ .getColumnIndex("address")));
+
+ String type = cr.getString(cr.getColumnIndex("type"));
+ // Inbox is type 1.
+ if (type.equalsIgnoreCase("1")) {
+ // The address in database is of originator
+ bmsg.setOriginatorVcard_name(vcard.name);
+ bmsg.setOriginatorVcard_phone_number(vcard.tel);
+ bmsg.setRecipientVcard_name(getOwnerName());
+ bmsg.setRecipientVcard_phone_number(getOwnerNumber());
+ } else {
+ bmsg.setRecipientVcard_name(vcard.name);
+ bmsg.setRecipientVcard_phone_number(vcard.tel);
+ bmsg.setOriginatorVcard_name(getOwnerName());
+ bmsg.setOriginatorVcard_phone_number(getOwnerNumber());
+ }
+
+ String smsBody = " ";
+
+ if ( (int)bluetoothMasAppParams.Charset == 1){
+ bmsg.setBody_charset("UTF-8");
+ smsBody = cr.getString(cr.getColumnIndex("body"));
+ }
+
+ if ( (int)bluetoothMasAppParams.Charset == 0){
+ bmsg.setBody_encoding("G-7BIT");
+ String smsBodyUnicode = cr.getString(cr.getColumnIndex("body"));
+ smsBody = getSMSDeliverPdu(smsBodyUnicode, cr.getString(cr.getColumnIndex("date")), vcard.tel);
+ }
+
+ bmsg.setBody_length(22 + smsBody.length());
+
+ bmsg.setBody_msg(smsBody);
+
+
+ // Send a bMessage
+ Log.d(TAG, "bMessageSMS test\n");
+ Log.d(TAG, "=======================\n\n");
+ String str = mu.toBmessageSMS(bmsg);
+ Log.d(TAG, str);
+ Log.d(TAG, "\n\n");
+
+ if (str != null && (str.length() > 0)) {
+ final String FILENAME = "message";
+ // BufferedOutputStream bos = null;
+ FileOutputStream bos = null;
+ File file = new File(context.getFilesDir() + "/" + FILENAME);
+ file.delete();
+
+ try {
+ bos = context
+ .openFileOutput(FILENAME, Context.MODE_PRIVATE);
+ bos.write(str.getBytes());
+ bos.flush();
+ bos.close();
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ File fileR = new File(context.getFilesDir() + "/" + FILENAME);
+ if (fileR.exists() == true) {
+ rsp.file = fileR;
+ rsp.fractionDeliver = 1;
+ }
+
+ }
+
+ }
+ else{
+
+ // No SMS message was discovered with that handle, now look for an
+ // MMS message
+
+ /*
+ * Spec 5.6.4 says MSE shall reject request with value native
+ * for MMS and Email
+ */
+ if ( (int)bluetoothMasAppParams.Charset == 0 ) {
+ rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+ return rsp;
+ }
+ int mmsMsgID = 0;
+ try {
+ mmsMsgID = getMmsMsgHndToID(Integer.valueOf(msgHandle));
+ } catch (Exception e) {
+ rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+ return rsp;
+ }
+ if (mmsMsgID > 0) {
+ bldMmsBmsg(mmsMsgID, rsp);
+ }
+ else{ //No SMS and MMS messages were discovered. now look for email messages
+ Log.d(TAG, "Inside email :");
+ // Email message
+ int emailMsgID = 0;
+ EmailUtils eu = new EmailUtils();
+
+ emailMsgID = (Integer.valueOf(msgHandle) - EMAIL_HDLR_CONSTANT);
+ String str = eu.bldEmailBmsg(emailMsgID, rsp, context, mu);
+
+ Log.d(TAG, str);
+ Log.d(TAG, "\n\n");
+
+ if (str != null && (str.length() > 0)) {
+ final String FILENAME = "message";
+ FileOutputStream bos = null;
+ File file = new File(context.getFilesDir() + "/" + FILENAME);
+ file.delete();
+
+ try {
+ bos = context.openFileOutput(FILENAME, Context.MODE_PRIVATE);
+ bos.write(str.getBytes());
+ bos.flush();
+ bos.close();
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ File fileR = new File(context.getFilesDir() + "/" + FILENAME);
+ if (fileR.exists() == true) {
+ rsp.file = fileR;
+ rsp.fractionDeliver = 1;
+ }
+ else {
+ rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+ }
+
+ }
+ }
+ }
+
+ return rsp;
+ }
+
+ public class BluetoothMasPushMsgRsp {
+ public int response;
+ public String msgHandle;
+ }
+
+ /**
+ * Retrieve the conversation thread id
+ */
+ private int getThreadId(String address) {
+
+ Cursor cr = context.getContentResolver().query(
+ Uri.parse("content://sms/"), null,
+ "address = '" + address + "'", null, null);
+ if (cr.moveToFirst()) {
+ int threadId = Integer.valueOf(cr.getString(cr
+ .getColumnIndex("thread_id")));
+ Log.d(TAG, " Found the entry, thread id = " + threadId);
+
+ return (threadId);
+ }
+ return 0;
+ }
+
+ /**
+ * Adds a SMS to the Sms ContentProvider
+ */
+
+ public String addToSmsFolder(String folder, String address, String text) {
+
+ int threadId = getThreadId(address);
+ Log.d(TAG, "-------------");
+ Log.d(TAG, "address " + address + " TEXT " + text + " Thread ID "
+ + threadId);
+
+ ContentValues values = new ContentValues();
+ values.put("thread_id", threadId);
+ values.put("body", text);
+ values.put("address", address);
+ values.put("read", 0);
+ values.put("seen", 0);
+ /*
+ * status none -1 complete 0 pending 64 failed 128
+ */
+ values.put("status", -1);
+ /*
+ * outbox 4 queued 6
+ */
+ values.put("locked", 0);
+ values.put("error_code", 0);
+ Uri uri = context.getContentResolver().insert(
+ Uri.parse("content://sms/" + folder), values);
+ Log.d(TAG, " NEW URI " + uri.toString());
+
+ String str = uri.toString();
+ String[] splitStr = str.split("/");
+ Log.d(TAG, " NEW HANDLE " + splitStr[3]);
+ return splitStr[3];
+ }
+ /**
+ * Adds an Email to the Email ContentProvider
+ */
+
+ public String addToEmailFolder(String folder, String address, String text, String subject, String OrigEmail, String OrigName) {
+
+ Log.d(TAG, "-------------");
+ Log.d(TAG, "address " + address);
+ Log.d(TAG, "TEXT " + text);
+
+ //TODO need to insert a row in the body table and update the mailbox table with the no of messages unread
+ Cursor cr;
+ int folderId = 0;
+ int accountId = 0;
+ Time timeObj = new Time();
+ timeObj.setToNow();
+
+ String whereClause = "UPPER(displayName) LIKE '%"+folder.toUpperCase().trim()+"%'";
+ cr = context.getContentResolver().query(
+ Uri.parse("content://com.android.email.provider/mailbox"),
+ null, whereClause, null, null);
+ if (cr.getCount() > 0) {
+ cr.moveToFirst();
+ folderId = cr.getInt(cr.getColumnIndex("_id"));
+ }
+
+
+
+ Cursor cr1;
+ String whereClause1 = "UPPER(emailAddress) LIKE '"+OrigEmail.toUpperCase().trim()+"'";
+ cr1 = context.getContentResolver().query(
+ Uri.parse("content://com.android.email.provider/account"),
+ null, whereClause1, null, null);
+ if (cr1.getCount() > 0) {
+ cr1.moveToFirst();
+ accountId = cr1.getInt(cr1.getColumnIndex("_id"));
+ }
+
+
+
+ Log.d(TAG, "-------------");
+ Log.d(TAG, "To address " + address);
+ Log.d(TAG, "Text " + text);
+ Log.d(TAG, "Originator email address:: " + OrigEmail);
+ Log.d(TAG, "Originator email name:: " + OrigName);
+ Log.d(TAG, "Time Stamp:: " + timeObj.toMillis(false));
+ Log.d(TAG, "Account Key:: " + accountId);
+ Log.d(TAG, "Folder Id:: " + folderId);
+ Log.d(TAG, "Folder Name:: " + folder);
+ Log.d(TAG, "Subject" + subject);
+
+ ContentValues values = new ContentValues();
+ //Hardcoded values
+ values.put("syncServerId", "1");
+ //end Hardcoded values
+ values.put("syncServerTimeStamp", timeObj.toMillis(false));
+ values.put("displayName", OrigName.trim());
+ values.put("timeStamp", timeObj.toMillis(false));
+ values.put("subject", subject.trim());
+ values.put("flagLoaded", "1");
+ values.put("flagFavorite", "0");
+ values.put("flagAttachment", "0");
+ values.put("flags", "0");
+
+ values.put("accountKey", accountId);
+ values.put("fromList", OrigEmail.trim());
+
+ values.put("mailboxKey", folderId);
+ values.put("toList", address.trim());
+ values.put("flagRead", 0);
+
+ Uri uri = context.getContentResolver().insert(
+ Uri.parse("content://com.android.email.provider/message"), values);
+ Log.d(TAG, " NEW URI " + uri.toString());
+
+ String str = uri.toString();
+ String[] splitStr = str.split("/");
+ Log.d(TAG, " NEW HANDLE " + splitStr[4]);
+
+ //TODO need to insert into the body table --seems like body table gets updated automatically
+ ContentValues valuesBody = new ContentValues();
+ valuesBody.put("messageKey", splitStr[4]);
+ valuesBody.put("textContent", text);
+
+ Uri uri1 = context.getContentResolver().insert(
+ Uri.parse("content://com.android.email.provider/body"), valuesBody);
+
+ return splitStr[4];
+
+ }
+ public long getEmailAccountId(String email) {
+ Cursor cr1;
+ long accountId = -1;
+ String whereClause1 = "UPPER(emailAddress) LIKE '"+email.toUpperCase().trim()+"'";
+ cr1 = context.getContentResolver().query(
+ Uri.parse("content://com.android.email.provider/account"),
+ null, whereClause1, null, null);
+ if (cr1.getCount() > 0) {
+ cr1.moveToFirst();
+ accountId = cr1.getInt(cr1.getColumnIndex("_id"));
+ }
+ return accountId;
+ }
+
+ /**
+ * Get the type (as in Sms ContentProvider) for the given table name
+ */
+ private int getSMSFolderType(String folder) {
+ int type = 0;
+ if (folder.equalsIgnoreCase(Inbox)) {
+ type = 1;
+ } else if (folder.equalsIgnoreCase(Sent)) {
+ type = 2;
+ } else if (folder.equalsIgnoreCase(Draft)) {
+ type = 3;
+ } else if (folder.equalsIgnoreCase(Outbox)) {
+ type = 4;
+ } else if (folder.equalsIgnoreCase(Failed)) {
+ type = 5;
+ } else if (folder.equalsIgnoreCase(Queued)) {
+ type = 6;
+ }
+ return type;
+ }
+
+ /**
+ * Modify the type (as in Sms ContentProvider) For eg: move from outbox to
+ * send type
+ */
+ private void moveToFolder(String handle, String folder) {
+ // Make sure the handle is not null (Tranparent == 0)
+ if (handle != null) {
+ ContentValues values = new ContentValues();
+ values.put("type", getSMSFolderType(folder));
+ Uri uri = Uri.parse("content://sms/" + handle);
+ context.getContentResolver().update(uri, values, null, null);
+ }
+ }
+
+ private String PhoneAddress;
+ private String SmsText;
+ private String SmsHandle;
+
+
+ private String EmailAddress;
+ private String EmailText;
+ private String EmailHandle;
+ private String EmailSubject;
+ private String EmailOriginator;
+ private String EmailOrigName;
+
+ /**
+ * Push a outgoing message from MAS Client to the network
+ *
+ * @return Response to push command
+ */
+ public BluetoothMasPushMsgRsp pushMsg(String name, File file,
+ BluetoothMasAppParams bluetoothMasAppParams) {
+ // TODO Auto-generated method stub
+
+ BluetoothMasPushMsgRsp rsp = new BluetoothMasPushMsgRsp();
+ rsp.response = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ rsp.msgHandle = null;
+
+ final String SENT = "Sent";
+ final String DELIVERED = "Delivered";
+
+ if(!checkPath(false, name, false) ||
+ CurrentPath == null ||
+ CurrentPath.equals("telecom") ||
+ (CurrentPath.equals("telecom/msg") && (name == null)) ) {
+ rsp.response = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+ return rsp;
+ }
+
+
+
+ byte[] readBytes = null;
+ FileInputStream fis;
+ try {
+ fis = new FileInputStream(file);
+ readBytes = new byte[(int) file.length()];
+ fis.read(readBytes);
+
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return rsp;
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return rsp;
+ }
+
+ String readStr = new String(readBytes);
+ MapUtils mu = new MapUtils();
+ String type = mu.fetchType(readStr);
+ if(type!=null && (type.equalsIgnoreCase("SMS_GSM") || type.equalsIgnoreCase("MMS"))){
+ BmessageConsts bMsg = mu.fromBmessageSMS(readStr);
+ PhoneAddress = bMsg.getRecipientVcard_phone_number();
+ SmsText = bMsg.getBody_msg();
+
+ // If the message to be pushed is an MMS message, extract any text,
+ // discard
+ // any attachments and convert the message to an SMS
+
+ if ((bMsg.getType()).equals("MMS")) {
+ /*
+ * The pair of calls below is used to send the MMS message out to
+ * the network.You need to first move the message to the drafts
+ * folder and then move the message from drafts to the outbox
+ * folder. This action causes the message to also be added to the
+ * pending_msgs table in the database. The transaction service will
+ * then send the message out to the network the next time it is
+ * scheduled to run
+ */
+
+ moveMMStoDrafts(readStr);
+ moveMMSfromDraftstoOutbox();
+
+ Log.d(TAG, "\nBroadcasting Intent to MmsSystemEventReceiver\n ");
+ Intent sendIntent = new Intent("android.intent.action.MMS_PUSH");
+ context.sendBroadcast(sendIntent);
+
+ return rsp;
+
+ }
+
+ String tmpPath = "";
+ tmpPath = (name == null) ? CurrentPath : CurrentPath + "/" + name;
+
+ if(!tmpPath.equalsIgnoreCase("telecom/msg/outbox")) {
+ String splitStrings[] = CurrentPath.split("/");
+ mnsClient.addMceInitiatedOperation("+");
+ int tmp = splitStrings.length;
+ String folderName;
+ if (name != null) {
+ if (name.equals("")){
+ folderName = splitStrings[tmp - 1];
+ } else {
+ folderName = name;
+ }
+ } else {
+ folderName = splitStrings[tmp - 1];
+ }
+ SmsHandle = addToSmsFolder(folderName, PhoneAddress, SmsText);
+ rsp.msgHandle = SmsHandle;
+ rsp.response = ResponseCodes.OBEX_HTTP_OK;
+ return rsp;
+ }
+
+ PendingIntent sentPI = PendingIntent.getBroadcast(this.context, 0,
+ new Intent(SENT), 0);
+
+ PendingIntent deliveredPI = PendingIntent.getBroadcast(this.context, 0,
+ new Intent(DELIVERED), 0);
+
+ // ---when the SMS has been sent---
+ context.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context arg0, Intent arg1) {
+ Log.d(TAG, "Sms SENT STATUS ");
+
+ switch (getResultCode()) {
+ case Activity.RESULT_OK:
+ Log.d(TAG, "Sms Sent");
+ moveToFolder(SmsHandle, Sent);
+ break;
+ case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
+ Log.d(TAG, "Generic Failure");
+ moveToFolder(SmsHandle, Failed);
+ break;
+ case SmsManager.RESULT_ERROR_NO_SERVICE:
+ Log.d(TAG, "NO SERVICE ERROR");
+ moveToFolder(SmsHandle, Queued);
+ break;
+ case SmsManager.RESULT_ERROR_NULL_PDU:
+ Log.d(TAG, "Null PDU");
+ moveToFolder(SmsHandle, Failed);
+ break;
+ case SmsManager.RESULT_ERROR_RADIO_OFF:
+ Log.d(TAG, "RADIO OFF");
+ moveToFolder(SmsHandle, Queued);
+ break;
+ }
+ context.unregisterReceiver(this);
+ }
+ }, new IntentFilter(SENT));
+
+ // ---when the SMS has been delivered---
+ context.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context arg0, Intent arg1) {
+ Log.d(TAG, "Sms SENT DELIVERED ");
+
+ switch (getResultCode()) {
+ case Activity.RESULT_OK:
+ Log.d(TAG, "Sms Delivered");
+ break;
+ case Activity.RESULT_CANCELED:
+ Log.d(TAG, "Sms NOT Delivered");
+ break;
+ }
+ context.unregisterReceiver(this);
+ }
+ }, new IntentFilter(DELIVERED));
+
+ SmsHandle = null;
+ rsp.msgHandle = "";
+
+ if(bluetoothMasAppParams.Transparent == 0) {
+ mnsClient.addMceInitiatedOperation("+");
+ SmsHandle = addToSmsFolder("outbox", PhoneAddress, SmsText);
+ rsp.msgHandle = SmsHandle;
+ }
+
+
+ SmsManager sms = SmsManager.getDefault();
+
+ Log.d(TAG, " Trying to send SMS ");
+ Log.d(TAG, " Text " + SmsText + " address " + PhoneAddress);
+
+ try {
+ sms.sendTextMessage(PhoneAddress, null, SmsText, sentPI,
+ deliveredPI);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+
+ }
+ }
+ else if(type!=null && type.equalsIgnoreCase("EMAIL")){
+ Log.d(TAG, " Before fromBmessageemail method:: "+readStr);
+ BmessageConsts bMsg = mu.fromBmessageEmail(readStr);
+ EmailAddress = bMsg.getRecipientVcard_email();
+ EmailText = bMsg.getBody_msg();
+ EmailSubject = bMsg.getSubject();
+ EmailOriginator = bMsg.getOriginatorVcard_email();
+ EmailOrigName = bMsg.getOriginatorVcard_name();
+
+ String tmpPath = "";
+ tmpPath = (name == null) ? CurrentPath : CurrentPath + "/" + name;
+
+ if(!tmpPath.equalsIgnoreCase("telecom/msg/sent1")) {
+ String splitStrings[] = CurrentPath.split("/");
+ mnsClient.addMceInitiatedOperation("+");
+ int tmp = splitStrings.length;
+ String folderName;
+ if (name != null) {
+ if (name.equals("")){
+ folderName = splitStrings[tmp - 1];
+ } else {
+ folderName = name;
+ }
+ } else {
+ folderName = splitStrings[tmp - 1];
+ }
+ EmailHandle = addToEmailFolder(folderName, EmailAddress, EmailText, EmailSubject,
+ EmailOriginator, EmailOrigName);
+ rsp.msgHandle = EmailHandle;
+ rsp.response = ResponseCodes.OBEX_HTTP_OK;
+
+ long accountId = getEmailAccountId(EmailOriginator);
+ Log.d(TAG, " Account id before Mail service:: "+accountId);
+
+ Intent emailIn = new Intent();
+
+ emailIn.setAction("com.android.email.intent.action.MAIL_SERVICE_WAKEUP");
+ emailIn.putExtra("com.android.email.intent.extra.ACCOUNT", accountId);
+ this.context.startService(emailIn);
+
+ return rsp;
+ }
+ else{
+ String splitStrings[] = CurrentPath.split("/");
+ mnsClient.addMceInitiatedOperation("+");
+ int tmp = splitStrings.length;
+ String folderName;
+ if (name != null) {
+ if (name.equals("")){
+ folderName = splitStrings[tmp - 1];
+ } else {
+ folderName = name;
+ }
+ }
+ else {
+ folderName = splitStrings[tmp - 1];
+ }
+
+ }
+
+ }
+
+ rsp.response = ResponseCodes.OBEX_HTTP_OK;
+
+ return rsp;
+ }
+
+ private void updateMMSThreadId(String msgHandle, int threadId){
+ ContentValues values = new ContentValues();
+ values.put("thread_id", threadId);
+ context.getContentResolver().update( Uri.parse("content://mms/" + msgHandle), values, null, null);
+ }
+
+ private void deleteMMS(String msgHandle){
+ Cursor cr = context.getContentResolver().query(Uri.parse("content://mms/" + msgHandle), null, null, null, null);
+ if ( cr.moveToFirst()){
+ int threadId = cr.getInt(cr.getColumnIndex(("thread_id")));
+ if ( threadId != DELETED_THREAD_ID){
+ // Move to deleted folder
+ updateMMSThreadId(msgHandle, Integer.valueOf(DELETED_THREAD_ID));
+ } else {
+ // Delete the message permanently
+ int msgId = Integer.valueOf(msgHandle) + MMS_HDLR_CONSTANT;
+ mnsClient.addMceInitiatedOperation(Integer.toString(msgId));
+ context.getContentResolver().delete(Uri.parse("content://mms/" + msgHandle), null, null);
+ }
+ cr.close();
+ }
+ }
+
+ private void unDeleteMMS(String msgHandle){
+
+ Cursor cr = context.getContentResolver().query(Uri.parse("content://mms/" + msgHandle), null, null, null, null );
+
+ if (cr.moveToFirst()){
+
+ // Make sure that the message is in delete folder
+ String currentThreadId = cr.getString(cr.getColumnIndex("thread_id"));
+ if ( currentThreadId != null && Integer.valueOf(currentThreadId) != -1){
+ Log.d(TAG, " Not in delete folder");
+ return;
+ }
+
+ // Fetch the address of the deleted message
+
+ String address = getMmsMsgAddress(Integer.valueOf(msgHandle));
+
+ // Search the database for the given message ID
+ Cursor crThreadId = context.getContentResolver().query(Uri.parse("content://mms/"), null,"_id = " + msgHandle + " AND thread_id != -1", null, null);
+ if (crThreadId.moveToFirst()) {
+ // A thread for the given message ID exists in the database
+ String threadIdStr = crThreadId.getString(crThreadId.getColumnIndex("thread_id"));
+ Log.d(TAG, " THREAD ID " + threadIdStr);
+ updateMMSThreadId(msgHandle, Integer.valueOf(threadIdStr));
+ }
+ else
+ {
+ /* No thread for the given address
+ * Create a fake message to obtain the thread, use that thread_id
+ * and then delete the fake message
+ */
+ ContentValues tempValue = new ContentValues();
+ tempValue.put("address", address);
+ tempValue.put("type", "20");
+ Uri tempUri = context.getContentResolver().insert( Uri.parse("content://sms/"), tempValue);
+
+ if ( tempUri != null ) {
+ String tempMsgId = tempUri.getLastPathSegment();
+ Cursor tempCr = context.getContentResolver().query(tempUri, null, null, null, null);
+ tempCr.moveToFirst();
+ String newThreadIdStr = tempCr.getString(tempCr.getColumnIndex("thread_id"));
+ tempCr.close();
+
+ updateMMSThreadId(msgHandle, Integer.valueOf(newThreadIdStr));
+
+ context.getContentResolver().delete(tempUri, null, null);
+ }
+ else {
+ Log.d(TAG, "Error in undelete");
+ }
+ }
+ crThreadId.close();
+ }
+ else
+ {
+ Log.d(TAG, "msgHandle not found");
+ }
+ cr.close();
+ }
+
+ private void updateSMSThreadId(String msgHandle, int threadId){
+ ContentValues values = new ContentValues();
+ values.put("thread_id", threadId);
+ context.getContentResolver().update( Uri.parse("content://sms/" + msgHandle), values, null, null);
+ }
+
+ private void deleteSMS(String msgHandle){
+ Cursor cr = context.getContentResolver().query(Uri.parse("content://sms/" + msgHandle), null, null, null, null);
+ if ( cr.moveToFirst()){
+ int threadId = cr.getInt(cr.getColumnIndex(("thread_id")));
+ if ( threadId != DELETED_THREAD_ID){
+ // Move to deleted folder
+ updateSMSThreadId(msgHandle, Integer.valueOf(DELETED_THREAD_ID));
+ } else {
+ // Delete the message permanently
+ mnsClient.addMceInitiatedOperation(msgHandle);
+ context.getContentResolver().delete(Uri.parse("content://sms/" + msgHandle), null, null);
+ }
+ cr.close();
+ }
+ }
+
+ private void unDeleteSMS(String msgHandle){
+
+ Cursor cr = context.getContentResolver().query(Uri.parse("content://sms/" + msgHandle), null, null, null, null );
+
+ if (cr.moveToFirst()){
+
+ // Make sure that the message is in delete folder
+ String currentThreadId = cr.getString(cr.getColumnIndex("thread_id"));
+ if ( currentThreadId != null && Integer.valueOf(currentThreadId) != -1){
+ Log.d(TAG, " Not in delete folder");
+ return;
+ }
+
+ // Fetch the address of the deleted message
+ String address = cr.getString(cr.getColumnIndex("address"));
+
+ // Search the database for the given address
+ Cursor crThreadId = context.getContentResolver().query(Uri.parse("content://sms/"),
+ null, "address = " + address + " AND thread_id != -1", null, null);
+ if (crThreadId.moveToFirst()){
+ // A thread for the given address exists in the database
+ String threadIdStr = crThreadId.getString(crThreadId.getColumnIndex("thread_id"));
+ Log.d(TAG, " THREAD ID " + threadIdStr);
+ updateSMSThreadId(msgHandle, Integer.valueOf(threadIdStr));
+ }
+ else
+ {
+ /* No thread for the given address
+ * Create a fake message to obtain the thread, use that thread_id
+ * and then delete the fake message
+ */
+ ContentValues tempValue = new ContentValues();
+ tempValue.put("address", address);
+ tempValue.put("type", "20");
+ Uri tempUri = context.getContentResolver().insert( Uri.parse("content://sms/"), tempValue);
+
+ if ( tempUri != null ) {
+ String tempMsgId = tempUri.getLastPathSegment();
+ Cursor tempCr = context.getContentResolver().query(tempUri, null, null, null, null);
+ tempCr.moveToFirst();
+ String newThreadIdStr = tempCr.getString(tempCr.getColumnIndex("thread_id"));
+ tempCr.close();
+
+ updateSMSThreadId(msgHandle, Integer.valueOf(newThreadIdStr));
+
+ context.getContentResolver().delete(tempUri, null, null);
+ }
+ }
+ crThreadId.close();
+ }
+ else
+ {
+ Log.d(TAG, "msgHandle not found");
+ }
+ cr.close();
+ }
+
+
+ /**
+ * Sets the message status (read/unread, delete)
+ *
+ * @return Obex response code
+ */
+ public int msgStatus(String name,
+ BluetoothMasAppParams bluetoothMasAppParams) {
+
+ if ((bluetoothMasAppParams.StatusIndicator != 0)
+ && (bluetoothMasAppParams.StatusIndicator != 1)) {
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+
+ if ((bluetoothMasAppParams.StatusValue != 0)
+ && (bluetoothMasAppParams.StatusValue != 1)) {
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+
+ Uri uri = Uri.parse("content://sms/" + name);
+
+ Cursor cr = context.getContentResolver().query(uri, null, null, null,
+ null);
+ if (cr.moveToFirst()) {
+ if (bluetoothMasAppParams.StatusIndicator == 0) {
+ /* Read Status */
+ ContentValues values = new ContentValues();
+ values.put("read", bluetoothMasAppParams.StatusValue);
+ context.getContentResolver().update(uri, values, null, null);
+ } else {
+ if (bluetoothMasAppParams.StatusValue == 1) {
+ deleteSMS(name);
+ } else if (bluetoothMasAppParams.StatusValue == 0) {
+ unDeleteSMS(name);
+ }
+ }
+ return ResponseCodes.OBEX_HTTP_OK;
+ }
+ // MMS MessageStatus processing begins here
+ cr.close();
+ String whereClause = " _id= " + getMmsMsgHndToID(Integer.valueOf(name));
+ uri = Uri.parse("content://mms/");
+ if(getMmsMsgHndToID(Integer.valueOf(name)) > 0){
+ cr = context.getContentResolver().query(uri, null, null, null, null);
+ }
+ if (cr.getCount() > 0) {
+ cr.moveToFirst();
+ if (bluetoothMasAppParams.StatusIndicator == 0) {
+ /* Read Status */
+ ContentValues values = new ContentValues();
+ values.put("read", bluetoothMasAppParams.StatusValue);
+ int rowUpdate = context.getContentResolver().update(uri,
+ values, whereClause, null);
+ Log.d(TAG, "\nRows updated => " + Integer.toString(rowUpdate));
+ return ResponseCodes.OBEX_HTTP_OK;
+ } else {
+ if (bluetoothMasAppParams.StatusValue == 1) {
+ deleteMMS(Integer.toString(getMmsMsgHndToID(Integer.valueOf(name))));
+ } else if (bluetoothMasAppParams.StatusValue == 0) {
+ unDeleteMMS(Integer.toString(getMmsMsgHndToID(Integer.valueOf(name))));
+ }
+
+ return ResponseCodes.OBEX_HTTP_OK;
+ }
+ }
+ cr.close();
+
+ //Email
+ //Query the mailbox table to get the id values for Inbox and Trash folder
+ Uri uri1 = Uri.parse("content://com.android.email.provider/mailbox");
+
+ Cursor cr1 = context.getContentResolver().query(uri1, null,
+ "(UPPER(displayName) = 'INBOX' OR UPPER(displayName) LIKE '%TRASH%')", null, null);
+ int inboxFolderId = 0;
+ int deletedFolderId = 0;
+ int msgFolderId = 0;
+ String folderName;
+ if (cr1.getCount() > 0) {
+ cr1.moveToFirst();
+ do {
+ folderName = cr1.getString(cr1.getColumnIndex("displayName"));
+ if(folderName.equalsIgnoreCase("INBOX")){
+ inboxFolderId = cr1.getInt(cr1.getColumnIndex("_id"));
+ }
+ else{
+ deletedFolderId = cr1.getInt(cr1.getColumnIndex("_id"));
+ }
+ } while ( cr1.moveToNext());
+ }
+
+
+ //Query the message table for the given message id
+ int emailMsgId = 0;
+ Log.d(TAG, "\n name ::=> " + (Integer.valueOf(name)));
+ emailMsgId = (Integer.valueOf(name) - EMAIL_HDLR_CONSTANT);
+
+ Uri uri2 = Uri.parse("content://com.android.email.provider/message/"+emailMsgId);
+ cr = context.getContentResolver().query(uri2, null, null, null,
+ null);
+ if (cr.moveToFirst()) {
+
+ if (bluetoothMasAppParams.StatusIndicator == 0) {
+ /* Read Status */
+ ContentValues values = new ContentValues();
+ values.put("flagRead", bluetoothMasAppParams.StatusValue);
+ context.getContentResolver().update(uri2, values, null, null);
+ }
+ else {
+ if (bluetoothMasAppParams.StatusValue == 1) { //if the email is deleted
+ msgFolderId = cr.getInt(cr.getColumnIndex("mailboxKey"));
+ if(msgFolderId == deletedFolderId){
+ //TODO need to add notification for deleted email here
+ mnsClient.addMceInitiatedOperation(name);
+ context.getContentResolver().delete(
+ Uri.parse("content://com.android.email.provider/message/"
+ + emailMsgId), null, null);
+ }
+ else{
+ ContentValues values = new ContentValues();
+ values.put("mailboxKey", deletedFolderId);
+ context.getContentResolver().update(uri2, values, null, null);
+ }
+ }
+ else{ // if the email is undeleted
+ ContentValues values = new ContentValues();
+ values.put("mailboxKey", inboxFolderId);
+ context.getContentResolver().update(uri2, values, null, null);
+ }
+ }
+ return ResponseCodes.OBEX_HTTP_OK;
+ }
+
+
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+ /**
+ * Sets the message update
+ *
+ * @return Obex response code
+ */
+ public int msgUpdate(String name,
+ BluetoothMasAppParams bluetoothMasAppParams) {
+
+ if ((bluetoothMasAppParams.StatusIndicator != 0)
+ && (bluetoothMasAppParams.StatusIndicator != 1)) {
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+
+ if ((bluetoothMasAppParams.StatusValue != 0)
+ && (bluetoothMasAppParams.StatusValue != 1)) {
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+
+ Uri uri = Uri.parse("content://sms/" + name);
+
+ Cursor cr = context.getContentResolver().query(uri, null, null, null,
+ null);
+ if (cr.moveToFirst()) {
+ if (bluetoothMasAppParams.StatusIndicator == 0) {
+ /* Read Status */
+ ContentValues values = new ContentValues();
+ values.put("read", bluetoothMasAppParams.StatusValue);
+ context.getContentResolver().update(uri, values, null, null);
+ } else {
+ if (bluetoothMasAppParams.StatusValue == 1) {
+ deleteSMS(name);
+ } else if (bluetoothMasAppParams.StatusValue == 0) {
+ unDeleteSMS(name);
+ }
+ }
+ return ResponseCodes.OBEX_HTTP_OK;
+ }
+ // MMS MessageStatus processing begins here
+ cr.close();
+ String whereClause = " _id= " + getMmsMsgHndToID(Integer.valueOf(name));
+ uri = Uri.parse("content://mms/");
+ if(getMmsMsgHndToID(Integer.valueOf(name)) > 0){
+ cr = context.getContentResolver().query(uri, null, null, null, null);
+ }
+ if (cr.getCount() > 0) {
+ cr.moveToFirst();
+ if (bluetoothMasAppParams.StatusIndicator == 0) {
+ /* Read Status */
+ ContentValues values = new ContentValues();
+ values.put("read", bluetoothMasAppParams.StatusValue);
+ int rowUpdate = context.getContentResolver().update(uri,
+ values, whereClause, null);
+ Log.d(TAG, "\nRows updated => " + Integer.toString(rowUpdate));
+ return ResponseCodes.OBEX_HTTP_OK;
+ } else {
+ if (bluetoothMasAppParams.StatusValue == 1) {
+ deleteMMS(Integer.toString(getMmsMsgHndToID(Integer.valueOf(name))));
+ } else if (bluetoothMasAppParams.StatusValue == 0) {
+ unDeleteMMS(Integer.toString(getMmsMsgHndToID(Integer.valueOf(name))));
+ }
+
+ return ResponseCodes.OBEX_HTTP_OK;
+ }
+ }
+ cr.close();
+
+ //Email
+ //Query the mailbox table to get the id values for Inbox and Trash folder
+ Uri uri1 = Uri.parse("content://com.android.email.provider/mailbox");
+
+ Cursor cr1 = context.getContentResolver().query(
+ uri1, null, "(UPPER(displayName) = 'INBOX' OR UPPER(displayName) LIKE '%TRASH%')", null, null);
+ int inboxFolderId = 0;
+ int deletedFolderId = 0;
+ int msgFolderId = 0;
+ String folderName;
+ if (cr1.getCount() > 0) {
+ cr1.moveToFirst();
+ do {
+ folderName = cr1.getString(cr1.getColumnIndex("displayName"));
+ if(folderName.equalsIgnoreCase("INBOX")){
+ inboxFolderId = cr1.getInt(cr1.getColumnIndex("_id"));
+ }
+ else{
+ deletedFolderId = cr1.getInt(cr1.getColumnIndex("_id"));
+ }
+ } while ( cr1.moveToNext());
+ }
+
+
+ //Query the message table for the given message id
+ int emailMsgId = 0;
+ Log.d(TAG, "\n name ::=> " + (Integer.valueOf(name)));
+ emailMsgId = (Integer.valueOf(name) - EMAIL_HDLR_CONSTANT);
+
+ Uri uri2 = Uri.parse("content://com.android.email.provider/message/"+emailMsgId);
+ cr = context.getContentResolver().query(uri2, null, null, null,
+ null);
+ if (cr.moveToFirst()) {
+
+ if (bluetoothMasAppParams.StatusIndicator == 0) {
+ /* Read Status */
+ ContentValues values = new ContentValues();
+ values.put("flagRead", bluetoothMasAppParams.StatusValue);
+ context.getContentResolver().update(uri2, values, null, null);
+ }
+ else {
+ if (bluetoothMasAppParams.StatusValue == 1) { //if the email is deleted
+ msgFolderId = cr.getInt(cr.getColumnIndex("mailboxKey"));
+ if(msgFolderId == deletedFolderId){
+ //TODO need to add notification for deleted email here
+ mnsClient.addMceInitiatedOperation(name);
+ context.getContentResolver().delete(
+ Uri.parse("content://com.android.email.provider/message/"
+ + emailMsgId), null, null);
+ }
+ else{
+ ContentValues values = new ContentValues();
+ values.put("mailboxKey", deletedFolderId);
+ context.getContentResolver().update(uri2, values, null, null);
+ }
+ }
+ else{ // if the email is undeleted
+ ContentValues values = new ContentValues();
+ values.put("mailboxKey", inboxFolderId);
+ context.getContentResolver().update(uri2, values, null, null);
+ }
+ }
+ return ResponseCodes.OBEX_HTTP_OK;
+ }
+
+
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+
+
+ /**
+ * Enable/disable notification
+ *
+ * @return Obex response code
+ */
+ public int notification(BluetoothDevice remoteDevice,
+ BluetoothMasAppParams bluetoothMasAppParams) {
+ // TODO Auto-generated method stub
+
+ if (bluetoothMasAppParams.Notification == 1) {
+ startMnsSession(remoteDevice);
+ return ResponseCodes.OBEX_HTTP_OK;
+ } else if (bluetoothMasAppParams.Notification == 0) {
+ stopMnsSession(remoteDevice);
+ return ResponseCodes.OBEX_HTTP_OK;
+ }
+
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+
+ }
+
+ private void clearDeletedItems() {
+ // Remove the deleted item entries
+ context.getContentResolver().delete(Uri.parse("content://sms/"),
+ "thread_id = " + DELETED_THREAD_ID, null);
+ context.getContentResolver().delete(Uri.parse("content://mms/"),
+ "thread_id = " + DELETED_THREAD_ID, null);
+ }
+
+ public void disconnect() {
+ clearDeletedItems();
+ }
+
+
+ /**
+ * Start an MNS obex client session and push notification whenever available
+ */
+ public void startMnsSession(BluetoothDevice remoteDevice) {
+ Log.d(TAG, "Start MNS Client");
+ mnsClient.getHandler()
+ .obtainMessage(BluetoothMns.MNS_CONNECT, -1, -1, remoteDevice)
+ .sendToTarget();
+ }
+
+ /**
+ * Stop pushing notifications and disconnect MNS obex session
+ */
+ public void stopMnsSession(BluetoothDevice remoteDevice) {
+ Log.d(TAG, "Stop MNS Client");
+ mnsClient
+ .getHandler()
+ .obtainMessage(BluetoothMns.MNS_DISCONNECT, -1, -1,
+ remoteDevice).sendToTarget();
+ }
+
+ /**
+ * Push an event over MNS client to MNS server
+ */
+ private void sendMnsEvent(String msg, String handle, String folder,
+ String old_folder, String msgType) {
+ Log.d(TAG, "Send MNS Event");
+ mnsClient.sendMnsEvent(msg, handle, folder, old_folder, msgType);
+ }
+
+ /**
+ * Obtain the number of MMS messages
+ */
+ private int getNumMmsMsgs(String name) {
+ int msgCount = 0;
+
+ if ( name.equalsIgnoreCase(Deleted)){
+ Uri uri = Uri.parse("content://mms/");
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, null, "thread_id = " + DELETED_THREAD_ID, null, null);
+ if(cursor != null){
+ msgCount = cursor.getCount();
+ cursor.close();
+ }
+ } else {
+ Uri uri = Uri.parse("content://mms/" + name);
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, null, "thread_id <> " + DELETED_THREAD_ID, null, null);
+ if(cursor != null){
+ msgCount = cursor.getCount();
+ cursor.close();
+ }
+ }
+ return msgCount;
+ }
+
+ /**
+ * Obtain the MMS message Handle
+ */
+
+ private Integer getMmsMsgHnd(int msgID) {
+ int handle = 0;
+ String whereClause = " mid= " + msgID + " AND ct=\"text/plain\"";
+ Uri uri = Uri.parse("content://mms/part");
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, null, whereClause, null, null);
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ int handleInd = cursor.getColumnIndex("_id");
+ handle = cursor.getInt(handleInd);
+ }
+ cursor.close();
+ return handle;
+ }
+
+ /**
+ * Obtain the MMS message ID from Handle
+ */
+
+ private Integer getMmsMsgHndToID(int msgHandle) {
+ int msgID = -1;
+ String whereClause = " mid= " + (msgHandle - MMS_HDLR_CONSTANT);
+ Uri uri = Uri.parse("content://mms/part");
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, null, whereClause, null, null);
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ int handleInd = cursor.getColumnIndex("mid");
+ msgID = cursor.getInt(handleInd);
+ }
+ cursor.close();
+ return msgID;
+ }
+
+ /**
+ * Obtain the MMS message MID list
+ */
+
+ private List<Integer> getMmsMsgMIDs(String whereClause) {
+ List<Integer> idList = new ArrayList<Integer>();
+ Uri uri = Uri.parse("content://mms");
+ ContentResolver cr = context.getContentResolver();
+ Cursor crID = cr.query(uri, null, whereClause, null, null);
+ int idInd = crID.getColumnIndex("_id");
+ if (crID.getCount() != 0) {
+ crID.moveToFirst();
+ do {
+ idList.add(Integer.valueOf(crID.getInt(idInd)));
+ } while (crID.moveToNext());
+ }
+
+ crID.close();
+ return idList;
+ }
+
+ /**
+ * Build a whereclause for MMS filtering
+ */
+
+ private String bldMmsWhereClause(BluetoothMasAppParams appParams,
+ int foldertype) {
+
+ String whereClause = "";
+ if ( foldertype != 0) {
+ // Inbox, Outbox, Sent, Draft folders
+ whereClause = "msg_box=" + foldertype + " AND thread_id <> " + DELETED_THREAD_ID;
+ } else {
+ // Deleted folder
+ whereClause = "thread_id = " + DELETED_THREAD_ID;
+ }
+
+ /* Filter readstatus: 0 no filtering, 0x01 get unread, 0x10 get read */
+ if (appParams.FilterReadStatus != 0) {
+ if ((appParams.FilterReadStatus & 0x1) != 0) {
+ if (whereClause != "") {
+ whereClause += " AND ";
+ }
+ whereClause += " read=0 ";
+ }
+ if ((appParams.FilterReadStatus & 0x10) != 0) {
+ if (whereClause != "") {
+ whereClause += " AND ";
+ }
+ whereClause += " read=1 ";
+ }
+ }
+
+ /* Filter Period Begin */
+ if ((appParams.FilterPeriodBegin != null)
+ && (appParams.FilterPeriodBegin.length() > 0)) {
+ Time time = new Time();
+ try {
+ time.parse(appParams.FilterPeriodBegin);
+ if (whereClause != "") {
+ whereClause += " AND ";
+ }
+ whereClause += "date >= " + (time.toMillis(false))/1000;
+ } catch (TimeFormatException e) {
+ Log.d(TAG, "Bad formatted FilterPeriodBegin "
+ + appParams.FilterPeriodBegin);
+ }
+ }
+
+ /* Filter Period End */
+ if ((appParams.FilterPeriodEnd != null)
+ && (appParams.FilterPeriodEnd.length() > 0)) {
+ Time time = new Time();
+ try {
+ time.parse(appParams.FilterPeriodEnd);
+ if (whereClause != "") {
+ whereClause += " AND ";
+ }
+ whereClause += "date < " + (time.toMillis(false))/1000;
+ } catch (TimeFormatException e) {
+ Log.d(TAG, "Bad formatted FilterPeriodEnd "
+ + appParams.FilterPeriodEnd);
+ }
+ }
+
+ return whereClause;
+ }
+
+ /**
+ * Obtain the MMS msg_box id
+ */
+
+ private int getMmsMsgBox(int msgID) {
+ int val = -1;
+ String whereClause = " _id= " + msgID;
+ Uri uri = Uri.parse("content://mms/");
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, null, whereClause, null, null);
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ int msgBoxInd = cursor.getColumnIndex("msg_box");
+ val = cursor.getInt(msgBoxInd);
+ }
+ cursor.close();
+ return val;
+ }
+
+ /**
+ * Obtain MMS message text
+ */
+
+ private String getMmsMsgTxt(int msgID) {
+ String text = null;
+ String whereClause = " mid= " + msgID + " AND ct=\"text/plain\"";
+ Uri uri = Uri.parse("content://mms/part");
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, null, whereClause, null, null);
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ int textInd = cursor.getColumnIndex("text");
+ text = cursor.getString(textInd);
+ }
+ cursor.close();
+ return text;
+ }
+
+ /**
+ * Obtain the MMS message Subject
+ */
+
+ private String getMmsMsgSubject(int msgID) {
+ String text = null;
+ String whereClause = " _id= " + msgID;
+ Uri uri = Uri.parse("content://mms/");
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, null, whereClause, null, null);
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ int subjectInd = cursor.getColumnIndex("sub");
+ text = cursor.getString(subjectInd);
+ }
+ cursor.close();
+ return text;
+ }
+
+ /**
+ * Obtain the MMS message Date
+ */
+
+ private String getMmsMsgDate(int msgID) {
+ String text = null;
+ String whereClause = " _id= " + msgID;
+ Uri uri = Uri.parse("content://mms/");
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, null, whereClause, null, null);
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ int dateInd = cursor.getColumnIndex("date");
+ text = cursor.getString(dateInd);
+ }
+ cursor.close();
+ return text;
+
+ }
+
+ /**
+ * Obtain the MMS attachment size
+ */
+
+ private int getMmsMsgAttachSize(int msgID) {
+ int attachSize = 0;
+ String whereClause = " _id= " + msgID;
+ Uri uri = Uri.parse("content://mms/");
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, null, whereClause, null, null);
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ int sizeInd = cursor.getColumnIndex("m_size");
+ attachSize = cursor.getInt(sizeInd);
+ }
+ cursor.close();
+ return attachSize;
+
+ }
+
+ /**
+ * Obtain the MMS message read status
+ */
+
+ private String getMmsMsgReadStatus(int msgID) {
+ String text = null;
+ String whereClause = " _id= " + msgID;
+ Uri uri = Uri.parse("content://mms/");
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, null, whereClause, null, null);
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ int readInd = cursor.getColumnIndex("read");
+ if (cursor.getInt(readInd) == 0) {
+ text = "no";
+ } else {
+ text = "yes";
+ }
+ }
+ cursor.close();
+ return text;
+
+ }
+
+ /**
+ * Obtain the MMS message read sent
+ */
+
+ private String getMmsMsgReadSent(int msgID) {
+ String text = null;
+ if ( getMmsMsgBox(msgID) == 2 ) {
+ // Sent folder
+ text = "yes";
+ } else {
+ text = "no";
+ }
+ return text;
+ }
+
+ /**
+ * Obtain the MMS message priority
+ */
+
+ private String getMmsMsgPriority(int msgID) {
+ final int PRIORITY_LOW = 0X80;
+ final int PRIORITY_NORMAL = 0X81;
+ final int PRIORITY_HIGH = 0X82;
+
+ String text = null;
+ String whereClause = " _id= " + msgID;
+ Uri uri = Uri.parse("content://mms/");
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, null, whereClause, null, null);
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ int priInd = cursor.getColumnIndex("pri");
+
+ switch (cursor.getInt(priInd)) {
+ case PRIORITY_LOW:
+ text = "no";
+ break;
+ case PRIORITY_NORMAL:
+ text = "no";
+ break;
+ case PRIORITY_HIGH:
+ text = "yes";
+ break;
+
+ default:
+ text = "no";
+ break;
+ }
+ }
+
+ cursor.close();
+ return text;
+
+ }
+
+ /**
+ * Obtain the MMS message read protected
+ */
+
+ private String getMmsMsgProtected(int msgID) {
+ String text = null;
+ String whereClause = " _id= " + msgID;
+ Uri uri = Uri.parse("content://mms/");
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, null, whereClause, null, null);
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ int readInd = cursor.getColumnIndex("locked");
+ if (cursor.getInt(readInd) == 0) {
+ text = "no";
+ } else {
+ text = "yes";
+ }
+ }
+ cursor.close();
+ return text;
+
+ }
+
+ /**
+ * Obtain MMS message address
+ */
+
+ private String getMmsMsgAddress(int msgID) {
+ String text = null;
+ String whereClause = " address != \"insert-address-token\"";
+ Uri uri = Uri.parse("content://mms/" + msgID + "/addr");
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, null, whereClause, null, null);
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ int addressInd = cursor.getColumnIndex("address");
+ text = cursor.getString(addressInd);
+ }
+ cursor.close();
+ return text;
+ }
+
+ /**
+ * Get the folder name (MAP representation) based on the message Handle
+ */
+ private int getMmsContainingFolder(int msgID) {
+ int folderNum = 0;
+ String whereClause = " _id= " + msgID;
+ Uri uri = Uri.parse("content://mms/");
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, null, whereClause, null, null);
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ int msgboxInd = cursor.getColumnIndex("msg_box");
+ String thread_id = cursor.getString(cursor.getColumnIndex("thread_id"));
+ if ( Integer.valueOf(thread_id) == DELETED_THREAD_ID) {
+ // Deleted folder
+ folderNum = 0;
+ } else {
+ folderNum = cursor.getInt(msgboxInd);
+ }
+ }
+ cursor.close();
+ return folderNum;
+ }
+
+ /**
+ * Get MMS folder name based on value Inbox = 1 Sent = 2 Drafts = 3 Outbox =
+ * 4 Queued = 6
+ *
+ */
+ private String getMmsMapVirtualFolderName(int type) {
+ String folderName = null;
+
+ switch (type) {
+ case 0:
+ folderName = Deleted;
+ break;
+ case 1:
+ folderName = Inbox;
+ break;
+ case 2:
+ folderName = Sent;
+ break;
+ case 3:
+ folderName = Draft;
+ break;
+ case 4: // outbox
+ case 5: // failed
+ case 6: // queued
+ folderName = Outbox;
+ break;
+
+ default:
+ break;
+ }
+ return folderName;
+ }
+
+
+ /**
+ * Build an MMS bMessage when given a message handle
+ */
+ private String bldMmsBmsg(int msgID, BluetoothMasMessageRsp rsp) {
+ String bMessage = null;
+ Cursor cr = null;
+ Uri uri = Uri.parse("content://mms/");
+ String whereClause = " _id = " + msgID;
+ cr = context.getContentResolver().query(uri, null, whereClause, null,
+ null);
+
+ if (cr.getCount() > 0) {
+ cr.moveToFirst();
+ String containingFolder = getMmsMapVirtualFolderName((getMmsContainingFolder(msgID)));
+ BmessageConsts bmsg = new BmessageConsts();
+
+ // Create a bMessage
+
+ // TODO Get Current type
+ bmsg.setType("MMS");
+
+ bmsg.setBmsg_version("1.0");
+ if (cr.getString(cr.getColumnIndex("read")).equalsIgnoreCase("1")) {
+ bmsg.setStatus("READ");
+ } else {
+ bmsg.setStatus("UNREAD");
+ }
+
+ bmsg.setFolder("TELECOM/MSG/" + containingFolder);
+
+ bmsg.setVcard_version("2.1");
+ VcardContent vcard = getVcardContent(getMmsMsgAddress(msgID));
+ String type = cr.getString(cr.getColumnIndex("msg_box"));
+ // Inbox is type 1.
+ if (type.equalsIgnoreCase("1")) {
+ bmsg.setOriginatorVcard_name(vcard.name);
+ bmsg.setOriginatorVcard_phone_number(vcard.tel);
+ bmsg.setRecipientVcard_name(getOwnerName());
+ bmsg.setRecipientVcard_phone_number(getOwnerNumber());
+ } else {
+ bmsg.setRecipientVcard_name(vcard.name);
+ bmsg.setRecipientVcard_phone_number(vcard.tel);
+ bmsg.setOriginatorVcard_name(getOwnerName());
+ bmsg.setOriginatorVcard_phone_number(getOwnerNumber());
+
+ }
+
+ StringBuilder sb = new StringBuilder();
+ Date date = new Date(Integer.valueOf(getMmsMsgDate(msgID)));
+ sb.append("Date: ").append(date.toString()).append("\r\n");
+
+ boolean NO_MIME = false;
+ boolean MIME = true;
+ boolean msgFormat = NO_MIME;
+ sb.append(bldMMSBody(bmsg, msgFormat, msgID));
+ bmsg.setBody_msg(sb.toString());
+ bmsg.setBody_length(sb.length() + 22);
+ bmsg.setBody_encoding("8BIT");
+
+ // Send a bMessage
+ String str = mu.toBmessageMMS(bmsg);
+ Log.d(TAG, str);
+ Log.d(TAG, "\n\n");
+
+ if (str != null && (str.length() > 0)) {
+ final String FILENAME = "message";
+ FileOutputStream bos = null;
+ File file = new File(context.getFilesDir() + "/" + FILENAME);
+ file.delete();
+
+ try {
+ bos = context
+ .openFileOutput(FILENAME, Context.MODE_PRIVATE);
+
+ bos.write(str.getBytes());
+ bos.flush();
+ bos.close();
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ File fileR = new File(context.getFilesDir() + "/" + FILENAME);
+ if (fileR.exists() == true) {
+ rsp.file = fileR;
+ rsp.fractionDeliver = 1;
+ }
+
+ }
+ }
+ return bMessage;
+ }
+
+ private boolean isOutgoingMMSMessage( int mmsMsgID) {
+ if ( getMmsMsgBox(mmsMsgID) == 1 ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * This method constructs an MMS message that is added to the message list
+ * which is used to construct a message listing
+ */
+
+ private MsgListingConsts bldMmsMsgLstItem(int mmsMsgID,
+ BluetoothMasAppParams appParams, String folderName, String datetimeStr) {
+
+ MsgListingConsts ml = new MsgListingConsts();
+
+ // Set the message handle
+ ml.setMsg_handle((mmsMsgID + MMS_HDLR_CONSTANT));
+
+ // Set the message subject
+ if ((appParams.ParameterMask & BIT_SUBJECT) != 0) {
+ ml.setSubject(getMmsMsgSubject(mmsMsgID));
+ ml.sendSubject = true;
+ }
+
+ // Construct datetime value
+ if ((appParams.ParameterMask & BIT_DATETIME) != 0) {
+ ml.setDatetime(datetimeStr);
+ }
+
+ // Construct msg body
+ if ((appParams.ParameterMask & BIT_TEXT) != 0) {
+ if ((getMmsMsgTxt(mmsMsgID) != null)) {
+ ml.setContains_text("yes");
+ } else {
+ ml.setContains_text("no");
+ }
+
+ }
+
+ // Set text size
+ if ((appParams.ParameterMask & BIT_SIZE) != 0) {
+ if (getMmsMsgTxt(mmsMsgID) == null) {
+ ml.setSize(0);
+ } else {
+
+ ml.setSize(getMmsMsgTxt(mmsMsgID).length());
+
+ }
+ }
+
+ // Set message type
+ if ((appParams.ParameterMask & BIT_TYPE) != 0) {
+ ml.setType("MMS");
+ }
+
+ if ((appParams.ParameterMask & BIT_RECIPIENT_NAME) != 0) {
+ // TODO "recipient_name" is the name of the
+ // recipient of the message, when it is known
+ // by the MSE device.
+ String recipientName = null;
+ if (isOutgoingMMSMessage(mmsMsgID) == false) {
+ recipientName = getOwnerName();
+ } else {
+ recipientName = getContactName(getMmsMsgAddress(mmsMsgID));
+ }
+ ml.setRecepient_name(recipientName);
+ }
+
+ if ((appParams.ParameterMask & BIT_RECIPIENT_ADDRESSING) != 0) {
+ // TODO In case of a SMS this is the recipient's
+ // phone
+ // number in canonical form (chapter 2.4.1 of [5])
+ String recipientAddressing = null;
+ if (isOutgoingMMSMessage(mmsMsgID) == false) {
+ recipientAddressing = getOwnerNumber();
+ } else {
+ recipientAddressing = getMmsMsgAddress(mmsMsgID);
+ }
+ ml.setRecepient_addressing(recipientAddressing);
+ ml.setSendRecipient_addressing(true);
+ }
+
+ if ((appParams.ParameterMask & BIT_SENDER_NAME) != 0) {
+ String senderName = null;
+ if (isOutgoingMMSMessage(mmsMsgID) == true) {
+ senderName = getOwnerName();
+ } else {
+ senderName = getContactName(getMmsMsgAddress(mmsMsgID));
+ }
+ ml.setSender_name(senderName);
+ }
+
+ if ((appParams.ParameterMask & BIT_SENDER_ADDRESSING) != 0) {
+ String senderAddressing = null;
+ if (isOutgoingMMSMessage(mmsMsgID) == true) {
+ senderAddressing = getOwnerNumber();
+ } else {
+ senderAddressing = getMmsMsgAddress(mmsMsgID);
+ }
+ ml.setSender_addressing(senderAddressing);
+ }
+
+ // Set read status
+ if ((appParams.ParameterMask & BIT_READ) != 0) {
+ if (getMmsMsgReadStatus(mmsMsgID).equalsIgnoreCase("yes")) {
+ ml.setRead("yes");
+ } else {
+ ml.setRead("no");
+ }
+ }
+
+ // Set priority
+ if ((appParams.ParameterMask & BIT_PRIORITY) != 0) {
+ ml.setPriority(getMmsMsgPriority(mmsMsgID));
+ }
+
+ // Set Protected
+ if ((appParams.ParameterMask & BIT_PROTECTED) != 0) {
+ ml.setMsg_protected(getMmsMsgProtected(mmsMsgID));
+ }
+
+ // Set sent
+ if ((appParams.ParameterMask & BIT_SENT) != 0) {
+ ml.setSent(getMmsMsgReadSent(mmsMsgID));
+ }
+
+ // Set reception status
+ if ((appParams.ParameterMask & BIT_RECEPTION_STATUS) != 0) {
+ ml.setReception_status("complete");
+ }
+
+ // Set attachment size
+ if ((appParams.ParameterMask & BIT_SENT) != 0) {
+ ml.setAttachment_size(getMmsMsgAttachSize(mmsMsgID));
+ }
+
+ return ml;
+ }
+
+ /**
+ * Get the type (as in Mms ContentProvider) for the given table name
+ */
+ private int getMMSFolderType(String folder) {
+ int type = 0;
+ if (folder.equalsIgnoreCase(Inbox)) {
+ type = 1;
+ } else if (folder.equalsIgnoreCase(Sent)) {
+ type = 2;
+ } else if (folder.equalsIgnoreCase(Drafts)) {
+ type = 3;
+ } else if (folder.equalsIgnoreCase(Outbox)) {
+ type = 4;
+ } else if (folder.equalsIgnoreCase(Failed)) {
+ type = 5;
+ } else if (folder.equalsIgnoreCase(Queued)) {
+ type = 6;
+ }
+ // Deleted will be folder 0
+ return type;
+ }
+ /**
+ * This method is used to take an MMS in the drafts folder and move it to
+ * the outbox This action is required to add the MMS to the pending_msgs
+ * table which is used to send the MMS out to the network
+ */
+
+ private void moveMMSfromDraftstoOutbox() {
+
+ String handle = null;
+
+ // scan drafts folder for an MMS to send
+ // fetch the message handle
+ Uri uri = Uri.parse("content://mms/drafts");
+ ContentResolver cr = context.getContentResolver();
+ Cursor crID = cr.query(uri, null, null, null, null);
+ if (crID.getCount() > 0) {
+ crID.moveToFirst();
+ int msgIDInd = crID.getColumnIndex("_id");
+ handle = crID.getString(msgIDInd);
+ }
+
+ if (handle != null) {
+ String whereClause = " _id= " + handle;
+ uri = Uri.parse("content://mms");
+ crID = cr.query(uri, null, whereClause, null, null);
+ if (crID.getCount() > 0) {
+ crID.moveToFirst();
+ ContentValues values = new ContentValues();
+ values.put("msg_box", 4);
+ cr.update(uri, values, whereClause, null);
+ }
+ }
+ }
+ /**
+ * This method is used to take a Bmessage that was pushed and move it to the
+ * folder
+ */
+ private void moveMMStoDrafts(String mmsMsg) {
+
+ String folder = "drafts";
+ BmessageConsts bMsg = mu.fromBmessageMMS(mmsMsg);
+ String Address = bMsg.getRecipientVcard_phone_number();
+ String MmsText = bMsg.getBody_msg();
+
+ /**
+ * The PTS tester does not contain the same message format as CE4A This
+ * code /* looks at the pushed message and checks for RPI-Messaging. If
+ * it does not /* find it then it then it assumes PTS tester format
+ */
+
+ int beginPos = MmsText.indexOf("Content-Disposition:inline")
+ + "Content-Disposition:inline".length();
+ int endPos = MmsText.indexOf("--RPI-Messaging", beginPos);
+
+ if (endPos < 0) {
+ beginPos = 0;
+ endPos = MmsText.length();
+ }
+
+ MmsText = (MmsText.substring(beginPos, endPos)).trim();
+
+ ContentValues values = new ContentValues();
+ values.put("msg_box", 3);
+ values.put("thread_id", createMMSThread(Address));
+ // function that creates a thread ID
+ values.put("read", 0);
+ values.put("seen", 0);
+ values.put("sub_cs", 106);
+ values.put("ct_t", "application/vnd.wap.multipart.related");
+ values.put("exp", 604800);
+ values.put("m_cls", "personal");
+ values.put("m_type", 128);
+ values.put("v", 18);
+ values.put("pri", 129);
+ values.put("rr", 129);
+ values.put("tr_id", "T12dc2e87182");
+ values.put("d_rpt", 129);
+ values.put("locked", 0);
+
+ Uri uri = Uri.parse("content://mms/" + folder);
+ ContentResolver cr = context.getContentResolver();
+ uri = cr.insert(uri, values);
+
+ String msgNum = uri.getLastPathSegment();
+ int msgID = Integer.parseInt(msgNum);
+ Log.d(TAG, " NEW URI " + uri.toString());
+
+ // Build the \mms\part portion
+ values.clear();
+
+ values.put("seq", -1);
+ values.put("ct", "application/smil");
+ values.put("cid", "<smil>");
+ values.put("cl", "smil.xml");
+ values.put(
+ "text",
+ "<smil><head><layout><root-layout width=\"320px\" height=\"480px\"/><region id=\"Text\" left=\"0\" top=\"320\" width=\"320px\" height=\"160px\" fit=\"meet\"/></layout></head><body><par dur=\"5000ms\"><text src=\"text_0.txt\" region=\"Text\"/></par></body></smil>");
+
+ uri = Uri.parse("content://mms/" + msgID + "/part");
+ uri = cr.insert(uri, values);
+ Log.d(TAG, " NEW URI " + uri.toString());
+
+ values.clear();
+ values.put("seq", 0);
+ values.put("ct", "text/plain");
+ values.put("name", "null");
+ values.put("chset", 106);
+ values.put("cd", "null");
+ values.put("fn", "null");
+ values.put("cid", "<smil>");
+ values.put("cl", "text_0.txt");
+ values.put("ctt_s", "null");
+ values.put("ctt_t", "null");
+ values.put("_data", "null");
+ values.put("text", MmsText);
+
+ uri = Uri.parse("content://mms/" + msgID + "/part");
+ uri = cr.insert(uri, values);
+ Log.d(TAG, " NEW URI " + uri.toString());
+
+ values.clear();
+ values.put("contact_id", "null");
+ values.put("address", "insert-address-token");
+ values.put("type", 137);
+ values.put("charset", 106);
+
+ uri = Uri.parse("content://mms/" + msgID + "/addr");
+ uri = cr.insert(uri, values);
+ Log.d(TAG, " NEW URI " + uri.toString());
+
+ values.clear();
+ values.put("contact_id", "null");
+ values.put("address", Address);
+ values.put("type", 151);
+ values.put("charset", 106);
+
+ uri = Uri.parse("content://mms/" + msgID + "/addr");
+ uri = cr.insert(uri, values);
+ Log.d(TAG, " NEW URI " + uri.toString());
+
+ }
+ /**
+ * Method to construct body of bmessage using either MIME or no MIME
+ *
+ */
+ private String bldMMSBody(BmessageConsts bMsg, boolean msgType, int msgID) {
+ boolean MIME = true;
+ StringBuilder sb = new StringBuilder();
+ String retString = null;
+ if (msgType == MIME) {
+ sb.append("To:").append(bMsg.recipient_vcard_phone_number)
+ .append("\r\n");
+ sb.append("Mime-Version: 1.0").append("\r\n");
+ sb.append(
+ "Content-Type: multipart/mixed; boundary=\"RPI-Messaging.42156.0\"")
+ .append("\r\n");
+ sb.append("Content-Transfer-Encoding: 7bit").append("\r\n")
+ .append("\r\n");
+ sb.append("MIME Message").append("\r\n");
+ sb.append("--RPI-Messaging.42156.0").append("\r\n");
+ sb.append("Content-Type: text/plain").append("\r\n");
+ sb.append("Content-Transfer-Encoding: 8bit").append("\r\n");
+ sb.append("Content-Disposition:inline").append("\r\n")
+ .append("\r\n");
+ sb.append(getMmsMsgTxt(msgID)).append("\r\n");
+ sb.append("--RPI-Messaging.42156.0--").append("\r\n")
+ .append("\r\n");
+ retString = sb.toString();
+
+ } else {
+ sb.append("Subject:").append("Not Implemented").append("\r\n");
+ sb.append("From:").append(bMsg.originator_vcard_phone_number)
+ .append("\r\n");
+ sb.append(getMmsMsgTxt(msgID)).append("\r\n").append("\r\n");
+ retString = sb.toString();
+ }
+ return retString;
+ }
+ /**
+ * Method to create a thread for a pushed MMS message
+ *
+ */
+
+ private int createMMSThread(String address) {
+ int returnValue = 1;
+ ContentValues tempValue = new ContentValues();
+ tempValue.put("address", address);
+ tempValue.put("type", "20");
+ Uri tempUri = context.getContentResolver().insert(
+ Uri.parse("content://sms/"), tempValue);
+
+ if (tempUri != null) {
+ Cursor tempCr = context.getContentResolver().query(tempUri, null,
+ null, null, null);
+ tempCr.moveToFirst();
+ String newThreadIdStr = tempCr.getString(tempCr
+ .getColumnIndex("thread_id"));
+ tempCr.close();
+ returnValue = Integer.valueOf(newThreadIdStr);
+
+ context.getContentResolver().delete(tempUri, null, null);
+ }
+
+ return returnValue;
+ }
+
+}
diff --git a/src/com/android/bluetooth/map/BluetoothMasAppParams.java b/src/com/android/bluetooth/map/BluetoothMasAppParams.java
new file mode 100644
index 000000000..8d30f6b35
--- /dev/null
+++ b/src/com/android/bluetooth/map/BluetoothMasAppParams.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map;
+
+
+public final class BluetoothMasAppParams {
+
+ public int MaxListCount;
+ public int ListStartOffset;
+ public short SubjectLength;
+ public long ParameterMask;
+ public byte FilterMessageType;
+ public byte FilterReadStatus;
+ public byte FilterPriority;
+ public String FilterPeriodBegin;
+ public String FilterPeriodEnd;
+ public String FilterRecipient;
+ public String FilterOriginator;
+ public byte FractionRequest;
+ public byte Charset;
+ public byte Attachment;
+ public byte Retry;
+ public byte Transparent;
+ public byte StatusIndicator;
+ public byte StatusValue;
+ public byte Notification;
+
+};
diff --git a/src/com/android/bluetooth/map/BluetoothMasMsg.java b/src/com/android/bluetooth/map/BluetoothMasMsg.java
new file mode 100644
index 000000000..31d5c59b1
--- /dev/null
+++ b/src/com/android/bluetooth/map/BluetoothMasMsg.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map;
+
+import java.io.File;
+
+public class BluetoothMasMsg {
+
+ byte fractionDeliver;
+ File file;
+
+}
diff --git a/src/com/android/bluetooth/map/BluetoothMasObexServer.java b/src/com/android/bluetooth/map/BluetoothMasObexServer.java
new file mode 100644
index 000000000..a24a91235
--- /dev/null
+++ b/src/com/android/bluetooth/map/BluetoothMasObexServer.java
@@ -0,0 +1,1122 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Message;
+import android.text.format.Time;
+import android.util.Log;
+
+import javax.obex.*;
+
+
+import com.android.bluetooth.map.BluetoothMasAppIf.BluetoothMasMessageListingRsp;
+import com.android.bluetooth.map.BluetoothMasAppIf.BluetoothMasMessageRsp;
+import com.android.bluetooth.map.BluetoothMasAppIf.BluetoothMasPushMsgRsp;
+
+
+public class BluetoothMasObexServer extends ServerRequestHandler {
+
+ private static final String TAG = "BluetoothMasObexServer";
+
+ private static final boolean D = BluetoothMasService.DEBUG;
+
+ private static final boolean V = BluetoothMasService.VERBOSE;
+
+ private static final int UUID_LENGTH = 16;
+
+ // type for list folder contents
+ private static final String TYPE_LISTING = "x-obex/folder-listing";
+ private static final String TYPE_MESSAGE_LISTING = "x-bt/MAP-msg-listing";
+ private static final String TYPE_MESSAGE = "x-bt/message";
+ private static final String TYPE_MESSAGE_STATUS = "x-bt/messageStatus";
+ private static final String TYPE_MESSAGE_UPDATE = "x-bt/MAP-messageUpdate";
+ private static final String TYPE_MESSAGE_NOTIFICATION = "x-bt/MAP-NotificationRegistration";
+
+ public long mConnectionId;
+
+ private Handler mCallback = null;
+
+ public Context mContext;
+
+ public static boolean sIsAborted = false;
+
+ public enum MasState {
+ MAS_SERVER_CONNECTING,
+ MAS_SERVER_DISCONNECTING,
+ MAS_SERVER_CONNECTED,
+ MAS_SERVER_DISCONNECTED,
+ MAS_SERVER_SET_FOLDER,
+ MAS_SERVER_GET_FILE_PENDING,
+ MAS_SERVER_BROWSE_FOLDER_PENDING,
+ MAS_SERVER_BROWSE_FOLDER,
+ MAS_SERVER_GET_MSG_LIST_PENDING,
+ MAS_SERVER_GET_MSG_LIST,
+ MAS_SERVER_GET_MSG_PENDING,
+ MAS_SERVER_GET_MSG,
+ MAS_SERVER_SET_MSG_STATUS,
+ MAS_SERVER_SET_NOTIFICATION_REG,
+ MAS_SERVER_UPDATE_INBOX,
+ MAS_SERVER_PUSH_MESSAGE
+ };
+ private static MasState state = MasState.MAS_SERVER_DISCONNECTED;
+
+ // 128 bit UUID for MAS
+ private static final byte[] MAS_TARGET = new byte[] {
+ (byte)0xbb, (byte)0x58, (byte)0x2b, (byte)0x40, (byte)0x42, (byte)0x0c, (byte)0x11, (byte)0xdb,
+ (byte)0xb0, (byte)0xde, (byte)0x08, (byte)0x00, (byte)0x20, (byte)0x0c, (byte)0x9a, (byte)0x66
+ };
+
+ private BluetoothMasAppIf appIf;
+
+ private BluetoothDevice mRemoteDevice;
+
+ private class MasAppParamsStore {
+
+ private BluetoothMasAppParams appParams = null;
+
+ public final void clear() {
+ if (D) Log.d(TAG, "Clear AppParams : Enter");
+ appParams.MaxListCount = BluetoothMasSpecParams.MAS_DEFAULT_MAX_LIST_COUNT;
+ appParams.ListStartOffset = 0;
+ appParams.SubjectLength = BluetoothMasSpecParams.MAS_DEFAULT_SUBJECT_LENGTH;
+ appParams.ParameterMask = BluetoothMasSpecParams.MAS_DEFAULT_PARAMETER_MASK;
+ appParams.FilterMessageType = 0;
+ appParams.FilterReadStatus = 0;
+ appParams.FilterPriority = 0;
+ appParams.FilterPeriodBegin = null;
+ appParams.FilterPeriodEnd = null;
+ appParams.FilterRecipient = null;
+ appParams.FilterOriginator = null;
+ appParams.Retry = 1;
+ appParams.Transparent = 0;
+ appParams.FractionRequest = BluetoothMasSpecParams.MAS_FRACTION_REQUEST_NOT_SET;
+ appParams.Charset = 0x01;
+ }
+
+ public MasAppParamsStore() {
+ super();
+ appParams = new BluetoothMasAppParams();
+ clear();
+ }
+
+ public final BluetoothMasAppParams get() {
+ if (D) Log.d(TAG, "Create MasAppParams struct for service : Enter");
+ BluetoothMasAppParams tmp = new BluetoothMasAppParams();
+
+ tmp.MaxListCount = appParams.MaxListCount;
+ tmp.ListStartOffset = appParams.ListStartOffset;
+ tmp.SubjectLength = appParams.SubjectLength;
+ tmp.ParameterMask = appParams.ParameterMask;
+
+ tmp.Attachment = appParams.Attachment;
+ tmp.Charset = appParams.Charset;
+
+ tmp.StatusIndicator = appParams.StatusIndicator;
+ tmp.StatusValue = appParams.StatusValue;
+ tmp.Retry = appParams.Retry;
+
+ tmp.FilterMessageType = appParams.FilterMessageType;
+ tmp.FilterReadStatus = appParams.FilterReadStatus;
+ tmp.FilterPriority = appParams.FilterPriority;
+
+ tmp.FilterPeriodBegin = (appParams.FilterPeriodBegin == null) ? null : new String(appParams.FilterPeriodBegin);
+ tmp.FilterPeriodEnd = (appParams.FilterPeriodEnd == null) ? null : new String(appParams.FilterPeriodEnd);
+ tmp.FilterRecipient = (appParams.FilterRecipient == null) ? null : new String(appParams.FilterRecipient);
+ tmp.FilterOriginator = (appParams.FilterOriginator == null) ? null : new String(appParams.FilterOriginator);
+ tmp.Retry = appParams.Retry;
+ tmp.Transparent = appParams.Transparent;
+ tmp.FractionRequest = appParams.FractionRequest;
+ tmp.Notification = appParams.Notification;
+
+ return tmp;
+ }
+
+ public final boolean isMaxListCountZero() {
+
+ return (appParams.MaxListCount == 0) ? true : false;
+
+ }
+
+ private final int getUint16BigEndian(byte b1, byte b2) {
+ int retVal;
+ retVal = (int) ((0x0000FF00 & (int) (b1 << 0x8)) | (0x000000FF & (int) b2));
+ return retVal;
+ }
+
+ private final long getUint32BigEndian(byte b1, byte b2, byte b3, byte b4) {
+ long retVal;
+ retVal = (long) ((0xFF000000 & (long) (b1 << 0x24))
+ | (0x00FF0000 & (long) (b2 << 0x16))
+ | (0x0000FF00 & (long) (b3 << 0x8)) | (0x000000FF & (long) b4));
+ return retVal;
+ }
+
+ private final boolean validateTag(long tagVal, long tagLen, long tagMinVal, long tagMaxVal, long tagActualLen) {
+
+ if (tagLen != tagActualLen) {
+ return false;
+ }
+
+ if ( tagVal < tagMinVal || tagVal > tagMaxVal){
+ return false;
+ }
+ return true;
+ }
+
+ public final boolean parse(byte[] params) {
+ int i = 0;
+
+
+ if (D) Log.d(TAG, "Parse App. Params: Enter");
+
+ if (params == null){
+ if (D) Log.d(TAG, "No App. Params to parse: Exit");
+ return true;
+ }
+
+ while (i < params.length) {
+ switch (params[i]) {
+ case BluetoothMasSpecParams.MAS_TAG_MAX_LIST_COUNT:
+ i += 2;
+ appParams.MaxListCount = getUint16BigEndian(params[i], params[i + 1]);
+ Log.d(TAG, " params i " + params[i] + " params i+1"
+ + params[i + 1] + " maxlistcount "
+ + appParams.MaxListCount);
+ if(validateTag((long)appParams.MaxListCount, (long) params[i-1],
+ (long) BluetoothMasSpecParams.MAS_TAG_MAX_LIST_COUNT_MIN_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_MAX_LIST_COUNT_MAX_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_MAX_LIST_COUNT_LEN ) == false){
+ return false;
+ }
+ i += BluetoothMasSpecParams.MAS_TAG_MAX_LIST_COUNT_LEN;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_LIST_START_OFFSET:
+ i += 2;
+ appParams.ListStartOffset = getUint16BigEndian(params[i],
+ params[i + 1]);
+ Log.d(TAG, " params i " + params[i] + " params i+1"
+ + params[i + 1] + " maxlistcount "
+ + appParams.ListStartOffset);
+ if(validateTag((long)appParams.ListStartOffset, (long) params[i-1],
+ (long) BluetoothMasSpecParams.MAS_TAG_LIST_START_OFFSET_MIN_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_LIST_START_OFFSET_MAX_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_LIST_START_OFFSET_LEN ) == false){
+ return false;
+ }
+ i += BluetoothMasSpecParams.MAS_TAG_LIST_START_OFFSET_LEN;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_FILTER_PERIOD_BEGIN:
+ i += 1;
+ appParams.FilterPeriodBegin = new String("");
+ for (int j = 1; j < params[i]; j++) {
+ appParams.FilterPeriodBegin += (char) params[i + j];
+ }
+ Log.d(TAG, "FilterPeriodBegin "
+ + appParams.FilterPeriodBegin);
+ i += params[i];
+ i += 1;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_FILTER_PERIOD_END:
+ i += 1;
+ appParams.FilterPeriodEnd = new String("");
+ for (int j = 1; j < params[i]; j++) {
+ appParams.FilterPeriodEnd += (char) params[i + j];
+ }
+ Log.d(TAG, "FilterPeriodEnd " + appParams.FilterPeriodEnd);
+ i += params[i];
+ i += 1;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_FILTER_RECIPIENT:
+ i += 1;
+ appParams.FilterRecipient = new String("");
+ for (int j = 1; j < params[i]; j++) {
+ appParams.FilterRecipient += (char)params[i + j];
+
+ }
+ Log.d(TAG, "FilterPeriodRecipient "
+ + appParams.FilterRecipient);
+ i += params[i];
+ i += 1;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_FILTER_ORIGINATOR:
+ i += 1;
+ appParams.FilterOriginator = new String("");
+ for (int j = 1; j < params[i]; j++) {
+ appParams.FilterOriginator += (char) params[i+ j];
+ }
+ Log.d(TAG, "FilterPeriodOriginator "
+ + appParams.FilterOriginator);
+ i += params[i];
+ i += 1;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_FILTER_MESSAGE_TYPE:
+ i += 2;
+ appParams.FilterMessageType = params[i];
+ Log.d(TAG, " params i " + params[i] + " FilterMessageType "
+ + appParams.FilterMessageType);
+ if(validateTag((long)appParams.FilterMessageType, (long) params[i-1],
+ (long) BluetoothMasSpecParams.MAS_TAG_FILTER_MESSAGE_TYPE_MIN_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_FILTER_MESSAGE_TYPE_MAX_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_FILTER_MESSAGE_TYPE_LEN ) == false){
+ return false;
+ }
+ i += BluetoothMasSpecParams.MAS_TAG_FILTER_MESSAGE_TYPE_LEN;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_FILTER_READ_STATUS:
+ i += 2;
+ appParams.FilterReadStatus = params[i];
+ Log.d(TAG, " params i " + params[i] + " FilterReadStatus "
+ + appParams.FilterReadStatus);
+ if(validateTag((long)appParams.FilterReadStatus, (long) params[i-1],
+ (long) BluetoothMasSpecParams.MAS_TAG_FILTER_READ_STATUS_MIN_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_FILTER_READ_STATUS_MAX_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_FILTER_READ_STATUS_LEN ) == false){
+ return false;
+ }
+ i += BluetoothMasSpecParams.MAS_TAG_FILTER_READ_STATUS_LEN;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_FILTER_PRIORITY:
+ i += 2;
+ appParams.FilterPriority = params[i];
+ Log.d(TAG, " params i " + params[i] + " FilterPriority "
+ + appParams.FilterPriority);
+ if(validateTag((long)appParams.FilterPriority, (long) params[i-1],
+ (long) BluetoothMasSpecParams.MAS_TAG_FILTER_PRIORITY_MIN_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_FILTER_PRIORITY_MAX_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_FILTER_PRIORITY_LEN ) == false){
+ return false;
+ }
+ i += BluetoothMasSpecParams.MAS_TAG_FILTER_PRIORITY_LEN;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_STATUS_INDICATOR:
+ i += 2;
+ appParams.StatusIndicator = params[i];
+ Log.d(TAG, " params i " + params[i] + " StatusIndicator "
+ + appParams.StatusIndicator);
+ if(validateTag((long)appParams.StatusIndicator, (long) params[i-1],
+ (long) BluetoothMasSpecParams.MAS_TAG_STATUS_INDICATOR_MIN_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_STATUS_INDICATOR_MAX_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_STATUS_INDICATOR_LEN ) == false){
+ return false;
+ }
+ i += BluetoothMasSpecParams.MAS_TAG_STATUS_INDICATOR_LEN;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_STATUS_VALUE:
+ i += 2;
+ appParams.StatusValue = params[i];
+ Log.d(TAG, " params i " + params[i] + " StatusValue "
+ + appParams.StatusValue);
+ if(validateTag((long)appParams.StatusValue, (long) params[i-1],
+ (long) BluetoothMasSpecParams.MAS_TAG_STATUS_VALUE_MIN_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_STATUS_VALUE_MAX_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_STATUS_VALUE_LEN ) == false){
+ return false;
+ }
+ i += BluetoothMasSpecParams.MAS_TAG_STATUS_VALUE_LEN;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_SUBJECT_LENGTH:
+ i += 2;
+ appParams.SubjectLength = (short)(params[i] & 0x00FF);
+ Log.d(TAG, " params i " + params[i] + " SubjectLen "
+ + appParams.SubjectLength);
+ if(validateTag((long)appParams.SubjectLength, (long) params[i-1],
+ (long) BluetoothMasSpecParams.MAS_TAG_SUBJECT_LENGTH_MIN_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_SUBJECT_LENGTH_MAX_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_SUBJECT_LENGTH_LEN ) == false){
+ return false;
+ }
+ i += BluetoothMasSpecParams.MAS_TAG_SUBJECT_LENGTH_LEN;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_PARAMETER_MASK:
+ i += 2;
+ appParams.ParameterMask = getUint32BigEndian(params[i],
+ params[i + 1], params[i + 2], params[i + 3]);
+ if ( appParams.ParameterMask == 0 ){
+ // If it is 0, send all parameters
+ appParams.ParameterMask = BluetoothMasSpecParams.MAS_DEFAULT_PARAMETER_MASK;
+ }
+ Log.d(TAG, " params i " + params[i] + " params i+1"
+ + params[i + 1] + "params[i+2]" + params[i + 2]
+ + "params[i+3" + params[i + 3] + " ParameterMask "
+ + appParams.ParameterMask);
+ if(validateTag((long)appParams.ParameterMask, (long) params[i-1],
+ (long) BluetoothMasSpecParams.MAS_TAG_PARAMETER_MASK_MIN_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_PARAMETER_MASK_MAX_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_PARAMETER_MASK_LEN ) == false){
+ return false;
+ }
+ i += BluetoothMasSpecParams.MAS_TAG_PARAMETER_MASK_LEN;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_CHARSET:
+ i += 2;
+ appParams.Charset = params[i];
+ Log.d(TAG, " params i " + params[i] + " Charset "
+ + appParams.Charset);
+ if(validateTag((long)appParams.Charset, (long) params[i-1],
+ (long) BluetoothMasSpecParams.MAS_TAG_CHARSET_MIN_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_CHARSET_MAX_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_CHARSET_LEN ) == false){
+ return false;
+ }
+ i += BluetoothMasSpecParams.MAS_TAG_CHARSET_LEN;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_TRANSPARENT:
+ i += 2;
+ appParams.Transparent = params[i];
+ Log.d(TAG, " params i " + params[i] + " Transparent "
+ + appParams.Transparent);
+ if(validateTag((long)appParams.Transparent, (long) params[i-1],
+ (long) BluetoothMasSpecParams.MAS_TAG_TRANSPARENT_MIN_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_TRANSPARENT_MAX_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_TRANSPARENT_LEN ) == false){
+ return false;
+ }
+ i += BluetoothMasSpecParams.MAS_TAG_TRANSPARENT_LEN;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_RETRY:
+ i += 2;
+ appParams.Retry = params[i];
+ Log.d(TAG, " params i " + params[i] + " Retry "
+ + appParams.Retry);
+ if(validateTag((long)appParams.Retry, (long) params[i-1],
+ (long) BluetoothMasSpecParams.MAS_TAG_RETRY_MIN_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_RETRY_MAX_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_RETRY_LEN ) == false){
+ return false;
+ }
+ i += BluetoothMasSpecParams.MAS_TAG_RETRY_LEN;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_ATTACHMENT:
+ i += 2;
+ appParams.Attachment = params[i];
+ Log.d(TAG, " params i " + params[i] + " Attachment "
+ + appParams.Attachment);
+ if(validateTag((long)appParams.Attachment, (long) params[i-1],
+ (long) BluetoothMasSpecParams.MAS_TAG_ATTACHMENT_MIN_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_ATTACHMENT_MAX_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_ATTACHMENT_LEN ) == false){
+ return false;
+ }
+ i += BluetoothMasSpecParams.MAS_TAG_ATTACHMENT_LEN;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_FRACTION_REQUEST:
+ i += 2;
+ appParams.FractionRequest = params[i];
+ Log.d(TAG, " params i " + params[i] + " Fraction Request "
+ + appParams.FractionRequest);
+ if(validateTag((long)appParams.FractionRequest, (long) params[i-1],
+ (long) BluetoothMasSpecParams.MAS_TAG_FRACTION_REQUEST_MIN_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_FRACTION_REQUEST_MAX_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_FRACTION_REQUEST_LEN ) == false){
+ return false;
+ }
+ i += BluetoothMasSpecParams.MAS_TAG_FRACTION_REQUEST_LEN;
+ break;
+
+ case BluetoothMasSpecParams.MAS_TAG_NOTIFICATION_STATUS:
+ i += 2;
+ appParams.Notification = params[i];
+ if(validateTag((long)appParams.MaxListCount, (long) params[i-1],
+ (long) BluetoothMasSpecParams.MAS_TAG_NOTIFICATION_STATUS_MIN_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_NOTIFICATION_STATUS_MAX_VAL,
+ (long) BluetoothMasSpecParams.MAS_TAG_NOTIFICATION_STATUS_LEN ) == false){
+ return false;
+
+ }
+ i += BluetoothMasSpecParams.MAS_TAG_NOTIFICATION_STATUS_LEN;
+ break;
+ default:
+ break;
+
+ }
+ }
+ return true;
+ }
+ }
+
+ private MasAppParamsStore masAppParams = new MasAppParamsStore();
+
+ public BluetoothMasObexServer(Handler callback,
+ BluetoothDevice remoteDevice, Context context) {
+ super();
+ appIf = new BluetoothMasAppIf(context, "SMS_MMS_EMAIL");
+ mConnectionId = -1;
+ mCallback = callback;
+ mContext = context;
+ mRemoteDevice = remoteDevice;
+
+ Log.d(TAG, "BlueoothMasObexServer const called");
+ // set initial value when ObexServer created
+ if (D) Log.d(TAG, "Initialize MasObexServer");
+ }
+
+ @Override
+ public int onConnect(final HeaderSet request, HeaderSet reply) {
+ if (D) Log.d(TAG, "onConnect()");
+ try {
+ byte[] uuid = (byte[]) request.getHeader(HeaderSet.TARGET);
+ if (uuid == null) {
+ Log.w(TAG, "Null UUID ");
+ return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
+ }
+ if (D)
+ Log.d(TAG, "onConnect(): uuid=" + Arrays.toString(uuid));
+
+ if (uuid.length != UUID_LENGTH) {
+ Log.w(TAG, "Wrong UUID length");
+ return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
+ }
+ for (int i = 0; i < UUID_LENGTH; i++) {
+ if (uuid[i] != MAS_TARGET[i]) {
+ Log.w(TAG, "Wrong UUID");
+ return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
+ }
+ }
+ reply.setHeader(HeaderSet.WHO, uuid);
+ } catch (IOException e) {
+ Log.e(TAG, e.toString());
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+
+ try {
+ byte[] remote = (byte[]) request.getHeader(HeaderSet.WHO);
+ if (remote != null) {
+ if (D) Log.d(TAG, "onConnect(): remote=" + Arrays.toString(remote));
+ reply.setHeader(HeaderSet.TARGET, remote);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, e.toString());
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+
+ if (V) Log.v(TAG, "onConnect(): uuid is ok, will send out "
+ + "MSG_SESSION_ESTABLISHED msg.");
+
+ Message msg = Message.obtain(mCallback);
+ msg.what = BluetoothMasService.MSG_SESSION_ESTABLISHED;
+ msg.sendToTarget();
+
+ state = MasState.MAS_SERVER_CONNECTED;
+ if (D) Log.d(TAG, "Connect(): Success");
+ return ResponseCodes.OBEX_HTTP_OK;
+ }
+
+ @Override
+ public void onDisconnect(final HeaderSet req, final HeaderSet resp) {
+ if (D) Log.d(TAG, "onDisconnect(): enter");
+ appIf.disconnect();
+
+ resp.responseCode = ResponseCodes.OBEX_HTTP_OK;
+ if (mCallback != null) {
+ Message msg = Message.obtain(mCallback);
+ msg.what = BluetoothMasService.MSG_SESSION_DISCONNECTED;
+ msg.sendToTarget();
+ if (V) Log.v(TAG,"onDisconnect(): msg MSG_SESSION_DISCONNECTED sent out.");
+ }
+
+ // MNS Service
+ appIf.stopMnsSession(mRemoteDevice);
+
+ state = MasState.MAS_SERVER_DISCONNECTED;
+ }
+
+ @Override
+ public int onAbort(HeaderSet request, HeaderSet reply) {
+ if (D) Log.e(TAG, "onAbort(): enter.");
+ sIsAborted = true;
+ return ResponseCodes.OBEX_HTTP_OK;
+ }
+
+ @Override
+ public int onSetPath(final HeaderSet request, final HeaderSet reply,
+ final boolean backup, final boolean create) {
+
+ if (D) Log.e(TAG, "onSetPath(): supports SetPath request.");
+
+ String tmpPath = null;
+ boolean retVal;
+ boolean tmpBackup = backup;
+
+ if (tmpBackup && create) {
+ tmpBackup = true;
+ } else {
+ tmpBackup = false;
+ }
+ if (state != MasState.MAS_SERVER_CONNECTED) {
+ if (D)
+ Log.e(TAG, "onSetPath() Failed: Mas Server not connected");
+ return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+ }
+ state = MasState.MAS_SERVER_SET_FOLDER;
+
+ try {
+ tmpPath = (String) request.getHeader(HeaderSet.NAME);
+ } catch (IOException e) {
+ Log.e(TAG, "Get name header fail");
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+ if (D)
+ Log.e(TAG, "backup=" + backup + " create=" + create + " name="
+ + tmpPath);
+
+ retVal = appIf.setPath(backup, tmpPath);
+ state = MasState.MAS_SERVER_CONNECTED;
+ if (retVal == true) {
+ if (V)
+ Log.e(TAG, "SetPath to" + tmpPath + "SUCCESS");
+ return ResponseCodes.OBEX_HTTP_OK;
+ } else {
+ Log.e(TAG, "Path not found");
+ return ResponseCodes.OBEX_HTTP_NOT_FOUND;
+ }
+ }
+
+ @Override
+ public void onClose() {
+
+ if (mCallback != null) {
+ Message msg = Message.obtain(mCallback);
+ msg.what = BluetoothMasService.MSG_SERVERSESSION_CLOSE;
+ msg.sendToTarget();
+ if (D) Log.e(TAG, "onClose(): msg MSG_SERVERSESSION_CLOSE sent out.");
+ }
+ }
+
+ @Override
+ public int onGet(Operation op) {
+
+ byte[] appParams = null;
+ boolean retVal = true;
+
+ if (D) Log.e(TAG, "onGet(): support GET request.");
+
+ sIsAborted = false;
+ HeaderSet request = null;
+ String type = "";
+ String name = "";
+
+ // TBD - IncompleteGet handling
+ try {
+ request = op.getReceivedHeader();
+ type = (String) request.getHeader(HeaderSet.TYPE);
+ name = (String) request.getHeader(HeaderSet.NAME);
+ appParams = (byte[])request.getHeader(HeaderSet.APPLICATION_PARAMETER);
+ } catch (IOException e) {
+ Log.e(TAG, "request headers error");
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+
+ masAppParams.clear();
+ retVal = masAppParams.parse(appParams);
+
+ if (type == null || (retVal == false) ) {
+ return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+ }
+
+ Log.e(TAG, "type = " + type);
+
+ if (type.equals(TYPE_LISTING)) {
+ return sendFolderListing(op);
+ }
+ if (type.equals(TYPE_MESSAGE_LISTING)) {
+ return sendMsgListing(op, name);
+ }
+ if (type.equals(TYPE_MESSAGE)) {
+ return sendMsg(op, name);
+ }
+
+ Log.e(TAG, "get returns HTTP_BAD_REQUEST");
+ return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+
+ }
+
+ private final int pushMsg(Operation op, String name) {
+ // TBD - Need to do this on a per masinstance basis
+ String DEFAULT_FILE = "PushMsg.txt";
+ int outputBufferSize = op.getMaxPacketSize();
+ int readLength = 0;
+ long timestamp = 0;
+ int position = 0;
+ byte[] b = new byte[outputBufferSize];
+ BufferedOutputStream bos = null;
+ InputStream is = null;
+ boolean error = false;
+ File file = null;
+ BluetoothMasPushMsgRsp pMsg;
+
+ file = new File(mContext.getFilesDir() + "/" + DEFAULT_FILE);
+
+ try {
+ is = op.openInputStream();
+ } catch (IOException e1) {
+ Log.e(TAG, "Error while opening InputStream");
+ error = true;
+ }
+
+ if (error != true) {
+ try {
+
+ FileOutputStream fos = mContext.openFileOutput(DEFAULT_FILE,
+ Context.MODE_PRIVATE);
+
+ bos = new BufferedOutputStream(fos);
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ if (error != true) {
+ try {
+ while (true) {
+ if (V) {
+ timestamp = System.currentTimeMillis();
+ }
+ readLength = is.read(b);
+ if (readLength == -1) {
+ if (D) {
+ Log.d(TAG, "Receive file reached stream end at position" + position);
+ }
+ break;
+ }
+ bos.write(b, 0, readLength);
+ position += readLength;
+ if (V) {
+ Log.v(TAG, "Receive file position = " + position
+ + " readLength " + readLength + " bytes took "
+ + (System.currentTimeMillis() - timestamp)
+ + " ms");
+ }
+ }
+ } catch (IOException e1) {
+ Log.e(TAG, "Error when receiving file");
+ error = true;
+ }
+ }
+
+ if (bos != null) {
+ try {
+ bos.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Error when closing stream after send");
+ error = true;
+ }
+ }
+
+ if (error != true) {
+ pMsg = appIf.pushMsg(name, file, masAppParams.get());
+
+ if ((pMsg.msgHandle != null)
+ && (pMsg.response == ResponseCodes.OBEX_HTTP_OK)) {
+ HeaderSet reply;
+ reply = new HeaderSet();
+ reply.setHeader(HeaderSet.NAME, pMsg.msgHandle);
+ return pushHeader(op, reply);
+
+ } else {
+ return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+ }
+ } else {
+ return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+ }
+ }
+
+ private final int msgStatus(Operation op, String name) {
+ if (D) Log.d(TAG, "msgStatus: Enter");
+ return appIf.msgStatus(name, masAppParams.get());
+ }
+
+ private final int msgUpdate(Operation op, String name) {
+ if (D) Log.d(TAG, "msgUpdate: Enter");
+ return appIf.msgUpdate(name, masAppParams.get());
+ }
+
+ private final int notification(Operation op) {
+ return appIf.notification(mRemoteDevice, masAppParams.get());
+ }
+
+ @Override
+ public int onPut(Operation op) {
+
+ byte[] appParams = null;
+ boolean retVal = true;
+ BluetoothMasAppParams tmp;
+
+ if (D) Log.d(TAG, "onPut(): support PUT request.");
+
+ sIsAborted = false;
+ HeaderSet request = null;
+ String type = "";
+ String name = "";
+
+ // TBD - IncompleteGet handling
+ try {
+ request = op.getReceivedHeader();
+ type = (String) request.getHeader(HeaderSet.TYPE);
+ name = (String) request.getHeader(HeaderSet.NAME);
+ appParams = (byte[])request.getHeader(HeaderSet.APPLICATION_PARAMETER);
+ } catch (IOException e) {
+ Log.e(TAG, "request headers error");
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+ masAppParams.clear();
+ if ( appParams != null ){
+ masAppParams.parse(appParams);
+ }
+ if(type == null || retVal == false) {
+ return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+ }
+
+ tmp = masAppParams.get();
+
+ if (tmp.Charset == 0x00) {
+ return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+ }
+ Log.e(TAG, "type = " + type);
+
+ if (type.equals(TYPE_MESSAGE)) {
+ return pushMsg(op, name);
+ }
+ if (type.equals(TYPE_MESSAGE_STATUS)) {
+ return msgStatus(op, name);
+ }
+ if (type.equals(TYPE_MESSAGE_UPDATE)) {
+ return msgUpdate(op, name);
+ }
+ if (type.equals(TYPE_MESSAGE_NOTIFICATION)) {
+ return notification(op);
+ }
+ Log.e(TAG, "put returns HTTP_BAD_REQUEST");
+ return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+
+ }
+
+ /**
+ */
+ private final int pushHeader(final Operation op, final HeaderSet reply) {
+
+ if (D) Log.d(TAG, "Push Header");
+ if (D) Log.d(TAG, reply.toString());
+
+ int pushResult = ResponseCodes.OBEX_HTTP_OK;
+ try {
+ op.sendHeaders(reply);
+ } catch (IOException e) {
+ Log.e(TAG, e.toString());
+ pushResult = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+ if (D) Log.d(TAG, "Push Header: Exit : RetVal " + pushResult);
+ return pushResult;
+ }
+
+ /** Function to send folder data to client */
+ private final int sendFolderListingBody(Operation op,
+ final String folderlistString) {
+
+ if (folderlistString == null) {
+ Log.e(TAG, "folderlistString is null!");
+ return ResponseCodes.OBEX_HTTP_OK;
+ }
+
+ int folderlistStringLen = folderlistString.length();
+ if (D) Log.d(TAG, "Send Folder Listing Body: len=" + folderlistStringLen);
+
+ OutputStream outputStream = null;
+ int pushResult = ResponseCodes.OBEX_HTTP_OK;
+ try {
+ outputStream = op.openOutputStream();
+ } catch (IOException e) {
+ Log.e(TAG, "open outputstrem failed" + e.toString());
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+
+ int position = 0;
+ long timestamp = 0;
+ int outputBufferSize = op.getMaxPacketSize();
+ if (V) Log.d(TAG, "outputBufferSize = " + outputBufferSize);
+ while (position != folderlistStringLen) {
+ if (sIsAborted) {
+ ((ServerOperation) op).isAborted = true;
+ sIsAborted = false;
+ break;
+ }
+ if (V) timestamp = System.currentTimeMillis();
+ int readLength = outputBufferSize;
+ if (folderlistStringLen - position < outputBufferSize) {
+ readLength = folderlistStringLen - position;
+ }
+ String subStr = folderlistString.substring(position, position + readLength);
+ try {
+ outputStream.write(subStr.getBytes(), 0, readLength);
+ } catch (IOException e) {
+ Log.e(TAG, "write outputstream failed" + e.toString());
+ pushResult = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ break;
+ }
+ if (V) {
+ Log.d(TAG, "Sending folderlist String position = " + position
+ + " readLength " + readLength + " bytes took "
+ + (System.currentTimeMillis() - timestamp) + " ms");
+ }
+ position += readLength;
+ }
+
+ if (V) Log.e(TAG, "Send Data complete!");
+
+ if (!closeStream(outputStream, op)) {
+ Log.e(TAG,"Send Folder Listing Body - Close output stream error! ");
+ pushResult = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+ if (V) Log.e(TAG, "Send Folder Listing Body complete! result = " + pushResult);
+ return pushResult;
+ }
+
+ private final int sendBody(Operation op, File fileinfo) {
+
+ Log.e(TAG, "sendFile = " + fileinfo.getName());
+ int position = 0;
+ int readLength = 0;
+ int outputBufferSize = op.getMaxPacketSize();
+ long timestamp = 0;
+ FileInputStream fileInputStream;
+ OutputStream outputStream;
+ BufferedInputStream bis;
+
+ if (D) Log.d(TAG, "Send Body: Enter");
+ try {
+ byte[] buffer = new byte[outputBufferSize];
+ fileInputStream = new FileInputStream(fileinfo);
+ outputStream = op.openOutputStream();
+ bis = new BufferedInputStream(fileInputStream, 0x4000);
+ while ((position != fileinfo.length())) {
+ timestamp = System.currentTimeMillis();
+ if (position != fileinfo.length()) {
+ readLength = bis.read(buffer, 0, outputBufferSize);
+ }
+ outputStream.write(buffer, 0, readLength);
+ position += readLength;
+ if (V) {
+ Log.e(TAG, "Sending file position = " + position
+ + " readLength " + readLength + " bytes took "
+ + (System.currentTimeMillis() - timestamp) + " ms");
+ }
+ }
+ } catch (IOException e) {
+ return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+ }
+ if (position == fileinfo.length()) {
+ if (D) Log.d(TAG, "SendBody : Exit: OK");
+ return ResponseCodes.OBEX_HTTP_OK;
+ }
+ else {
+ if (D) Log.d(TAG, "SendBody : Exit: CONTINUE");
+ return ResponseCodes.OBEX_HTTP_CONTINUE;
+ }
+ }
+
+ /** Send a bMessage to client */
+ private final int sendMsg(Operation op, String name) {
+
+ BluetoothMasMessageRsp msg;
+ byte[] val = new byte[1];
+
+ if (D) Log.d(TAG, "SendMsg : Enter");
+ msg = appIf.msg(name, masAppParams.get());
+ if(msg == null || msg.rsp != ResponseCodes.OBEX_HTTP_OK) {
+ return msg.rsp;
+ }
+
+ if(masAppParams.get().FractionRequest == 1){
+ HeaderSet reply;
+ val[0] = msg.fractionDeliver;
+ ApplicationParameter ap = new ApplicationParameter();
+ ap.addAPPHeader(
+ (byte) BluetoothMasSpecParams.MAS_TAG_FRACTION_DELIVER,
+ (byte) BluetoothMasSpecParams.MAS_TAG_FRACTION_DELIVER_LEN,
+ val);
+
+ reply = new HeaderSet();
+ reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());
+
+ int retVal;
+ retVal = pushHeader(op, reply);
+ if (retVal != ResponseCodes.OBEX_HTTP_OK) {
+ if (D) Log.d(TAG, "SendMsg : FAILED: RetVal " + retVal);
+ return retVal;
+ }
+ }
+ if (D) Log.d(TAG, "SendMsg : SUCCESS");
+ return sendBody(op, msg.file);
+ }
+
+ /** Send an XML format String to client for Folder listing */
+ private final int sendFolderListing(Operation op) {
+ int folderListSize;
+
+ if (D) Log.d(TAG, "SendFolderListing : Enter");
+ folderListSize = appIf.folderListingSize();
+ byte[] size = new byte[2];
+ size[0] = (byte) ((folderListSize / 0x100) & 0xff);
+ size[1] = (byte) ((folderListSize % 0x100) & 0xff);
+
+ HeaderSet reply;
+ ApplicationParameter ap = new ApplicationParameter();
+ ap.addAPPHeader(
+ (byte) BluetoothMasSpecParams.MAS_TAG_FOLDER_LISTING_SIZE,
+ (byte) BluetoothMasSpecParams.MAS_TAG_FOLDER_LISTING_SIZE_LEN,
+ size);
+ reply = new HeaderSet();
+ reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());
+
+ if (!masAppParams.isMaxListCountZero()) {
+ int retVal;
+ retVal = pushHeader(op, reply);
+ if (retVal != ResponseCodes.OBEX_HTTP_OK) {
+ if (D) Log.d(TAG, "SendFolderListing : FAILED : RetVal" + retVal);
+ return retVal;
+ }
+ return sendFolderListingBody(op, appIf.folderListing(masAppParams.get()));
+ } else {
+ op.noEndofBody();
+ return pushHeader(op, reply);
+ }
+ }
+
+ /** Send an XML format String to client for Message listing */
+ private final int sendMsgListing(Operation op, String name) {
+
+ byte[] val = new byte[2];
+
+ BluetoothMasMessageListingRsp appIfMsgListRsp = appIf.msgListing(name,
+ masAppParams.get());
+
+ if(appIfMsgListRsp == null || appIfMsgListRsp.rsp != ResponseCodes.OBEX_HTTP_OK) {
+ return appIfMsgListRsp.rsp;
+ }
+
+ if (D) Log.d(TAG, "SendMsgListing : Enter");
+
+ Time time = new Time();
+ time.setToNow();
+
+ String time3339 = time.format3339(false);
+ int timeStrLength = time3339.length();
+
+ String datetimeStr = time.toString().substring(0, 15) +
+ time3339.substring(timeStrLength - 6, timeStrLength - 3) +
+ time3339.substring(timeStrLength - 2, timeStrLength);
+
+ byte[] MSETime = datetimeStr.getBytes();
+
+ HeaderSet reply;
+ ApplicationParameter ap = new ApplicationParameter();
+ ap.addAPPHeader((byte) BluetoothMasSpecParams.MAS_TAG_MSE_TIME,
+ (byte) BluetoothMasSpecParams.MAS_TAG_MSE_TIME_LEN, MSETime);
+ val[0] = appIfMsgListRsp.newMessage;
+ ap.addAPPHeader((byte) BluetoothMasSpecParams.MAS_TAG_NEW_MESSAGE,
+ (byte) BluetoothMasSpecParams.MAS_TAG_NEW_MESSAGE_LEN, val);
+
+ val[0] = (byte) ((appIfMsgListRsp.msgListingSize / 0x100) & 0xff);
+ val[1] = (byte) ((appIfMsgListRsp.msgListingSize % 0x100) & 0xff);
+
+ ap.addAPPHeader(
+ (byte) BluetoothMasSpecParams.MAS_TAG_MESSAGE_LISTING_SIZE,
+ (byte) BluetoothMasSpecParams.MAS_TAG_MESSAGE_LISTING_SIZE_LEN,
+ val);
+
+ reply = new HeaderSet();
+ reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());
+
+ if (!masAppParams.isMaxListCountZero()) {
+ int retVal;
+ retVal = pushHeader(op, reply);
+ if (retVal != ResponseCodes.OBEX_HTTP_OK) {
+ if (D) Log.d(TAG, "SendMsgListing : Failed : RetVal " + retVal);
+ return retVal;
+ }
+ return sendBody(op, appIfMsgListRsp.file);
+ } else {
+ return pushHeader(op, reply);
+ }
+ }
+
+ public static boolean closeStream(final OutputStream out, final Operation op) {
+ boolean returnvalue = true;
+ try {
+ if (out != null) {
+ out.close();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "outputStream close failed" + e.toString());
+ returnvalue = false;
+ }
+ try {
+ if (op != null) {
+ op.close();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "operation close failed" + e.toString());
+ returnvalue = false;
+ }
+ return returnvalue;
+ }
+};
+
diff --git a/src/com/android/bluetooth/map/BluetoothMasReceiver.java b/src/com/android/bluetooth/map/BluetoothMasReceiver.java
new file mode 100644
index 000000000..c0860848f
--- /dev/null
+++ b/src/com/android/bluetooth/map/BluetoothMasReceiver.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.bluetooth.map;
+import android.bluetooth.BluetoothAdapter;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.util.Log;
+
+public class BluetoothMasReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "BluetoothMasReceiver";
+
+ private static final boolean V = BluetoothMasService.VERBOSE;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (V) Log.v(TAG, "BluetoothMasReceiver onReceive :" + intent.getAction());
+ Log.e(TAG, "BluetoothMasReceiver onReceive :" + intent.getAction());
+ Intent in = new Intent();
+ in.putExtras(intent);
+ in.setClass(context, BluetoothMasService.class);
+ String action = intent.getAction();
+ in.putExtra("action", action);
+ boolean startService = true;
+ if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+ int state = in.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+ BluetoothAdapter.ERROR);
+ in.putExtra(BluetoothAdapter.EXTRA_STATE, state);
+ /*
+ * Other than Tranistioning state, start the MAP service whenever BT
+ * transitioned to OFF/ON, or Adapter returns error
+ */
+
+ Log.d(TAG, "Bluetooth STATE CHANGED to " + state);
+
+ if ((state == BluetoothAdapter.STATE_TURNING_ON)
+ || (state == BluetoothAdapter.STATE_TURNING_OFF)) {
+ startService = false;
+ }
+ if (state == BluetoothAdapter.ERROR) {
+ Log.d(TAG, " BluetoothAdapter returns ERROR");
+ }
+
+ if ((state == BluetoothAdapter.STATE_OFF)) {
+
+ startService = false;
+ // Stop MAS service
+ context.stopService(in);
+
+ // TODO - Stop MNS service?
+ }
+
+ } else if (action.equals(Intent.ACTION_MEDIA_EJECT)
+ || action.equals(Intent.ACTION_MEDIA_MOUNTED))
+ startService = true;
+
+ if (startService) {
+ context.startService(in);
+ }
+ }
+}
diff --git a/src/com/android/bluetooth/map/BluetoothMasService.java b/src/com/android/bluetooth/map/BluetoothMasService.java
new file mode 100644
index 000000000..9028499a3
--- /dev/null
+++ b/src/com/android/bluetooth/map/BluetoothMasService.java
@@ -0,0 +1,629 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.bluetooth.map;
+
+
+import com.android.bluetooth.R;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothServerSocket;
+import android.bluetooth.BluetoothSocket;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.CursorJoiner;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PowerManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import javax.obex.*;
+
+
+public class BluetoothMasService extends Service {
+ private static final String TAG = "BluetoothMasService";
+
+ /**
+ * To enable MAP DEBUG/VERBOSE logging - run below cmd in adb shell, and
+ * restart com.android.bluetooth process. only enable DEBUG log:
+ * "setprop log.tag.BluetoothMapService DEBUG"; enable both VERBOSE and
+ * DEBUG log: "setprop log.tag.BluetoothMapService VERBOSE"
+ */
+
+ public static final boolean DEBUG = true;
+
+ public static final boolean VERBOSE = true;
+
+ private int mState;
+
+ /**
+ * Intent indicating incoming connection request which is sent to
+ * BluetoothMasActivity
+ */
+ public static final String ACCESS_REQUEST_ACTION = "com.android.bluetooth.map.accessrequest";
+
+ /**
+ * Intent indicating incoming connection request accepted by user which is
+ * sent from BluetoothMasActivity
+ */
+ public static final String ACCESS_ALLOWED_ACTION = "com.android.bluetooth.map.accessallowed";
+
+ /**
+ * Intent indicating incoming connection request denied by user which is
+ * sent from BluetoothMasActivity
+ */
+ public static final String ACCESS_DISALLOWED_ACTION = "com.android.bluetooth.map.accessdisallowed";
+
+ /**
+ * Intent indicating incoming obex authentication request which is from
+ * PCE(Carkit)
+ */
+ public static final String AUTH_CHALL_ACTION = "com.android.bluetooth.map.authchall";
+
+ /**
+ * Intent indicating obex session key input complete by user which is sent
+ * from BluetoothMasActivity
+ */
+ public static final String AUTH_RESPONSE_ACTION = "com.android.bluetooth.map.authresponse";
+
+ /**
+ * Intent indicating user canceled obex authentication session key input
+ * which is sent from BluetoothMasActivity
+ */
+ public static final String AUTH_CANCELLED_ACTION = "com.android.bluetooth.map.authcancelled";
+
+ /**
+ * Intent indicating timeout for user confirmation, which is sent to
+ * BluetoothMasActivity
+ */
+ public static final String USER_CONFIRM_TIMEOUT_ACTION = "com.android.bluetooth.map.userconfirmtimeout";
+
+ public static final String THIS_PACKAGE_NAME = "com.android.bluetooth";
+
+ /**
+ * Intent Extra name indicating always allowed which is sent from
+ * BluetoothMasActivity
+ */
+ public static final String EXTRA_ALWAYS_ALLOWED = "com.android.bluetooth.map.alwaysallowed";
+
+ /**
+ * Intent Extra name indicating session key which is sent from
+ * BluetoothMasActivity
+ */
+ public static final String EXTRA_SESSION_KEY = "com.android.bluetooth.map.sessionkey";
+
+ private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
+
+ private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
+
+ public static final int MSG_SERVERSESSION_CLOSE = 5004;
+
+ public static final int MSG_SESSION_ESTABLISHED = 5005;
+
+ public static final int MSG_SESSION_DISCONNECTED = 5006;
+
+ public static final int MSG_OBEX_AUTH_CHALL = 5007;
+
+ private static final int START_LISTENER = 1;
+
+ private static final int USER_TIMEOUT = 2;
+
+ private static final int AUTH_TIMEOUT = 3;
+
+ private static final int PORT_NUM = 16;
+
+ private static final int USER_CONFIRM_TIMEOUT_VALUE = 30000;
+
+ private static final int TIME_TO_WAIT_VALUE = 6000;
+
+ // Ensure not conflict with Opp notification ID
+ private static final int NOTIFICATION_ID_ACCESS = -1000005;
+
+ private static final int NOTIFICATION_ID_AUTH = -1000006;
+
+ private PowerManager.WakeLock mWakeLock = null;
+
+ private BluetoothAdapter mAdapter;
+
+ private SocketAcceptThread mAcceptThread = null;
+
+ private BluetoothMapAuthenticator mAuth = null;
+
+ private BluetoothServerSocket mServerSocket = null;
+
+ private BluetoothSocket mConnSocket = null;
+
+ public static BluetoothDevice mRemoteDevice = null;
+
+ private static String sRemoteDeviceName = null;
+
+ private boolean mHasStarted = false;
+
+ private volatile boolean mInterrupted;
+
+ private int mStartId = -1;
+
+ private BluetoothMasObexServer mMapServer = null;
+
+ private ServerSession mServerSession = null;
+
+ public BluetoothMasService() {
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ if (VERBOSE)
+ Log.e(TAG, "Map Service onCreate");
+ Log.e(TAG, "Map Service onCreate");
+
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+
+ if (!mHasStarted) {
+ mHasStarted = true;
+ if (VERBOSE)
+ Log.e(TAG, "Starting MAP service");
+
+ int state = mAdapter.getState();
+ if (state == BluetoothAdapter.STATE_ON) {
+ mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
+ .obtainMessage(START_LISTENER), TIME_TO_WAIT_VALUE);
+ }
+ }
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (VERBOSE)
+ Log.e(TAG, "Map Service onStartCommand");
+ int retCode = super.onStartCommand(intent, flags, startId);
+ if (retCode == START_STICKY) {
+ mStartId = startId;
+ if (mAdapter == null) {
+ Log.w(TAG, "Stopping BluetoothMasService: "
+ + "device does not have BT or device is not ready");
+ // Release all resources
+ closeService();
+ } else {
+ // No need to handle the null intent case, because we have
+ // all restart work done in onCreate()
+ if (intent != null) {
+ parseIntent(intent);
+ }
+ }
+ }
+ return retCode;
+ }
+
+ // process the intent from receiver
+ private void parseIntent(final Intent intent) {
+ String action = intent.getStringExtra("action");
+ if (VERBOSE)
+ Log.e(TAG, "action: " + action);
+
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+ BluetoothAdapter.ERROR);
+ boolean removeTimeoutMsg = true;
+ if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+ removeTimeoutMsg = false;
+ if (state == BluetoothAdapter.STATE_OFF) {
+ // Release all resources
+ closeService();
+ }
+ } else if (action.equals(Intent.ACTION_MEDIA_EJECT)
+ || action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
+ closeService();
+ } else if (action.equals(ACCESS_ALLOWED_ACTION)) {
+ if (intent.getBooleanExtra(EXTRA_ALWAYS_ALLOWED, false)) {
+ boolean result = true;
+ if (VERBOSE)
+ Log.v(TAG, "setTrust() result=" + result);
+ }
+ try {
+ if (mConnSocket != null) {
+ startObexServerSession();
+ } else {
+ stopObexServerSession();
+ }
+ } catch (IOException ex) {
+ Log.e(TAG, "Caught the error: " + ex.toString());
+ }
+ } else if (action.equals(ACCESS_DISALLOWED_ACTION)) {
+ stopObexServerSession();
+ } else if (action.equals(AUTH_RESPONSE_ACTION)) {
+ String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY);
+ notifyAuthKeyInput(sessionkey);
+ } else if (action.equals(AUTH_CANCELLED_ACTION)) {
+ notifyAuthCancelled();
+ } else {
+ removeTimeoutMsg = false;
+ }
+
+ if (removeTimeoutMsg) {
+ mSessionStatusHandler.removeMessages(USER_TIMEOUT);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ if (VERBOSE)
+ Log.e(TAG, "Map Service onDestroy");
+
+ super.onDestroy();
+ if (mWakeLock != null) {
+ mWakeLock.release();
+ mWakeLock = null;
+ }
+ closeService();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (VERBOSE)
+ Log.e(TAG, "Map Service onBind");
+ return null;// mBind;
+ }
+
+ private void startRfcommSocketListener() {
+ if (VERBOSE)
+ Log.e(TAG, "Map Service startRfcommSocketListener");
+
+ if (mServerSocket == null) {
+ if (!initSocket()) {
+ closeService();
+ return;
+ }
+ }
+ if (mAcceptThread == null) {
+ mAcceptThread = new SocketAcceptThread();
+ mAcceptThread.setName("BluetoothMapAcceptThread");
+ mAcceptThread.start();
+ }
+ }
+
+ private final boolean initSocket() {
+ if (VERBOSE)
+ Log.e(TAG, "Map Service initSocket");
+
+ boolean initSocketOK = true;
+ final int CREATE_RETRY_TIME = 10;
+
+ // It's possible that create will fail in some cases. retry for 10 times
+ for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
+ try {
+ Method m = mAdapter.getClass().getMethod("listenUsingRfcommOn",
+ new Class[] { int.class });
+ try {
+ mServerSocket = (BluetoothServerSocket) m.invoke(mAdapter,
+ PORT_NUM);
+ } catch (IllegalArgumentException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ } catch (SecurityException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchMethodException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ if (!initSocketOK) {
+ synchronized (this) {
+ try {
+ if (VERBOSE)
+ Log.e(TAG, "wait 3 seconds");
+ Thread.sleep(3000);
+ } catch (InterruptedException e) {
+ Log
+ .e(TAG,
+ "socketAcceptThread thread was interrupted (3)");
+ mInterrupted = true;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (initSocketOK) {
+ if (VERBOSE)
+ Log.e(TAG, "Succeed to create listening socket on channel "
+ + PORT_NUM);
+
+ } else {
+ Log.e(TAG, "Error to create listening socket after "
+ + CREATE_RETRY_TIME + " try");
+ }
+ return initSocketOK;
+ }
+
+ private final void closeSocket(boolean server, boolean accept)
+ throws IOException {
+ if (server == true) {
+ // Stop the possible trying to init serverSocket
+ mInterrupted = true;
+
+ if (mServerSocket != null) {
+ mServerSocket.close();
+ }
+ }
+
+ if (accept == true) {
+ if (mConnSocket != null) {
+ mConnSocket.close();
+ }
+ }
+ }
+
+ private final void closeService() {
+ if (VERBOSE)
+ Log.e(TAG, "Map Service closeService");
+
+ try {
+ closeSocket(true, true);
+ } catch (IOException ex) {
+ Log.e(TAG, "CloseSocket error: " + ex);
+ }
+
+ if (mAcceptThread != null) {
+ try {
+ mAcceptThread.shutdown();
+ mAcceptThread.join();
+ mAcceptThread = null;
+ } catch (InterruptedException ex) {
+ Log.w(TAG, "mAcceptThread close error" + ex);
+ }
+ }
+ mServerSocket = null;
+ mConnSocket = null;
+
+ if (mServerSession != null) {
+ mServerSession.close();
+ mServerSession = null;
+ }
+
+ mHasStarted = false;
+ if (stopSelfResult(mStartId)) {
+ if (VERBOSE)
+ Log.e(TAG, "successfully stopped map service");
+ }
+ }
+
+ private final void startObexServerSession() throws IOException {
+ if (VERBOSE)
+ Log.v(TAG, "Map Service startObexServerSession");
+
+ // acquire the wakeLock before start Obex transaction thread
+ if (mWakeLock == null) {
+ PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK,
+ "StartingObexMapTransaction");
+ mWakeLock.setReferenceCounted(false);
+ mWakeLock.acquire();
+ }
+
+ mMapServer = new BluetoothMasObexServer(mSessionStatusHandler,
+ mRemoteDevice, this);
+ synchronized (this) {
+ mAuth = new BluetoothMapAuthenticator(mSessionStatusHandler);
+ mAuth.setChallenged(false);
+ mAuth.setCancelled(false);
+ }
+ BluetoothMapRfcommTransport transport = new BluetoothMapRfcommTransport(
+ mConnSocket);
+ mServerSession = new ServerSession(transport, mMapServer, mAuth);
+ if (VERBOSE) {
+ Log.e(TAG, "startObexServerSession() success!");
+ }
+ }
+
+ private void stopObexServerSession() {
+ if (VERBOSE)
+ Log.e(TAG, "Map Service stopObexServerSession");
+
+ // Release the wake lock if obex transaction is over
+ if (mWakeLock != null) {
+ mWakeLock.release();
+ mWakeLock = null;
+ }
+
+ if (mServerSession != null) {
+ mServerSession.close();
+ mServerSession = null;
+ }
+
+ mAcceptThread = null;
+
+ try {
+ closeSocket(false, true);
+ mConnSocket = null;
+ } catch (IOException e) {
+ Log.e(TAG, "closeSocket error: " + e.toString());
+ }
+ // Last obex transaction is finished, we start to listen for incoming
+ // connection again
+ if (mAdapter.isEnabled()) {
+ startRfcommSocketListener();
+ }
+ }
+
+ private void notifyAuthKeyInput(final String key) {
+ synchronized (mAuth) {
+ if (key != null) {
+ mAuth.setSessionKey(key);
+ }
+ mAuth.setChallenged(true);
+ mAuth.notify();
+ }
+ }
+
+ private void notifyAuthCancelled() {
+ synchronized (mAuth) {
+ mAuth.setCancelled(true);
+ mAuth.notify();
+ }
+ }
+
+ /**
+ * A thread that runs in the background waiting for remote rfcomm
+ * connect.Once a remote socket connected, this thread shall be
+ * shutdown.When the remote disconnect,this thread shall run again waiting
+ * for next request.
+ */
+ private class SocketAcceptThread extends Thread {
+
+ private boolean stopped = false;
+
+ @Override
+ public void run() {
+ while (!stopped) {
+ try {
+ mConnSocket = mServerSocket.accept();
+
+ mRemoteDevice = mConnSocket.getRemoteDevice();
+ if (mRemoteDevice == null) {
+ Log.i(TAG, "getRemoteDevice() = null");
+ break;
+ }
+ sRemoteDeviceName = mRemoteDevice.getName();
+ // In case getRemoteName failed and return null
+ if (TextUtils.isEmpty(sRemoteDeviceName)) {
+ sRemoteDeviceName = getString(R.string.defaultname);
+ }
+ boolean trust = true;
+ if (VERBOSE)
+ Log.e(TAG, "GetTrustState() = " + trust);
+
+ if (trust) {
+ try {
+ if (VERBOSE) Log.e( TAG, "incomming connection accepted from: "
+ + sRemoteDeviceName + " automatically as trusted device");
+ startObexServerSession();
+ } catch (IOException ex) {
+ Log.e(TAG, "catch exception starting obex server session"
+ + ex.toString());
+ }
+ } else {
+ createMapNotification(ACCESS_REQUEST_ACTION);
+ if (VERBOSE) Log.e(TAG, "incomming connection accepted from: "
+ + sRemoteDeviceName);
+ mSessionStatusHandler.sendMessageDelayed(
+ mSessionStatusHandler.obtainMessage(USER_TIMEOUT),
+ USER_CONFIRM_TIMEOUT_VALUE);
+ }
+ stopped = true; // job done ,close this thread;
+ } catch (IOException ex) {
+ if (stopped) {
+ break;
+ }
+ if (VERBOSE)
+ Log.e(TAG, "Accept exception: " + ex.toString());
+ }
+ }
+ }
+
+ void shutdown() {
+ stopped = true;
+ interrupt();
+ }
+ }
+
+ private final Handler mSessionStatusHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (VERBOSE)
+ Log.e(TAG, "Handler(): got msg=" + msg.what);
+
+ switch (msg.what) {
+ case START_LISTENER:
+ if (mAdapter.isEnabled()) {
+ startRfcommSocketListener();
+ } else {
+ closeService();// release all resources
+ }
+ break;
+ case USER_TIMEOUT:
+ Intent intent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
+ sendBroadcast(intent);
+ removeMapNotification(NOTIFICATION_ID_ACCESS);
+ break;
+ case AUTH_TIMEOUT:
+ Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
+ sendBroadcast(i);
+ removeMapNotification(NOTIFICATION_ID_AUTH);
+ notifyAuthCancelled();
+ break;
+ case MSG_SERVERSESSION_CLOSE:
+ stopObexServerSession();
+ break;
+ case MSG_SESSION_ESTABLISHED:
+ break;
+ case MSG_SESSION_DISCONNECTED:
+ // case MSG_SERVERSESSION_CLOSE will handle ,so just skip
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ private void createMapNotification(String action) {
+ }
+
+ private void removeMapNotification(int id) {
+ Context context = getApplicationContext();
+ NotificationManager nm = (NotificationManager) context
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.cancel(id);
+ }
+
+ public static String getRemoteDeviceName() {
+ return sRemoteDeviceName;
+ }
+};
diff --git a/src/com/android/bluetooth/map/BluetoothMasSpecParams.java b/src/com/android/bluetooth/map/BluetoothMasSpecParams.java
new file mode 100644
index 000000000..dfeb7561a
--- /dev/null
+++ b/src/com/android/bluetooth/map/BluetoothMasSpecParams.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map;
+
+
+public final class BluetoothMasSpecParams {
+
+ public static final int MAS_TAG_MAX_LIST_COUNT = 0x01;
+ public static final int MAS_TAG_LIST_START_OFFSET = 0x02;
+ public static final int MAS_TAG_FILTER_MESSAGE_TYPE = 0x03;
+ public static final int MAS_TAG_FILTER_PERIOD_BEGIN = 0x04;
+ public static final int MAS_TAG_FILTER_PERIOD_END = 0x05;
+ public static final int MAS_TAG_FILTER_READ_STATUS = 0x06;
+ public static final int MAS_TAG_FILTER_RECIPIENT = 0x07;
+ public static final int MAS_TAG_FILTER_ORIGINATOR = 0x08;
+ public static final int MAS_TAG_FILTER_PRIORITY = 0x09;
+ public static final int MAS_TAG_ATTACHMENT = 0x0A;
+ public static final int MAS_TAG_TRANSPARENT = 0x0B;
+ public static final int MAS_TAG_RETRY = 0x0C;
+ public static final int MAS_TAG_NEW_MESSAGE = 0x0D;
+ public static final int MAS_TAG_NOTIFICATION_STATUS = 0x0E;
+ public static final int MAS_TAG_MAS_INSTANCE_ID = 0x0F;
+ public static final int MAS_TAG_PARAMETER_MASK = 0x10;
+ public static final int MAS_TAG_FOLDER_LISTING_SIZE = 0x11;
+ public static final int MAS_TAG_MESSAGE_LISTING_SIZE = 0x12;
+ public static final int MAS_TAG_SUBJECT_LENGTH = 0x13;
+ public static final int MAS_TAG_CHARSET = 0x14;
+ public static final int MAS_TAG_FRACTION_REQUEST = 0x15;
+ public static final int MAS_TAG_FRACTION_DELIVER = 0x16;
+ public static final int MAS_TAG_STATUS_INDICATOR = 0x17;
+ public static final int MAS_TAG_STATUS_VALUE = 0x18;
+ public static final int MAS_TAG_MSE_TIME = 0x19;
+
+ public static final int MAS_TAG_MAX_LIST_COUNT_LEN = 0x02;
+ public static final int MAS_TAG_LIST_START_OFFSET_LEN = 0x02;
+ public static final int MAS_TAG_SUBJECT_LENGTH_LEN = 0x01;
+ public static final int MAS_TAG_FILTER_MESSAGE_TYPE_LEN = 0x01;
+ public static final int MAS_TAG_FILTER_READ_STATUS_LEN = 0x01;
+ public static final int MAS_TAG_FILTER_PRIORITY_LEN = 0x01;
+ public static final int MAS_TAG_PARAMETER_MASK_LEN = 0x04;
+ public static final int MAS_TAG_ATTACHMENT_LEN = 0x01;
+ public static final int MAS_TAG_TRANSPARENT_LEN = 0x01;
+ public static final int MAS_TAG_RETRY_LEN = 0x01;
+ public static final int MAS_TAG_NEW_MESSAGE_LEN = 0x01;
+ public static final int MAS_TAG_NOTIFICATION_STATUS_LEN = 0x01;
+ public static final int MAS_TAG_MAS_INSTANCE_ID_LEN = 0x01;
+ public static final int MAS_TAG_FOLDER_LISTING_SIZE_LEN = 0x02;
+ public static final int MAS_TAG_MESSAGE_LISTING_SIZE_LEN = 0x02;
+ public static final int MAS_TAG_CHARSET_LEN = 0x01;
+ public static final int MAS_TAG_FRACTION_REQUEST_LEN = 0x01;
+ public static final int MAS_TAG_FRACTION_DELIVER_LEN = 0x01;
+ public static final int MAS_TAG_STATUS_INDICATOR_LEN = 0x01;
+ public static final int MAS_TAG_STATUS_VALUE_LEN = 0x01;
+ public static final int MAS_TAG_MSE_TIME_LEN = 0x14;
+
+ public static final int MAS_DEFAULT_MAX_LIST_COUNT = 1024;
+ public static final int MAS_DEFAULT_SUBJECT_LENGTH = 255;
+ public static final int MAS_DEFAULT_PARAMETER_MASK = 0xFFFF;
+
+ public static final int MAS_FRACTION_REQUEST_NOT_SET = 0x02;
+
+ public static final int MAS_TAG_MAX_LIST_COUNT_MIN_VAL = 0x0;
+ public static final int MAS_TAG_MAX_LIST_COUNT_MAX_VAL = 0xFFFF;
+ public static final int MAS_TAG_LIST_START_OFFSET_MIN_VAL = 0x00;
+ public static final int MAS_TAG_LIST_START_OFFSET_MAX_VAL = 0xFFFF;
+ public static final int MAS_TAG_SUBJECT_LENGTH_MIN_VAL = 0x01;
+ public static final int MAS_TAG_SUBJECT_LENGTH_MAX_VAL = 0xFF;
+ public static final int MAS_TAG_FILTER_MESSAGE_TYPE_MIN_VAL = 0x00;
+ public static final int MAS_TAG_FILTER_MESSAGE_TYPE_MAX_VAL = 0x0F;
+ public static final int MAS_TAG_FILTER_READ_STATUS_MIN_VAL = 0x00;
+ public static final int MAS_TAG_FILTER_READ_STATUS_MAX_VAL = 0x02;
+ public static final int MAS_TAG_FILTER_PRIORITY_MIN_VAL = 0x00;
+ public static final int MAS_TAG_FILTER_PRIORITY_MAX_VAL = 0x02;
+ public static final int MAS_TAG_PARAMETER_MASK_MIN_VAL = 0x0;
+ public static final int MAS_TAG_PARAMETER_MASK_MAX_VAL = 0xFFFF;
+ public static final int MAS_TAG_ATTACHMENT_MIN_VAL = 0x00;
+ public static final int MAS_TAG_ATTACHMENT_MAX_VAL = 0x01;
+ public static final int MAS_TAG_TRANSPARENT_MIN_VAL = 0x00;
+ public static final int MAS_TAG_TRANSPARENT_MAX_VAL = 0x01;
+ public static final int MAS_TAG_RETRY_MIN_VAL = 0x00;
+ public static final int MAS_TAG_RETRY_MAX_VAL = 0x01;
+ public static final int MAS_TAG_NEW_MESSAGE_MIN_VAL = 0x00;
+ public static final int MAS_TAG_NEW_MESSAGE_MAX_VAL = 0x01;
+ public static final int MAS_TAG_NOTIFICATION_STATUS_MIN_VAL = 0x00;
+ public static final int MAS_TAG_NOTIFICATION_STATUS_MAX_VAL = 0x01;
+ public static final int MAS_TAG_MAS_INSTANCE_ID_MIN_VAL = 0x00;
+ public static final int MAS_TAG_MAS_INSTANCE_ID_MAX_VAL = 0xFF;
+ public static final int MAS_TAG_FOLDER_LISTING_SIZE_MIN_VAL = 0x00;
+ public static final int MAS_TAG_FOLDER_LISTING_SIZE_MAX_VAL = 0xFFFF;
+ public static final int MAS_TAG_MESSAGE_LISTING_SIZE_MIN_VAL = 0x00;
+ public static final int MAS_TAG_MESSAGE_LISTING_SIZE_MAX_VAL = 0xFFFF;
+ public static final int MAS_TAG_CHARSET_MIN_VAL = 0x00;
+ public static final int MAS_TAG_CHARSET_MAX_VAL = 0x01;
+ public static final int MAS_TAG_FRACTION_REQUEST_MIN_VAL = 0x00;
+ public static final int MAS_TAG_FRACTION_REQUEST_MAX_VAL = 0x01;
+ public static final int MAS_TAG_FRACTION_DELIVER_MIN_VAL = 0x00;
+ public static final int MAS_TAG_FRACTION_DELIVER_MAX_VAL = 0x01;
+ public static final int MAS_TAG_STATUS_INDICATOR_MIN_VAL = 0x00;
+ public static final int MAS_TAG_STATUS_INDICATOR_MAX_VAL = 0x01;
+ public static final int MAS_TAG_STATUS_VALUE_MIN_VAL = 0x00;
+ public static final int MAS_TAG_STATUS_VALUE_MAX_VAL = 0x01;
+
+
+
+};
+
diff --git a/src/com/android/bluetooth/map/BluetoothMasTestActivity.java b/src/com/android/bluetooth/map/BluetoothMasTestActivity.java
new file mode 100644
index 000000000..e1abf82b5
--- /dev/null
+++ b/src/com/android/bluetooth/map/BluetoothMasTestActivity.java
@@ -0,0 +1,291 @@
+ /*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map;
+
+
+import com.android.bluetooth.R;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.Preference;
+import android.text.InputFilter;
+import android.text.TextWatcher;
+import android.text.InputFilter.LengthFilter;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+
+/**
+ * MapActivity shows two dialogues: One for accepting incoming map request and
+ * the other prompts the user to enter a session key for authentication with a
+ * remote Bluetooth device.
+ */
+public class BluetoothMasTestActivity extends Activity implements
+ DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener, TextWatcher {
+ private static final String TAG = "BluetoothMasActivity";
+
+ private static final boolean V = BluetoothMasService.VERBOSE;
+
+ private static final int BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH = 16;
+
+ private static final int DIALOG_YES_NO_CONNECT = 1;
+
+ private static final int DIALOG_YES_NO_AUTH = 2;
+
+ private static final String KEY_USER_TIMEOUT = "user_timeout";
+
+ private View mView;
+
+ private EditText mKeyView;
+
+ private TextView messageView;
+
+ private String mSessionKey = "";
+
+ private int mCurrentDialog;
+
+ private Button mOkButton;
+
+ private CheckBox mAlwaysAllowed;
+
+ private boolean mTimeout = false;
+
+ private boolean mAlwaysAllowedValue = true;
+
+ private static final int DISMISS_TIMEOUT_DIALOG = 0;
+
+ private static final int DISMISS_TIMEOUT_DIALOG_VALUE = 2000;
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!BluetoothMasService.USER_CONFIRM_TIMEOUT_ACTION.equals(intent.getAction())) {
+ return;
+ }
+ onTimeout();
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent i = getIntent();
+ String action = i.getAction();
+ if (action.equals(BluetoothMasService.ACCESS_REQUEST_ACTION)) {
+ showMapDialog(DIALOG_YES_NO_CONNECT);
+ mCurrentDialog = DIALOG_YES_NO_CONNECT;
+ } else if (action.equals(BluetoothMasService.AUTH_CHALL_ACTION)) {
+ showMapDialog(DIALOG_YES_NO_AUTH);
+ mCurrentDialog = DIALOG_YES_NO_AUTH;
+ } else {
+ Log.e(TAG, "Error: this activity may be started only with intent "
+ + "MAP_ACCESS_REQUEST");
+ finish();
+ }
+ registerReceiver(mReceiver, new IntentFilter(
+ BluetoothMasService.USER_CONFIRM_TIMEOUT_ACTION));
+ }
+
+ private void showMapDialog(int id) {
+ }
+
+ private String createDisplayText(final int id) {
+ String mRemoteName = BluetoothMasService.getRemoteDeviceName();
+ return null;
+ }
+
+ private View createView(final int id) {
+ switch (id) {
+ case DIALOG_YES_NO_CONNECT:
+ mView = getLayoutInflater().inflate(R.layout.access, null);
+ messageView = (TextView)mView.findViewById(R.id.message);
+ messageView.setText(createDisplayText(id));
+ mAlwaysAllowed = (CheckBox)mView.findViewById(R.id.alwaysallowed);
+ mAlwaysAllowed.setChecked(true);
+ mAlwaysAllowed.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (isChecked) {
+ mAlwaysAllowedValue = true;
+ } else {
+ mAlwaysAllowedValue = false;
+ }
+ }
+ });
+ return mView;
+ case DIALOG_YES_NO_AUTH:
+ mView = getLayoutInflater().inflate(R.layout.auth, null);
+ messageView = (TextView)mView.findViewById(R.id.message);
+ messageView.setText(createDisplayText(id));
+ mKeyView = (EditText)mView.findViewById(R.id.text);
+ mKeyView.addTextChangedListener(this);
+ mKeyView.setFilters(new InputFilter[] {
+ new LengthFilter(BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH)
+ });
+ return mView;
+ default:
+ return null;
+ }
+ }
+
+ private void onPositive() {
+ if (!mTimeout) {
+ if (mCurrentDialog == DIALOG_YES_NO_CONNECT) {
+ sendIntentToReceiver(BluetoothMasService.ACCESS_ALLOWED_ACTION,
+ BluetoothMasService.EXTRA_ALWAYS_ALLOWED, mAlwaysAllowedValue);
+ } else if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
+ sendIntentToReceiver(BluetoothMasService.AUTH_RESPONSE_ACTION,
+ BluetoothMasService.EXTRA_SESSION_KEY, mSessionKey);
+ mKeyView.removeTextChangedListener(this);
+ }
+ }
+ mTimeout = false;
+ finish();
+ }
+
+ private void onNegative() {
+ if (mCurrentDialog == DIALOG_YES_NO_CONNECT) {
+ sendIntentToReceiver(BluetoothMasService.ACCESS_DISALLOWED_ACTION, null, null);
+ } else if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
+ sendIntentToReceiver(BluetoothMasService.AUTH_CANCELLED_ACTION, null, null);
+ mKeyView.removeTextChangedListener(this);
+ }
+ finish();
+ }
+
+ private void sendIntentToReceiver(final String intentName, final String extraName,
+ final String extraValue) {
+ Intent intent = new Intent(intentName);
+ intent.setClassName(BluetoothMasService.THIS_PACKAGE_NAME, BluetoothMasReceiver.class
+ .getName());
+ if (extraName != null) {
+ intent.putExtra(extraName, extraValue);
+ }
+ sendBroadcast(intent);
+ }
+
+ private void sendIntentToReceiver(final String intentName, final String extraName,
+ final boolean extraValue) {
+ Intent intent = new Intent(intentName);
+ intent.setClassName(BluetoothMasService.THIS_PACKAGE_NAME, BluetoothMasReceiver.class
+ .getName());
+ if (extraName != null) {
+ intent.putExtra(extraName, extraValue);
+ }
+ sendBroadcast(intent);
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
+ mSessionKey = mKeyView.getText().toString();
+ }
+ onPositive();
+ break;
+
+ case DialogInterface.BUTTON_NEGATIVE:
+ onNegative();
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ private void onTimeout() {
+ // TODO - Implement appropriate timeout function
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ mTimeout = savedInstanceState.getBoolean(KEY_USER_TIMEOUT);
+ if (V) Log.e(TAG, "onRestoreInstanceState() mTimeout: " + mTimeout);
+
+ if (mTimeout) {
+ onTimeout();
+ }
+
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_USER_TIMEOUT, mTimeout);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(mReceiver);
+ }
+
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ return true;
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int before, int after) {
+ }
+
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ public void afterTextChanged(android.text.Editable s) {
+ if (s.length() > 0) {
+ mOkButton.setEnabled(true);
+ }
+ }
+
+ private final Handler mTimeoutHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case DISMISS_TIMEOUT_DIALOG:
+ if (V) Log.e(TAG, "Received DISMISS_TIMEOUT_DIALOG msg.");
+ finish();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+}
+
diff --git a/src/com/android/bluetooth/map/BluetoothMns.java b/src/com/android/bluetooth/map/BluetoothMns.java
new file mode 100644
index 000000000..c1387d8c8
--- /dev/null
+++ b/src/com/android/bluetooth/map/BluetoothMns.java
@@ -0,0 +1,3477 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map;
+
+
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSocket;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.CursorJoiner;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
+import android.os.Process;
+import android.text.format.Time;
+import android.util.Log;
+
+import com.android.bluetooth.map.MapUtils.MapUtils;
+import com.android.bluetooth.map.MapUtils.EmailUtils;
+
+import javax.obex.*;
+
+/**
+ * This class run an MNS session.
+ */
+public class BluetoothMns {
+ private static final String TAG = "BtMns";
+
+ private static final boolean D = BluetoothMasService.DEBUG;
+
+ private static final boolean V = BluetoothMasService.VERBOSE;
+
+ public static final int RFCOMM_ERROR = 10;
+
+ public static final int RFCOMM_CONNECTED = 11;
+
+ public static final int SDP_RESULT = 12;
+
+ public static final int MNS_CONNECT = 13;
+
+ public static final int MNS_DISCONNECT = 14;
+
+ public static final int MNS_SEND_EVENT = 15;
+
+ private static final int CONNECT_WAIT_TIMEOUT = 45000;
+
+ private static final int CONNECT_RETRY_TIME = 100;
+
+ private static final short MNS_UUID16 = 0x1133;
+
+ public static final String NEW_MESSAGE = "NewMessage";
+
+ public static final String DELIVERY_SUCCESS = "DeliverySuccess";
+
+ public static final String SENDING_SUCCESS = "SendingSuccess";
+
+ public static final String DELIVERY_FAILURE = "DeliveryFailure";
+
+ public static final String SENDING_FAILURE = "SendingFailure";
+
+ public static final String MEMORY_FULL = "MemoryFull";
+
+ public static final String MEMORY_AVAILABLE = "MemoryAvailable";
+
+ public static final String MESSAGE_DELETED = "MessageDeleted";
+
+ public static final String MESSAGE_SHIFT = "MessageShift";
+
+ public static final int MMS_HDLR_CONSTANT = 100000;
+
+ public static final int EMAIL_HDLR_CONSTANT = 200000;
+
+ private static final int MSG_CP_INBOX_TYPE = 1;
+
+ private static final int MSG_CP_SENT_TYPE = 2;
+
+ private static final int MSG_CP_DRAFT_TYPE = 3;
+
+ private static final int MSG_CP_OUTBOX_TYPE = 4;
+
+ private static final int MSG_CP_FAILED_TYPE = 5;
+
+ private static final int MSG_CP_QUEUED_TYPE = 6;
+
+ private Context mContext;
+
+ private BluetoothAdapter mAdapter;
+
+ private BluetoothMnsObexSession mSession;
+
+ private int mStartId = -1;
+
+ private ObexTransport mTransport;
+
+ private HandlerThread mHandlerThread;
+
+ private EventHandler mSessionHandler;
+
+ private BluetoothDevice mDestination;
+
+ private MapUtils mu = null;
+
+ public static final ParcelUuid BluetoothUuid_ObexMns = ParcelUuid
+ .fromString("00001133-0000-1000-8000-00805F9B34FB");
+
+ private long mTimestamp;
+
+ public String deletedFolderName = null;
+
+ public BluetoothMns(Context context) {
+ /* check Bluetooth enable status */
+ /*
+ * normally it's impossible to reach here if BT is disabled. Just check
+ * for safety
+ */
+
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mContext = context;
+
+ mDestination = BluetoothMasService.mRemoteDevice;
+
+ mu = new MapUtils();
+
+ if (!mAdapter.isEnabled()) {
+ Log.e(TAG, "Can't send event when Bluetooth is disabled ");
+ return;
+ }
+
+ if (mHandlerThread == null) {
+ if (V) Log.v(TAG, "Create handler thread for batch ");
+ mHandlerThread = new HandlerThread("Bt MNS Transfer Handler",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ mHandlerThread.start();
+ mSessionHandler = new EventHandler(mHandlerThread.getLooper());
+ }
+ }
+
+ public Handler getHandler() {
+ return mSessionHandler;
+ }
+
+ /*
+ * Receives events from mConnectThread & mSession back in the main thread.
+ */
+ private class EventHandler extends Handler {
+ public EventHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ Log.d(TAG, " Handle Message " + msg.what);
+ switch (msg.what) {
+ case MNS_CONNECT:
+ if (mSession != null) {
+ Log.d(TAG, "Disconnect previous obex connection");
+ mSession.disconnect();
+ mSession = null;
+ }
+ start((BluetoothDevice) msg.obj);
+ break;
+ case MNS_DISCONNECT:
+ deregisterUpdates();
+ stop();
+ break;
+ case SDP_RESULT:
+ if (V) Log.v(TAG, "SDP request returned " + msg.arg1
+ + " (" + (System.currentTimeMillis() - mTimestamp + " ms)"));
+ if (!((BluetoothDevice) msg.obj).equals(mDestination)) {
+ return;
+ }
+ try {
+ mContext.unregisterReceiver(mReceiver);
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ if (msg.arg1 > 0) {
+ mConnectThread = new SocketConnectThread(mDestination,
+ msg.arg1);
+ mConnectThread.start();
+ } else {
+ /* SDP query fail case */
+ Log.e(TAG, "SDP query failed!");
+ }
+
+ break;
+
+ /*
+ * RFCOMM connect fail is for outbound share only! Mark batch
+ * failed, and all shares in batch failed
+ */
+ case RFCOMM_ERROR:
+ if (V) Log.v(TAG, "receive RFCOMM_ERROR msg");
+ mConnectThread = null;
+
+ break;
+ /*
+ * RFCOMM connected. Do an OBEX connect by starting the session
+ */
+ case RFCOMM_CONNECTED:
+ if (V) Log.v(TAG, "Transfer receive RFCOMM_CONNECTED msg");
+ mConnectThread = null;
+ mTransport = (ObexTransport) msg.obj;
+ startObexSession();
+ registerUpdates();
+
+ break;
+
+ /* Handle the error state of an Obex session */
+ case BluetoothMnsObexSession.MSG_SESSION_ERROR:
+ if (V) Log.v(TAG, "receive MSG_SESSION_ERROR");
+ deregisterUpdates();
+ mSession.disconnect();
+ mSession = null;
+ break;
+
+ case BluetoothMnsObexSession.MSG_CONNECT_TIMEOUT:
+ if (V) Log.v(TAG, "receive MSG_CONNECT_TIMEOUT");
+ /*
+ * for outbound transfer, the block point is
+ * BluetoothSocket.write() The only way to unblock is to tear
+ * down lower transport
+ */
+ try {
+ if (mTransport == null) {
+ Log.v(TAG,"receive MSG_SHARE_INTERRUPTED but " +
+ "mTransport = null");
+ } else {
+ mTransport.close();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "failed to close mTransport");
+ }
+ if (V) Log.v(TAG, "mTransport closed ");
+
+ break;
+
+ case MNS_SEND_EVENT:
+ sendEvent((String) msg.obj);
+ break;
+ }
+ }
+ }
+
+ /*
+ * Class to hold message handle for MCE Initiated operation
+ */
+ public class BluetoothMnsMsgHndlMceInitOp {
+ public String msgHandle;
+ Time time;
+ }
+
+ /*
+ * Keep track of Message Handles on which the operation was
+ * initiated by MCE
+ */
+ List<BluetoothMnsMsgHndlMceInitOp> opList = new ArrayList<BluetoothMnsMsgHndlMceInitOp>();
+
+ /*
+ * Adds the Message Handle to the list for tracking
+ * MCE initiated operation
+ */
+ public void addMceInitiatedOperation(String msgHandle) {
+ BluetoothMnsMsgHndlMceInitOp op = new BluetoothMnsMsgHndlMceInitOp();
+ op.msgHandle = msgHandle;
+ op.time = new Time();
+ op.time.setToNow();
+ opList.add(op);
+ }
+ /*
+ * Removes the Message Handle from the list for tracking
+ * MCE initiated operation
+ */
+ public void removeMceInitiatedOperation(int location) {
+ opList.remove(location);
+ }
+
+ /*
+ * Finds the location in the list of the given msgHandle, if
+ * available. "+" indicates the next (any) operation
+ */
+ public int findLocationMceInitiatedOperation( String msgHandle) {
+ int location = -1;
+
+ Time currentTime = new Time();
+ currentTime.setToNow();
+
+ for ( BluetoothMnsMsgHndlMceInitOp op: opList) {
+ // Remove stale entries
+ if ( currentTime.toMillis(false) - op.time.toMillis(false) > 10000 ) {
+ opList.remove(op);
+ }
+ }
+
+ for ( BluetoothMnsMsgHndlMceInitOp op: opList) {
+ if ( op.msgHandle.equalsIgnoreCase(msgHandle)){
+ location = opList.indexOf(op);
+ break;
+ }
+ }
+
+ if (location == -1) {
+ for ( BluetoothMnsMsgHndlMceInitOp op: opList) {
+ if ( op.msgHandle.equalsIgnoreCase("+")) {
+ location = opList.indexOf(op);
+ break;
+ }
+ }
+ }
+ return location;
+ }
+
+
+ /**
+ * Post a MNS Event to the MNS thread
+ */
+ public void sendMnsEvent(String msg, String handle, String folder,
+ String old_folder, String msgType) {
+ int location = -1;
+
+ /* Send the notification, only if it was not initiated
+ * by MCE. MEMORY_FULL and MEMORY_AVAILABLE cannot be
+ * MCE initiated
+ */
+ if ( msg.equals(MEMORY_AVAILABLE) || msg.equals(MEMORY_FULL)) {
+ location = -1;
+ } else {
+ location = findLocationMceInitiatedOperation(handle);
+ }
+
+ if (location == -1) {
+ String str = mu.mapEventReportXML(msg, handle, folder, old_folder,
+ msgType);
+ mSessionHandler.obtainMessage(MNS_SEND_EVENT, -1, -1, str)
+ .sendToTarget();
+ } else {
+ removeMceInitiatedOperation(location);
+ }
+ }
+
+ /**
+ * Push the message over Obex client session
+ */
+ private void sendEvent(String str) {
+ if (str != null && (str.length() > 0)) {
+
+ Log.d(TAG, "--------------");
+ Log.d(TAG, " CONTENT OF EVENT REPORT FILE: " + str);
+
+ final String FILENAME = "EventReport";
+ FileOutputStream fos = null;
+ File file = new File(mContext.getFilesDir() + "/" + FILENAME);
+ file.delete();
+ try {
+ fos = mContext.openFileOutput(FILENAME, Context.MODE_PRIVATE);
+ fos.write(str.getBytes());
+ fos.flush();
+ fos.close();
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ File fileR = new File(mContext.getFilesDir() + "/" + FILENAME);
+ if (fileR.exists() == true) {
+ Log.d(TAG, " Sending event report file ");
+ mSession.sendEvent(fileR, (byte) 0);
+ } else {
+ Log.d(TAG, " ERROR IN CREATING SEND EVENT OBJ FILE");
+ }
+ }
+ }
+
+ private boolean updatesRegistered = false;
+
+ /**
+ * Register with content provider to receive updates
+ * of change on cursor.
+ */
+ private void registerUpdates() {
+
+ Log.d(TAG, "REGISTER MNS UPDATES");
+
+ Uri smsUri = Uri.parse("content://sms/");
+ crSmsA = mContext.getContentResolver().query(smsUri,
+ new String[] { "_id", "body", "type" }, null, null, "_id asc");
+ crSmsB = mContext.getContentResolver().query(smsUri,
+ new String[] { "_id", "body", "type" }, null, null, "_id asc");
+
+ Uri smsInboxUri = Uri.parse("content://sms/inbox/");
+ crSmsInboxA = mContext.getContentResolver().query(smsInboxUri,
+ new String[] { "_id", "body", "type" }, null, null, "_id asc");
+ crSmsInboxB = mContext.getContentResolver().query(smsInboxUri,
+ new String[] { "_id", "body", "type" }, null, null, "_id asc");
+
+ Uri smsSentUri = Uri.parse("content://sms/sent/");
+ crSmsSentA = mContext.getContentResolver().query(smsSentUri,
+ new String[] { "_id", "body", "type" }, null, null, "_id asc");
+ crSmsSentB = mContext.getContentResolver().query(smsSentUri,
+ new String[] { "_id", "body", "type" }, null, null, "_id asc");
+
+ Uri smsDraftUri = Uri.parse("content://sms/draft/");
+ crSmsDraftA = mContext.getContentResolver().query(smsDraftUri,
+ new String[] { "_id", "body", "type" }, null, null, "_id asc");
+ crSmsDraftB = mContext.getContentResolver().query(smsDraftUri,
+ new String[] { "_id", "body", "type" }, null, null, "_id asc");
+
+ Uri smsOutboxUri = Uri.parse("content://sms/outbox/");
+ crSmsOutboxA = mContext.getContentResolver().query(smsOutboxUri,
+ new String[] { "_id", "body", "type" }, null, null, "_id asc");
+ crSmsOutboxB = mContext.getContentResolver().query(smsOutboxUri,
+ new String[] { "_id", "body", "type" }, null, null, "_id asc");
+
+ Uri smsFailedUri = Uri.parse("content://sms/failed/");
+ crSmsFailedA = mContext.getContentResolver().query(smsFailedUri,
+ new String[] { "_id", "body", "type" }, null, null, "_id asc");
+ crSmsFailedB = mContext.getContentResolver().query(smsFailedUri,
+ new String[] { "_id", "body", "type" }, null, null, "_id asc");
+
+ Uri smsQueuedUri = Uri.parse("content://sms/queued/");
+ crSmsQueuedA = mContext.getContentResolver().query(smsQueuedUri,
+ new String[] { "_id", "body", "type" }, null, null, "_id asc");
+ crSmsQueuedB = mContext.getContentResolver().query(smsQueuedUri,
+ new String[] { "_id", "body", "type" }, null, null, "_id asc");
+
+ Uri smsObserverUri = Uri.parse("content://mms-sms/");
+ mContext.getContentResolver().registerContentObserver(smsObserverUri,
+ true, smsContentObserver);
+
+ Uri mmsUri = Uri.parse("content://mms/");
+ crMmsA = mContext.getContentResolver()
+ .query(mmsUri, new String[] { "_id", "read", "m_type" }, null,
+ null, "_id asc");
+ crMmsB = mContext.getContentResolver()
+ .query(mmsUri, new String[] { "_id", "read", "m_type" }, null,
+ null, "_id asc");
+
+ Uri mmsOutboxUri = Uri.parse("content://mms/outbox/");
+ crMmsOutboxA = mContext.getContentResolver()
+ .query(mmsOutboxUri, new String[] { "_id", "read", "m_type" },
+ null, null, "_id asc");
+ crMmsOutboxB = mContext.getContentResolver()
+ .query(mmsOutboxUri, new String[] { "_id", "read", "m_type" },
+ null, null, "_id asc");
+ Uri mmsDraftUri = Uri.parse("content://mms/drafts/");
+ crMmsDraftA = mContext.getContentResolver()
+ .query(mmsDraftUri, new String[] { "_id", "read", "m_type" },
+ null, null, "_id asc");
+ crMmsDraftB = mContext.getContentResolver()
+ .query(mmsDraftUri, new String[] { "_id", "read", "m_type" },
+ null, null, "_id asc");
+ Uri mmsInboxUri = Uri.parse("content://mms/inbox/");
+ crMmsInboxA = mContext.getContentResolver()
+ .query(mmsInboxUri, new String[] { "_id", "read", "m_type" },
+ null, null, "_id asc");
+ crMmsInboxB = mContext.getContentResolver()
+ .query(mmsInboxUri, new String[] { "_id", "read", "m_type" },
+ null, null, "_id asc");
+
+ Uri mmsSentUri = Uri.parse("content://mms/sent/");
+ crMmsSentA = mContext.getContentResolver()
+ .query(mmsSentUri, new String[] { "_id", "read", "m_type" },
+ null, null, "_id asc");
+ crMmsSentB = mContext.getContentResolver()
+ .query(mmsSentUri, new String[] { "_id", "read", "m_type" },
+ null, null, "_id asc");
+
+ //email start
+ Uri emailUri = Uri.parse("content://com.android.email.provider/message");
+ crEmailA = mContext.getContentResolver().query(emailUri,
+ new String[] { "_id", "mailboxkey" }, null, null, "_id asc");
+ crEmailB = mContext.getContentResolver().query(emailUri,
+ new String[] { "_id", "mailboxkey" }, null, null, "_id asc");
+
+ EmailUtils eu = new EmailUtils();
+ String emailInboxCondition = eu.getWhereIsQueryForTypeEmail("inbox", mContext);
+ crEmailInboxA = mContext.getContentResolver().query(emailUri,
+ new String[] { "_id", "mailboxkey" }, emailInboxCondition, null, "_id asc");
+ crEmailInboxB = mContext.getContentResolver().query(emailUri,
+ new String[] { "_id", "mailboxkey" }, emailInboxCondition, null, "_id asc");
+
+ String emailSentCondition = eu.getWhereIsQueryForTypeEmail("sent", mContext);
+ crEmailSentA = mContext.getContentResolver().query(emailUri,
+ new String[] {"_id", "mailboxkey" }, emailSentCondition, null, "_id asc");
+ crEmailSentB = mContext.getContentResolver().query(emailUri,
+ new String[] {"_id", "mailboxkey" }, emailSentCondition, null, "_id asc");
+
+ String emailDraftCondition = eu.getWhereIsQueryForTypeEmail("drafts", mContext);
+ crEmailDraftA = mContext.getContentResolver().query(emailUri,
+ new String[] {"_id", "mailboxkey"}, emailDraftCondition, null, "_id asc");
+ crEmailDraftB = mContext.getContentResolver().query(emailUri,
+ new String[] {"_id", "mailboxkey" }, emailDraftCondition, null, "_id asc");
+
+ String emailOutboxCondition = eu.getWhereIsQueryForTypeEmail("outbox", mContext);
+ crEmailOutboxA = mContext.getContentResolver().query(emailUri,
+ new String[] {"_id", "mailboxkey"}, emailOutboxCondition, null, "_id asc");
+ crEmailOutboxB = mContext.getContentResolver().query(emailUri,
+ new String[] { "_id", "mailboxkey"}, emailOutboxCondition, null, "_id asc");
+
+ Uri emailObserverUri = Uri.parse("content://com.android.email.provider/message");
+ mContext.getContentResolver().registerContentObserver(emailObserverUri,
+ true, emailContentObserver);
+
+ Uri emailInboxObserverUri = Uri.parse("content://com.android.email.provider/message");
+ mContext.getContentResolver().registerContentObserver(
+ emailInboxObserverUri, true, emailInboxContentObserver);
+
+ Uri emailSentObserverUri = Uri.parse("content://com.android.email.provider/message");
+ mContext.getContentResolver().registerContentObserver(
+ emailSentObserverUri, true, emailSentContentObserver);
+
+ Uri emailDraftObserverUri = Uri.parse("content://com.android.email.provider/message");
+ mContext.getContentResolver().registerContentObserver(
+ emailDraftObserverUri, true, emailDraftContentObserver);
+
+ Uri emailOutboxObserverUri = Uri.parse("content://com.android.email.provider/message");
+ mContext.getContentResolver().registerContentObserver(
+ emailOutboxObserverUri, true, emailOutboxContentObserver);
+
+ //email end
+
+
+ Uri smsInboxObserverUri = Uri.parse("content://mms-sms/inbox");
+ mContext.getContentResolver().registerContentObserver(
+ smsInboxObserverUri, true, inboxContentObserver);
+
+ Uri smsSentObserverUri = Uri.parse("content://mms-sms/sent");
+ mContext.getContentResolver().registerContentObserver(
+ smsSentObserverUri, true, sentContentObserver);
+
+ Uri smsDraftObserverUri = Uri.parse("content://mms-sms/draft");
+ mContext.getContentResolver().registerContentObserver(
+ smsDraftObserverUri, true, draftContentObserver);
+
+ Uri smsOutboxObserverUri = Uri.parse("content://mms-sms/outbox");
+ mContext.getContentResolver().registerContentObserver(
+ smsOutboxObserverUri, true, outboxContentObserver);
+
+ Uri smsFailedObserverUri = Uri.parse("content://mms-sms/failed");
+ mContext.getContentResolver().registerContentObserver(
+ smsFailedObserverUri, true, failedContentObserver);
+
+ Uri smsQueuedObserverUri = Uri.parse("content://mms-sms/queued");
+ mContext.getContentResolver().registerContentObserver(
+ smsQueuedObserverUri, true, queuedContentObserver);
+
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
+ filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
+ mContext.registerReceiver(mStorageStatusReceiver, filter);
+
+ updatesRegistered = true;
+ Log.d(TAG, " ---------------- ");
+ Log.d(TAG, " REGISTERED MNS UPDATES ");
+ Log.d(TAG, " ---------------- ");
+ }
+
+ /**
+ * Stop listening to changes in cursor
+ */
+ private void deregisterUpdates() {
+
+ if ( updatesRegistered == true ){
+ updatesRegistered = false;
+ Log.d(TAG, "DEREGISTER MNS SMS UPDATES");
+ mContext.getContentResolver().unregisterContentObserver(
+ smsContentObserver);
+ mContext.getContentResolver().unregisterContentObserver(
+ inboxContentObserver);
+ mContext.getContentResolver().unregisterContentObserver(
+ sentContentObserver);
+ mContext.getContentResolver().unregisterContentObserver(
+ draftContentObserver);
+ mContext.getContentResolver().unregisterContentObserver(
+ outboxContentObserver);
+ mContext.getContentResolver().unregisterContentObserver(
+ failedContentObserver);
+ mContext.getContentResolver().unregisterContentObserver(
+ queuedContentObserver);
+
+ //email start
+ mContext.getContentResolver().unregisterContentObserver(
+ emailContentObserver);
+ //email end
+
+ mContext.unregisterReceiver(mStorageStatusReceiver);
+
+ crSmsA.close();
+ crSmsB.close();
+ currentCRSms = CR_SMS_A;
+ crSmsInboxA.close();
+ crSmsInboxB.close();
+ currentCRSmsInbox = CR_SMS_INBOX_A;
+ crSmsSentA.close();
+ crSmsSentB.close();
+ currentCRSmsSent = CR_SMS_SENT_A;
+ crSmsDraftA.close();
+ crSmsDraftB.close();
+ currentCRSmsDraft = CR_SMS_DRAFT_A;
+ crSmsOutboxA.close();
+ crSmsOutboxB.close();
+ currentCRSmsOutbox = CR_SMS_OUTBOX_A;
+ crSmsFailedA.close();
+ crSmsFailedB.close();
+ currentCRSmsFailed = CR_SMS_FAILED_A;
+ crSmsQueuedA.close();
+ crSmsQueuedB.close();
+ currentCRSmsQueued = CR_SMS_QUEUED_A;
+
+ crMmsA.close();
+ crMmsB.close();
+ currentCRMms = CR_MMS_A;
+ crMmsOutboxA.close();
+ crMmsOutboxB.close();
+ currentCRMmsOutbox = CR_MMS_OUTBOX_A;
+ crMmsDraftA.close();
+ crMmsDraftB.close();
+ currentCRMmsDraft = CR_MMS_DRAFT_A;
+ crMmsInboxA.close();
+ crMmsInboxB.close();
+ currentCRMmsInbox = CR_MMS_INBOX_A;
+ crMmsSentA.close();
+ crMmsSentB.close();
+ currentCRMmsSent = CR_MMS_SENT_A;
+
+ //email start
+ crEmailA.close();
+ crEmailB.close();
+ currentCREmail = CR_EMAIL_A;
+ crEmailOutboxA.close();
+ crEmailOutboxB.close();
+ currentCREmailOutbox = CR_EMAIL_OUTBOX_A;
+ crEmailDraftA.close();
+ crEmailDraftB.close();
+ currentCREmailDraft = CR_EMAIL_DRAFT_A;
+ crEmailInboxA.close();
+ crEmailInboxB.close();
+ currentCREmailInbox = CR_EMAIL_INBOX_A;
+ crEmailSentA.close();
+ crEmailSentB.close();
+ currentCREmailSent = CR_EMAIL_SENT_A;
+ //email end
+
+ }
+
+ }
+
+ private SmsContentObserverClass smsContentObserver = new SmsContentObserverClass();
+ private InboxContentObserverClass inboxContentObserver = new InboxContentObserverClass();
+ private SentContentObserverClass sentContentObserver = new SentContentObserverClass();
+ private DraftContentObserverClass draftContentObserver = new DraftContentObserverClass();
+ private OutboxContentObserverClass outboxContentObserver = new OutboxContentObserverClass();
+ private FailedContentObserverClass failedContentObserver = new FailedContentObserverClass();
+ private QueuedContentObserverClass queuedContentObserver = new QueuedContentObserverClass();
+
+ private EmailContentObserverClass emailContentObserver = new EmailContentObserverClass();
+ private EmailInboxContentObserverClass emailInboxContentObserver = new EmailInboxContentObserverClass();
+ private EmailSentContentObserverClass emailSentContentObserver = new EmailSentContentObserverClass();
+ private EmailDraftContentObserverClass emailDraftContentObserver = new EmailDraftContentObserverClass();
+ private EmailOutboxContentObserverClass emailOutboxContentObserver = new EmailOutboxContentObserverClass();
+
+ private Cursor crSmsA = null;
+ private Cursor crSmsB = null;
+ private Cursor crSmsInboxA = null;
+ private Cursor crSmsInboxB = null;
+ private Cursor crSmsSentA = null;
+ private Cursor crSmsSentB = null;
+ private Cursor crSmsDraftA = null;
+ private Cursor crSmsDraftB = null;
+ private Cursor crSmsOutboxA = null;
+ private Cursor crSmsOutboxB = null;
+ private Cursor crSmsFailedA = null;
+ private Cursor crSmsFailedB = null;
+ private Cursor crSmsQueuedA = null;
+ private Cursor crSmsQueuedB = null;
+
+ private Cursor crMmsA = null;
+ private Cursor crMmsB = null;
+ private Cursor crMmsOutboxA = null;
+ private Cursor crMmsOutboxB = null;
+ private Cursor crMmsDraftA = null;
+ private Cursor crMmsDraftB = null;
+ private Cursor crMmsInboxA = null;
+ private Cursor crMmsInboxB = null;
+ private Cursor crMmsSentA = null;
+ private Cursor crMmsSentB = null;
+
+
+ private Cursor crEmailA = null;
+ private Cursor crEmailB = null;
+ private Cursor crEmailOutboxA = null;
+ private Cursor crEmailOutboxB = null;
+ private Cursor crEmailDraftA = null;
+ private Cursor crEmailDraftB = null;
+ private Cursor crEmailInboxA = null;
+ private Cursor crEmailInboxB = null;
+ private Cursor crEmailSentA = null;
+ private Cursor crEmailSentB = null;
+
+ private final int CR_SMS_A = 1;
+ private final int CR_SMS_B = 2;
+ private int currentCRSms = CR_SMS_A;
+ private final int CR_SMS_INBOX_A = 1;
+ private final int CR_SMS_INBOX_B = 2;
+ private int currentCRSmsInbox = CR_SMS_INBOX_A;
+ private final int CR_SMS_SENT_A = 1;
+ private final int CR_SMS_SENT_B = 2;
+ private int currentCRSmsSent = CR_SMS_SENT_A;
+ private final int CR_SMS_DRAFT_A = 1;
+ private final int CR_SMS_DRAFT_B = 2;
+ private int currentCRSmsDraft = CR_SMS_DRAFT_A;
+ private final int CR_SMS_OUTBOX_A = 1;
+ private final int CR_SMS_OUTBOX_B = 2;
+ private int currentCRSmsOutbox = CR_SMS_OUTBOX_A;
+ private final int CR_SMS_FAILED_A = 1;
+ private final int CR_SMS_FAILED_B = 2;
+ private int currentCRSmsFailed = CR_SMS_FAILED_A;
+ private final int CR_SMS_QUEUED_A = 1;
+ private final int CR_SMS_QUEUED_B = 2;
+ private int currentCRSmsQueued = CR_SMS_QUEUED_A;
+
+ private final int CR_MMS_A = 1;
+ private final int CR_MMS_B = 2;
+ private int currentCRMms = CR_MMS_A;
+ private final int CR_MMS_OUTBOX_A = 1;
+ private final int CR_MMS_OUTBOX_B = 2;
+ private int currentCRMmsOutbox = CR_MMS_OUTBOX_A;
+ private final int CR_MMS_DRAFT_A = 1;
+ private final int CR_MMS_DRAFT_B = 2;
+ private int currentCRMmsDraft = CR_MMS_DRAFT_A;
+ private final int CR_MMS_INBOX_A = 1;
+ private final int CR_MMS_INBOX_B = 2;
+ private int currentCRMmsInbox = CR_MMS_INBOX_A;
+ private final int CR_MMS_SENT_A = 1;
+ private final int CR_MMS_SENT_B = 2;
+ private int currentCRMmsSent = CR_MMS_SENT_A;
+
+ private final int CR_EMAIL_A = 1;
+ private final int CR_EMAIL_B = 2;
+ private int currentCREmail = CR_EMAIL_A;
+ private final int CR_EMAIL_OUTBOX_A = 1;
+ private final int CR_EMAIL_OUTBOX_B = 2;
+ private int currentCREmailOutbox = CR_EMAIL_OUTBOX_A;
+ private final int CR_EMAIL_DRAFT_A = 1;
+ private final int CR_EMAIL_DRAFT_B = 2;
+ private int currentCREmailDraft = CR_EMAIL_DRAFT_A;
+ private final int CR_EMAIL_INBOX_A = 1;
+ private final int CR_EMAIL_INBOX_B = 2;
+ private int currentCREmailInbox = CR_EMAIL_INBOX_A;
+ private final int CR_EMAIL_SENT_A = 1;
+ private final int CR_EMAIL_SENT_B = 2;
+ private int currentCREmailSent = CR_EMAIL_SENT_A;
+
+
+ /**
+ * Get the folder name (MAP representation) based on the
+ * folder type value in SMS database
+ */
+ private String getMAPFolder(int type) {
+ String folder = null;
+ switch (type) {
+ case 1:
+ folder = "inbox";
+ break;
+ case 2:
+ folder = "sent";
+ break;
+ case 3:
+ folder = "draft";
+ break;
+ case 4:
+ case 5:
+ case 6:
+ folder = "outbox";
+ break;
+ default:
+ break;
+ }
+ return folder;
+ }
+
+ /**
+ * Get the folder name based on the type in SMS ContentProvider
+ */
+ private String getFolder(int type) {
+ String folder = null;
+ switch (type) {
+ case 1:
+ folder = "inbox";
+ break;
+ case 2:
+ folder = "sent";
+ break;
+ case 3:
+ folder = "draft";
+ break;
+ case 4:
+ folder = "outbox";
+ break;
+ case 5:
+ folder = "failed";
+ break;
+ case 6:
+ folder = "queued";
+ break;
+ default:
+ break;
+ }
+ return folder;
+ }
+
+ /**
+ * Gets the table type (as in Sms Content Provider) for the
+ * given id
+ */
+ private int getMessageType(String id) {
+ Cursor cr = mContext.getContentResolver().query(
+ Uri.parse("content://sms/" + id),
+ new String[] { "_id", "type" }, null, null, null);
+ if (cr.moveToFirst()) {
+ return cr.getInt(cr.getColumnIndex("type"));
+ }
+ return -1;
+ }
+ /**
+ * Gets the table type (as in Email Content Provider) for the
+ * given id
+ */
+ private int getDeletedFlagEmail(String id) {
+ int deletedFlag =0;
+ Cursor cr = mContext.getContentResolver().query(
+ Uri.parse("content://com.android.email.provider/message/" + id),
+ new String[] { "_id", "mailboxKey" }, null, null, null);
+ int folderId = -1;
+ if (cr.moveToFirst()) {
+ folderId = cr.getInt(cr.getColumnIndex("mailboxKey"));
+ }
+
+ Cursor cr1 = mContext.getContentResolver().query(
+ Uri.parse("content://com.android.email.provider/mailbox"),
+ new String[] { "_id", "displayName" }, "_id ="+ folderId, null, null);
+ String folderName = null;
+ if (cr1.moveToFirst()) {
+ folderName = cr1.getString(cr1.getColumnIndex("displayName"));
+ }
+ if(folderName !=null && (folderName.equalsIgnoreCase("Trash") ||
+ folderName.toUpperCase().contains("TRASH"))){
+ deletedFlag = 1;
+ }
+ return deletedFlag;
+ }
+
+ /**
+ * Get the folder name (table name of Sms Content Provider)
+ */
+ private String getContainingFolder(String oldFolder, String id,
+ String dateTime) {
+ String newFolder = null;
+ Cursor cr = mContext.getContentResolver().query(
+ Uri.parse("content://sms/"),
+ new String[] { "_id", "date", "type" }, " _id = " + id, null,
+ null);
+ if (cr.moveToFirst()) {
+ return getFolder(cr.getInt(cr.getColumnIndex("type")));
+ }
+ return newFolder;
+ }
+
+
+ private BroadcastReceiver mStorageStatusReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_LOW)) {
+ Log.d(TAG, " Memory Full ");
+ sendMnsEvent(MEMORY_FULL, null, null, null, null);
+ } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_OK)) {
+ Log.d(TAG, " Memory Available ");
+ sendMnsEvent(MEMORY_AVAILABLE, null, null, null, null);
+ }
+ }
+ };
+
+ /**
+ * This class listens for changes in Email Content Provider's inbox table
+ * It acts, only when a entry gets removed from the table
+ */
+ private class EmailInboxContentObserverClass extends ContentObserver {
+
+ public EmailInboxContentObserverClass() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ if (currentCREmailInbox == CR_EMAIL_INBOX_A) {
+ currentItemCount = crEmailInboxA.getCount();
+ crEmailInboxB.requery();
+ newItemCount = crEmailInboxB.getCount();
+ } else {
+ currentItemCount = crEmailInboxB.getCount();
+ crEmailInboxA.requery();
+ newItemCount = crEmailInboxA.getCount();
+ }
+
+ Log.d(TAG, "EMAIL INBOX current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (currentItemCount > newItemCount) {
+ crEmailInboxA.moveToFirst();
+ crEmailInboxB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crEmailInboxA,
+ new String[] { "_id" }, crEmailInboxB,
+ new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCREmailInbox == CR_EMAIL_INBOX_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " EMAIL DELETED FROM INBOX ");
+ String id = crEmailInboxA.getString(crEmailInboxA
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED EMAIL ID " + id);
+ int deletedFlag = getDeletedFlagEmail(id); //TODO
+ if(deletedFlag == 1){
+ id = Integer.toString(Integer.valueOf(id)
+ + EMAIL_HDLR_CONSTANT);
+
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/INBOX", null, "EMAIL");
+ }
+ else {
+ Log.d(TAG, "Shouldn't reach here as you cannot "
+ + "move msg from Inbox to any other folder");
+ }
+
+ } else {
+ // TODO - The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCREmailInbox == CR_EMAIL_INBOX_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " EMAIL DELETED FROM INBOX ");
+ String id = crEmailInboxB.getString(crEmailInboxB
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED EMAIL ID " + id);
+ int deletedFlag = getDeletedFlagEmail(id); //TODO
+ if(deletedFlag == 1){
+ id = Integer.toString(Integer.valueOf(id)
+ + EMAIL_HDLR_CONSTANT);
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/INBOX", null, "EMAIL");
+ }
+ else {
+ Log.d(TAG, "Shouldn't reach here as you cannot "
+ + "move msg from Inbox to any other folder");
+ }
+ }
+ else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+
+ if (currentCREmailInbox == CR_EMAIL_INBOX_A) {
+ currentCREmailInbox = CR_EMAIL_INBOX_B;
+ } else {
+ currentCREmailInbox = CR_EMAIL_INBOX_A;
+ }
+ }
+ }
+ /**
+ * This class listens for changes in Email Content Provider's Sent table
+ * It acts, only when a entry gets removed from the table
+ */
+ private class EmailSentContentObserverClass extends ContentObserver {
+
+ public EmailSentContentObserverClass() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ if (currentCREmailSent == CR_EMAIL_SENT_A) {
+ currentItemCount = crEmailSentA.getCount();
+ crEmailSentB.requery();
+ newItemCount = crEmailSentB.getCount();
+ } else {
+ currentItemCount = crEmailSentB.getCount();
+ crEmailSentA.requery();
+ newItemCount = crEmailSentA.getCount();
+ }
+
+ Log.d(TAG, "EMAIL SENT current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (currentItemCount > newItemCount) {
+ crEmailSentA.moveToFirst();
+ crEmailSentB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crEmailSentA,
+ new String[] { "_id" }, crEmailSentB,
+ new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCREmailSent == CR_EMAIL_SENT_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " EMAIL DELETED FROM SENT ");
+ String id = crEmailSentA.getString(crEmailSentA
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED EMAIL ID " + id);
+
+ int deletedFlag = getDeletedFlagEmail(id);
+ if(deletedFlag == 1){
+ id = Integer.toString(Integer.valueOf(id)
+ + EMAIL_HDLR_CONSTANT);
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/SENT", null, "EMAIL");
+ } else {
+ Log.d(TAG,"Shouldn't reach here as you cannot " +
+ "move msg from Sent to any other folder");
+ }
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCREmailSent == CR_EMAIL_SENT_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " EMAIL DELETED FROM SENT ");
+ String id = crEmailSentB.getString(crEmailSentB
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED EMAIL ID " + id);
+ int deletedFlag = getDeletedFlagEmail(id);
+ if(deletedFlag == 1){
+ id = Integer.toString(Integer.valueOf(id)
+ + EMAIL_HDLR_CONSTANT);
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/SENT", null, "EMAIL");
+ } else {
+ Log.d(TAG, "Shouldn't reach here as " +
+ "you cannot move msg from Sent to " +
+ "any other folder");
+ }
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCREmailSent == CR_EMAIL_SENT_A) {
+ currentCREmailSent = CR_EMAIL_SENT_B;
+ } else {
+ currentCREmailSent = CR_EMAIL_SENT_A;
+ }
+ }
+ }
+
+ /**
+ * This class listens for changes in Email Content Provider's Draft table
+ * It acts, only when a entry gets removed from the table
+ */
+ private class EmailDraftContentObserverClass extends ContentObserver {
+
+ public EmailDraftContentObserverClass() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ if (currentCREmailDraft == CR_EMAIL_DRAFT_A) {
+ currentItemCount = crEmailDraftA.getCount();
+ crEmailDraftB.requery();
+ newItemCount = crEmailDraftB.getCount();
+ } else {
+ currentItemCount = crEmailDraftB.getCount();
+ crEmailDraftA.requery();
+ newItemCount = crEmailDraftA.getCount();
+ }
+
+ Log.d(TAG, "EMAIL DRAFT current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (currentItemCount > newItemCount) {
+ crEmailDraftA.moveToFirst();
+ crEmailDraftB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crEmailDraftA,
+ new String[] { "_id" }, crEmailDraftB,
+ new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCREmailDraft == CR_EMAIL_DRAFT_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " EMAIL DELETED FROM DRAFT ");
+ String id = crEmailDraftA.getString(crEmailDraftA
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED EMAIL ID " + id);
+
+ int deletedFlag = getDeletedFlagEmail(id);
+ if(deletedFlag == 1){
+ id = Integer.toString(Integer.valueOf(id)
+ + EMAIL_HDLR_CONSTANT);
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/DRAFT", null, "EMAIL");
+ } else {
+ Cursor cr1 = null;
+ int folderId;
+ String containingFolder = null;
+ EmailUtils eu = new EmailUtils();
+ Uri uri1 = Uri.parse("content://com.android.email.provider/message");
+ String whereClause = " _id = " + id;
+ cr1 = mContext.getContentResolver().query(uri1, null, whereClause, null,
+ null);
+
+ if (cr1.getCount() > 0) {
+ cr1.moveToFirst();
+ folderId = cr1.getInt(cr1.getColumnIndex("mailboxKey"));
+ containingFolder = eu.getContainingFolderEmail(folderId, mContext);
+ }
+
+ String newFolder = containingFolder;
+ id = Integer.toString(Integer.valueOf(id)
+ + EMAIL_HDLR_CONSTANT);
+ sendMnsEvent(MESSAGE_SHIFT, id, "TELECOM/MSG/"
+ + newFolder, "TELECOM/MSG/DRAFT",
+ "EMAIL");
+ if ( newFolder.equalsIgnoreCase("sent")) {
+ sendMnsEvent(SENDING_SUCCESS, id,
+ "TELECOM/MSG/" + newFolder,
+ null, "EMAIL");
+ }
+ }
+
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCREmailDraft == CR_EMAIL_DRAFT_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " EMAIL DELETED FROM DRAFT ");
+ String id = crEmailDraftB.getString(crEmailDraftB
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED EMAIL ID " + id);
+ int deletedFlag = getDeletedFlagEmail(id);
+ if(deletedFlag == 1){
+ id = Integer.toString(Integer.valueOf(id)
+ + EMAIL_HDLR_CONSTANT);
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/DRAFT", null, "EMAIL");
+ } else {
+ Cursor cr1 = null;
+ int folderId;
+ String containingFolder = null;
+ EmailUtils eu = new EmailUtils();
+ Uri uri1 = Uri.parse("content://com.android.email.provider/message");
+ String whereClause = " _id = " + id;
+ cr1 = mContext.getContentResolver().query(uri1, null, whereClause, null,
+ null);
+
+ if (cr1.getCount() > 0) {
+ cr1.moveToFirst();
+ folderId = cr1.getInt(cr1.getColumnIndex("mailboxKey"));
+ containingFolder = eu.getContainingFolderEmail(folderId, mContext);
+ }
+
+ String newFolder = containingFolder;
+ id = Integer.toString(Integer.valueOf(id)
+ + EMAIL_HDLR_CONSTANT);
+ sendMnsEvent(MESSAGE_SHIFT, id, "TELECOM/MSG/"
+ + newFolder, "TELECOM/MSG/DRAFT",
+ "EMAIL");
+ if ( newFolder.equalsIgnoreCase("sent")) {
+ sendMnsEvent(SENDING_SUCCESS, id,
+ "TELECOM/MSG/" + newFolder,
+ null, "EMAIL");
+ }
+ }
+
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCREmailDraft == CR_EMAIL_DRAFT_A) {
+ currentCREmailDraft = CR_EMAIL_DRAFT_B;
+ } else {
+ currentCREmailDraft = CR_EMAIL_DRAFT_A;
+ }
+ }
+ }
+
+ /**
+ * This class listens for changes in Sms Content Provider's Outbox table
+ * It acts only when a entry gets removed from the table
+ */
+ private class EmailOutboxContentObserverClass extends ContentObserver {
+
+ public EmailOutboxContentObserverClass() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ if (currentCREmailOutbox == CR_EMAIL_OUTBOX_A) {
+ currentItemCount = crEmailOutboxA.getCount();
+ crEmailOutboxB.requery();
+ newItemCount = crEmailOutboxB.getCount();
+ } else {
+ currentItemCount = crEmailOutboxB.getCount();
+ crEmailOutboxA.requery();
+ newItemCount = crEmailOutboxA.getCount();
+ }
+
+ Log.d(TAG, "EMAIL OUTBOX current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (currentItemCount > newItemCount) {
+ crEmailOutboxA.moveToFirst();
+ crEmailOutboxB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crEmailOutboxA,
+ new String[] { "_id" }, crEmailOutboxB,
+ new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCREmailOutbox == CR_EMAIL_OUTBOX_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " EMAIL DELETED FROM OUTBOX ");
+ String id = crEmailOutboxA.getString(crEmailOutboxA
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED EMAIL ID " + id);
+ int deletedFlag = getDeletedFlagEmail(id);
+ if(deletedFlag == 1){
+ id = Integer.toString(Integer.valueOf(id)
+ + EMAIL_HDLR_CONSTANT);
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/OUTBOX", null, "EMAIL");
+ } else {
+ Cursor cr1 = null;
+ int folderId;
+ String containingFolder = null;
+ EmailUtils eu = new EmailUtils();
+ Uri uri1 = Uri.parse("content://com.android.email.provider/message");
+ String whereClause = " _id = " + id;
+ cr1 = mContext.getContentResolver().query(uri1, null, whereClause, null,
+ null);
+
+ if (cr1.getCount() > 0) {
+ cr1.moveToFirst();
+ folderId = cr1.getInt(cr1.getColumnIndex("mailboxKey"));
+ containingFolder = eu.getContainingFolderEmail(folderId, mContext);
+ }
+
+ String newFolder = containingFolder;
+ id = Integer.toString(Integer.valueOf(id)
+ + EMAIL_HDLR_CONSTANT);
+
+ if ((newFolder != null)
+ && (!newFolder
+ .equalsIgnoreCase("outbox"))) {
+ // The message has moved on MAP virtual
+ // folder representation.
+ sendMnsEvent(MESSAGE_SHIFT, id,
+ "TELECOM/MSG/" + newFolder,
+ "TELECOM/MSG/OUTBOX", "EMAIL");
+ if ( newFolder.equalsIgnoreCase("sent")) {
+ sendMnsEvent(SENDING_SUCCESS, id,
+ "TELECOM/MSG/" + newFolder,
+ null, "EMAIL");
+ }
+ }
+ }
+
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCREmailOutbox == CR_EMAIL_OUTBOX_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " EMAIL DELETED FROM OUTBOX ");
+ String id = crEmailOutboxB.getString(crEmailOutboxB
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED EMAIL ID " + id);
+ int deletedFlag = getDeletedFlagEmail(id);
+ if(deletedFlag == 1){
+ id = Integer.toString(Integer.valueOf(id)
+ + EMAIL_HDLR_CONSTANT);
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/OUTBOX", null, "EMAIL");
+ } else {
+ Cursor cr1 = null;
+ int folderId;
+ String containingFolder = null;
+ EmailUtils eu = new EmailUtils();
+ Uri uri1 = Uri.parse("content://com.android.email.provider/message");
+ String whereClause = " _id = " + id;
+ cr1 = mContext.getContentResolver().query(uri1, null, whereClause, null,
+ null);
+
+ if (cr1.getCount() > 0) {
+ cr1.moveToFirst();
+ folderId = cr1.getInt(cr1.getColumnIndex("mailboxKey"));
+ containingFolder = eu.getContainingFolderEmail(folderId, mContext);
+ }
+
+ String newFolder = containingFolder;
+ id = Integer.toString(Integer.valueOf(id)
+ + EMAIL_HDLR_CONSTANT);
+
+ if ((newFolder != null)
+ && (!newFolder
+ .equalsIgnoreCase("outbox"))) {
+ // The message has moved on MAP virtual
+ // folder representation.
+ sendMnsEvent(MESSAGE_SHIFT, id,
+ "TELECOM/MSG/" + newFolder,
+ "TELECOM/MSG/OUTBOX", "EMAIL");
+ }
+ }
+
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCREmailOutbox == CR_EMAIL_OUTBOX_A) {
+ currentCREmailOutbox = CR_EMAIL_OUTBOX_B;
+ } else {
+ currentCREmailOutbox = CR_EMAIL_OUTBOX_A;
+ }
+ }
+ }
+
+
+
+ /**
+ * This class listens for changes in Sms Content Provider
+ * It acts, only when a new entry gets added to database
+ */
+ private class SmsContentObserverClass extends ContentObserver {
+
+ public SmsContentObserverClass() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ checkMmsAdded();
+
+ // Synchronize this?
+ if (currentCRSms == CR_SMS_A) {
+ currentItemCount = crSmsA.getCount();
+ crSmsB.requery();
+ newItemCount = crSmsB.getCount();
+ } else {
+ currentItemCount = crSmsB.getCount();
+ crSmsA.requery();
+ newItemCount = crSmsA.getCount();
+ }
+
+ Log.d(TAG, "SMS current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (newItemCount > currentItemCount) {
+ crSmsA.moveToFirst();
+ crSmsB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crSmsA,
+ new String[] { "_id" }, crSmsB, new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCRSms == CR_SMS_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ Log.d(TAG, " SMS ADDED TO INBOX ");
+ String body1 = crSmsA.getString(crSmsA
+ .getColumnIndex("body"));
+ String id1 = crSmsA.getString(crSmsA
+ .getColumnIndex("_id"));
+ Log.d(TAG, " ADDED SMS ID " + id1 + " BODY "
+ + body1);
+ String folder = getMAPFolder(crSmsA.getInt(crSmsA
+ .getColumnIndex("type")));
+ if ( folder != null ) {
+ sendMnsEvent(NEW_MESSAGE, id1, "TELECOM/MSG/"
+ + folder, null, "SMS_GSM");
+ } else {
+ Log.d(TAG, " ADDED TO UNKNOWN FOLDER");
+ }
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCRSms == CR_SMS_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ Log.d(TAG, " SMS ADDED ");
+ String body1 = crSmsB.getString(crSmsB
+ .getColumnIndex("body"));
+ String id1 = crSmsB.getString(crSmsB
+ .getColumnIndex("_id"));
+ Log.d(TAG, " ADDED SMS ID " + id1 + " BODY "
+ + body1);
+ String folder = getMAPFolder(crSmsB.getInt(crSmsB
+ .getColumnIndex("type")));
+ if ( folder != null ) {
+ sendMnsEvent(NEW_MESSAGE, id1, "TELECOM/MSG/"
+ + folder, null, "SMS_GSM");
+ } else {
+ Log.d(TAG, " ADDED TO UNKNOWN FOLDER");
+ }
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCRSms == CR_SMS_A) {
+ currentCRSms = CR_SMS_B;
+ } else {
+ currentCRSms = CR_SMS_A;
+ }
+ }
+ }
+
+ /**
+ * This class listens for changes in Email Content Provider
+ * It acts, only when a new entry gets added to database
+ */
+ private class EmailContentObserverClass extends ContentObserver {
+
+ public EmailContentObserverClass() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+ EmailUtils eu = new EmailUtils();
+ String containingFolder = null;
+
+ // Synchronize this?
+ if (currentCREmail == CR_EMAIL_A) {
+ currentItemCount = crEmailA.getCount();
+ crEmailB.requery();
+ newItemCount = crEmailB.getCount();
+ } else {
+ currentItemCount = crEmailB.getCount();
+ crEmailA.requery();
+ newItemCount = crEmailA.getCount();
+ }
+
+ Log.d(TAG, "Email current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (newItemCount > currentItemCount) {
+ crEmailA.moveToFirst();
+ crEmailB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crEmailA,
+ new String[] { "_id" }, crEmailB, new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCREmail == CR_EMAIL_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ Log.d(TAG, " EMAIL ADDED TO INBOX ");
+ String id1 = crEmailA.getString(crEmailA
+ .getColumnIndex("_id"));
+ Log.d(TAG, " ADDED EMAIL ID " + id1);
+ Cursor cr1 = null;
+ int folderId;
+ Uri uri1 = Uri.parse("content://com.android.email.provider/message");
+ String whereClause = " _id = " + id1;
+ cr1 = mContext.getContentResolver().query(uri1, null, whereClause, null,
+ null);
+ if ( cr1.moveToFirst()) {
+ do {
+ for(int i=0;i<cr1.getColumnCount();i++){
+ Log.d(TAG, " Column Name: "+ cr1.getColumnName(i) + " Value: " + cr1.getString(i));
+ }
+ } while ( cr1.moveToNext());
+ }
+
+ if (cr1.getCount() > 0) {
+ cr1.moveToFirst();
+ folderId = cr1.getInt(cr1.getColumnIndex("mailboxKey"));
+ containingFolder = eu.getContainingFolderEmail(folderId, mContext);
+ }
+
+ if ( containingFolder != null ) {
+ Log.d(TAG, " containingFolder:: "+containingFolder);
+ id1 = Integer.toString(Integer.valueOf(id1)
+ + EMAIL_HDLR_CONSTANT);
+ sendMnsEvent(NEW_MESSAGE, id1, "TELECOM/MSG/"
+ + containingFolder, null, "EMAIL");
+ } else {
+ Log.d(TAG, " ADDED TO UNKNOWN FOLDER");
+ }
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCREmail == CR_EMAIL_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ Log.d(TAG, " EMAIL ADDED ");
+ String id1 = crEmailB.getString(crEmailB
+ .getColumnIndex("_id"));
+ Log.d(TAG, " ADDED EMAIL ID " + id1);
+ Cursor cr1 = null;
+ int folderId;
+ Uri uri1 = Uri.parse("content://com.android.email.provider/message");
+ String whereClause = " _id = " + id1;
+ cr1 = mContext.getContentResolver().query(uri1, null, whereClause, null,
+ null);
+
+ if ( cr1.moveToFirst()) {
+ do {
+ for(int i=0;i<cr1.getColumnCount();i++){
+ Log.d(TAG, " Column Name: "+ cr1.getColumnName(i) +
+ " Value: " + cr1.getString(i));
+ }
+ } while ( cr1.moveToNext());
+ }
+
+ if (cr1.getCount() > 0) {
+ cr1.moveToFirst();
+ folderId = cr1.getInt(cr1.getColumnIndex("mailboxKey"));
+ containingFolder = eu.getContainingFolderEmail(folderId, mContext);
+ }
+ if ( containingFolder != null ) {
+ Log.d(TAG, " containingFolder:: "+containingFolder);
+ id1 = Integer.toString(Integer.valueOf(id1)
+ + EMAIL_HDLR_CONSTANT);
+ sendMnsEvent(NEW_MESSAGE, id1, "TELECOM/MSG/"
+ + containingFolder, null, "EMAIL");
+ } else {
+ Log.d(TAG, " ADDED TO UNKNOWN FOLDER");
+ }
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCREmail == CR_EMAIL_A) {
+ currentCREmail = CR_EMAIL_B;
+ } else {
+ currentCREmail = CR_EMAIL_A;
+ }
+ }
+ }
+
+
+ /**
+ * This class listens for changes in Sms Content Provider's inbox table
+ * It acts, only when a entry gets removed from the table
+ */
+ private class InboxContentObserverClass extends ContentObserver {
+
+ public InboxContentObserverClass() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ checkMmsInbox();
+
+ if (currentCRSmsInbox == CR_SMS_INBOX_A) {
+ currentItemCount = crSmsInboxA.getCount();
+ crSmsInboxB.requery();
+ newItemCount = crSmsInboxB.getCount();
+ } else {
+ currentItemCount = crSmsInboxB.getCount();
+ crSmsInboxA.requery();
+ newItemCount = crSmsInboxA.getCount();
+ }
+
+ Log.d(TAG, "SMS INBOX current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (currentItemCount > newItemCount) {
+ crSmsInboxA.moveToFirst();
+ crSmsInboxB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crSmsInboxA,
+ new String[] { "_id" }, crSmsInboxB,
+ new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCRSmsInbox == CR_SMS_INBOX_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " SMS DELETED FROM INBOX ");
+ String body = crSmsInboxA.getString(crSmsInboxA
+ .getColumnIndex("body"));
+ String id = crSmsInboxA.getString(crSmsInboxA
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED SMS ID " + id + " BODY "
+ + body);
+ int msgType = getMessageType(id);
+ if (msgType == -1) {
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/INBOX", null, "SMS_GSM");
+ } else {
+ Log.d(TAG, "Shouldn't reach here as you cannot " +
+ "move msg from Inbox to any other folder");
+ }
+ } else {
+ // TODO - The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCRSmsInbox == CR_SMS_INBOX_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " SMS DELETED FROM INBOX ");
+ String body = crSmsInboxB.getString(crSmsInboxB
+ .getColumnIndex("body"));
+ String id = crSmsInboxB.getString(crSmsInboxB
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED SMS ID " + id + " BODY "
+ + body);
+ int msgType = getMessageType(id);
+ if (msgType == -1) {
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/INBOX", null, "SMS_GSM");
+ } else {
+ Log.d(TAG,"Shouldn't reach here as you cannot " +
+ "move msg from Inbox to any other folder");
+ }
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCRSmsInbox == CR_SMS_INBOX_A) {
+ currentCRSmsInbox = CR_SMS_INBOX_B;
+ } else {
+ currentCRSmsInbox = CR_SMS_INBOX_A;
+ }
+ }
+ }
+
+
+
+ /**
+ * This class listens for changes in Sms Content Provider's Sent table
+ * It acts, only when a entry gets removed from the table
+ */
+ private class SentContentObserverClass extends ContentObserver {
+
+ public SentContentObserverClass() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ checkMmsSent();
+
+ if (currentCRSmsSent == CR_SMS_SENT_A) {
+ currentItemCount = crSmsSentA.getCount();
+ crSmsSentB.requery();
+ newItemCount = crSmsSentB.getCount();
+ } else {
+ currentItemCount = crSmsSentB.getCount();
+ crSmsSentA.requery();
+ newItemCount = crSmsSentA.getCount();
+ }
+
+ Log.d(TAG, "SMS SENT current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (currentItemCount > newItemCount) {
+ crSmsSentA.moveToFirst();
+ crSmsSentB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crSmsSentA,
+ new String[] { "_id" }, crSmsSentB,
+ new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ // while((CursorJointer.Result joinerResult = joiner.next()) !=
+ // null)
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCRSmsSent == CR_SMS_SENT_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " SMS DELETED FROM SENT ");
+ String body = crSmsSentA.getString(crSmsSentA
+ .getColumnIndex("body"));
+ String id = crSmsSentA.getString(crSmsSentA
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED SMS ID " + id + " BODY "
+ + body);
+
+ int msgType = getMessageType(id);
+ if (msgType == -1) {
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/SENT", null, "SMS_GSM");
+ } else {
+ Log.d(TAG,"Shouldn't reach here as you cannot " +
+ "move msg from Sent to any other folder");
+ }
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCRSmsSent == CR_SMS_SENT_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " SMS DELETED FROM SENT ");
+ String body = crSmsSentB.getString(crSmsSentB
+ .getColumnIndex("body"));
+ String id = crSmsSentB.getString(crSmsSentB
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED SMS ID " + id + " BODY "
+ + body);
+ int msgType = getMessageType(id);
+ if (msgType == -1) {
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/SENT", null, "SMS_GSM");
+ } else {
+ Log.d(TAG, "Shouldn't reach here as " +
+ "you cannot move msg from Sent to " +
+ "any other folder");
+ }
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCRSmsSent == CR_SMS_SENT_A) {
+ currentCRSmsSent = CR_SMS_SENT_B;
+ } else {
+ currentCRSmsSent = CR_SMS_SENT_A;
+ }
+ }
+ }
+
+ /**
+ * This class listens for changes in Sms Content Provider's Draft table
+ * It acts, only when a entry gets removed from the table
+ */
+ private class DraftContentObserverClass extends ContentObserver {
+
+ public DraftContentObserverClass() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ checkMmsDrafts();
+
+ if (currentCRSmsDraft == CR_SMS_DRAFT_A) {
+ currentItemCount = crSmsDraftA.getCount();
+ crSmsDraftB.requery();
+ newItemCount = crSmsDraftB.getCount();
+ } else {
+ currentItemCount = crSmsDraftB.getCount();
+ crSmsDraftA.requery();
+ newItemCount = crSmsDraftA.getCount();
+ }
+
+ Log.d(TAG, "SMS DRAFT current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (currentItemCount > newItemCount) {
+ crSmsDraftA.moveToFirst();
+ crSmsDraftB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crSmsDraftA,
+ new String[] { "_id" }, crSmsDraftB,
+ new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCRSmsDraft == CR_SMS_DRAFT_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " SMS DELETED FROM DRAFT ");
+ String body = crSmsDraftA.getString(crSmsDraftA
+ .getColumnIndex("body"));
+ String id = crSmsDraftA.getString(crSmsDraftA
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED SMS ID " + id + " BODY "
+ + body);
+
+ int msgType = getMessageType(id);
+ if (msgType == -1) {
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/DRAFT", null, "SMS_GSM");
+ } else {
+ String newFolder = getMAPFolder(msgType);
+ sendMnsEvent(MESSAGE_SHIFT, id, "TELECOM/MSG/"
+ + newFolder, "TELECOM/MSG/DRAFT",
+ "SMS_GSM");
+ if ( newFolder.equalsIgnoreCase("sent")) {
+ sendMnsEvent(SENDING_SUCCESS, id,
+ "TELECOM/MSG/" + newFolder,
+ null, "SMS_GSM");
+ }
+ }
+
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCRSmsDraft == CR_SMS_DRAFT_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " SMS DELETED FROM DRAFT ");
+ String body = crSmsDraftB.getString(crSmsDraftB
+ .getColumnIndex("body"));
+ String id = crSmsDraftB.getString(crSmsDraftB
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED SMS ID " + id + " BODY "
+ + body);
+ int msgType = getMessageType(id);
+ if (msgType == -1) {
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/DRAFT", null, "SMS_GSM");
+ } else {
+ String newFolder = getMAPFolder(msgType);
+ sendMnsEvent(MESSAGE_SHIFT, id, "TELECOM/MSG/"
+ + newFolder, "TELECOM/MSG/DRAFT",
+ "SMS_GSM");
+ if ( newFolder.equalsIgnoreCase("sent")) {
+ sendMnsEvent(SENDING_SUCCESS, id,
+ "TELECOM/MSG/" + newFolder,
+ null, "SMS_GSM");
+ }
+ }
+
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCRSmsDraft == CR_SMS_DRAFT_A) {
+ currentCRSmsDraft = CR_SMS_DRAFT_B;
+ } else {
+ currentCRSmsDraft = CR_SMS_DRAFT_A;
+ }
+ }
+ }
+
+ /**
+ * This class listens for changes in Sms Content Provider's Outbox table
+ * It acts only when a entry gets removed from the table
+ */
+ private class OutboxContentObserverClass extends ContentObserver {
+
+ public OutboxContentObserverClass() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ // Check MMS Outbox for changes
+
+ checkMmsOutbox();
+
+ // Check SMS Outbox for changes
+
+ if (currentCRSmsOutbox == CR_SMS_OUTBOX_A) {
+ currentItemCount = crSmsOutboxA.getCount();
+ crSmsOutboxB.requery();
+ newItemCount = crSmsOutboxB.getCount();
+ } else {
+ currentItemCount = crSmsOutboxB.getCount();
+ crSmsOutboxA.requery();
+ newItemCount = crSmsOutboxA.getCount();
+ }
+
+ Log.d(TAG, "SMS OUTBOX current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (currentItemCount > newItemCount) {
+ crSmsOutboxA.moveToFirst();
+ crSmsOutboxB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crSmsOutboxA,
+ new String[] { "_id" }, crSmsOutboxB,
+ new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCRSmsOutbox == CR_SMS_OUTBOX_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " SMS DELETED FROM OUTBOX ");
+ String body = crSmsOutboxA.getString(crSmsOutboxA
+ .getColumnIndex("body"));
+ String id = crSmsOutboxA.getString(crSmsOutboxA
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED SMS ID " + id + " BODY "
+ + body);
+ int msgType = getMessageType(id);
+ if (msgType == -1) {
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/OUTBOX", null, "SMS_GSM");
+ } else {
+ String newFolder = getMAPFolder(msgType);
+ if ((newFolder != null)
+ && (!newFolder
+ .equalsIgnoreCase("outbox"))) {
+ // The message has moved on MAP virtual
+ // folder representation.
+ sendMnsEvent(MESSAGE_SHIFT, id,
+ "TELECOM/MSG/" + newFolder,
+ "TELECOM/MSG/OUTBOX", "SMS_GSM");
+ if ( newFolder.equalsIgnoreCase("sent")) {
+ sendMnsEvent(SENDING_SUCCESS, id,
+ "TELECOM/MSG/" + newFolder,
+ null, "SMS_GSM");
+ }
+ }
+ if ( (msgType == MSG_CP_QUEUED_TYPE) ||
+ (msgType == MSG_CP_FAILED_TYPE)) {
+ // Message moved from outbox to queue or
+ // failed folder
+ sendMnsEvent(SENDING_FAILURE, id,
+ "TELECOM/MSG/OUTBOX", null, "SMS_GSM");
+ }
+ }
+
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCRSmsOutbox == CR_SMS_OUTBOX_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " SMS DELETED FROM OUTBOX ");
+ String body = crSmsOutboxB.getString(crSmsOutboxB
+ .getColumnIndex("body"));
+ String id = crSmsOutboxB.getString(crSmsOutboxB
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED SMS ID " + id + " BODY "
+ + body);
+ int msgType = getMessageType(id);
+ if (msgType == -1) {
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/OUTBOX", null, "SMS_GSM");
+ } else {
+ String newFolder = getMAPFolder(msgType);
+ if ((newFolder != null)
+ && (!newFolder
+ .equalsIgnoreCase("outbox"))) {
+ // The message has moved on MAP virtual
+ // folder representation.
+ sendMnsEvent(MESSAGE_SHIFT, id,
+ "TELECOM/MSG/" + newFolder,
+ "TELECOM/MSG/OUTBOX", "SMS_GSM");
+ }
+ }
+
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCRSmsOutbox == CR_SMS_OUTBOX_A) {
+ currentCRSmsOutbox = CR_SMS_OUTBOX_B;
+ } else {
+ currentCRSmsOutbox = CR_SMS_OUTBOX_A;
+ }
+ }
+ }
+
+ /**
+ * This class listens for changes in Sms Content Provider's Failed table
+ * It acts only when a entry gets removed from the table
+ */
+ private class FailedContentObserverClass extends ContentObserver {
+
+ public FailedContentObserverClass() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ // Mms doesn't have Failed type
+
+ if (currentCRSmsFailed == CR_SMS_FAILED_A) {
+ currentItemCount = crSmsFailedA.getCount();
+ crSmsFailedB.requery();
+ newItemCount = crSmsFailedB.getCount();
+ } else {
+ currentItemCount = crSmsFailedB.getCount();
+ crSmsFailedA.requery();
+ newItemCount = crSmsFailedA.getCount();
+ }
+
+ Log.d(TAG, "SMS FAILED current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (currentItemCount > newItemCount) {
+ crSmsFailedA.moveToFirst();
+ crSmsFailedB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crSmsFailedA,
+ new String[] { "_id" }, crSmsFailedB,
+ new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCRSmsFailed == CR_SMS_FAILED_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " SMS DELETED FROM FAILED ");
+ String body = crSmsFailedA.getString(crSmsFailedA
+ .getColumnIndex("body"));
+ String id = crSmsFailedA.getString(crSmsFailedA
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED SMS ID " + id + " BODY "
+ + body);
+ int msgType = getMessageType(id);
+ if (msgType == -1) {
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/OUTBOX", null, "SMS_GSM");
+ } else {
+ String newFolder = getMAPFolder(msgType);
+ if ((newFolder != null)
+ && (!newFolder
+ .equalsIgnoreCase("outbox"))) {
+ // The message has moved on MAP virtual
+ // folder representation.
+ sendMnsEvent(MESSAGE_SHIFT, id,
+ "TELECOM/MSG/" + newFolder,
+ "TELECOM/MSG/OUTBOX", "SMS_GSM");
+ if ( newFolder.equalsIgnoreCase("sent")) {
+ sendMnsEvent(SENDING_SUCCESS, id,
+ "TELECOM/MSG/" + newFolder,
+ null, "SMS_GSM");
+ }
+ }
+ }
+
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCRSmsFailed == CR_SMS_FAILED_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " SMS DELETED FROM FAILED ");
+ String body = crSmsFailedB.getString(crSmsFailedB
+ .getColumnIndex("body"));
+ String id = crSmsFailedB.getString(crSmsFailedB
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED SMS ID " + id + " BODY "
+ + body);
+ int msgType = getMessageType(id);
+ if (msgType == -1) {
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/OUTBOX", null, "SMS_GSM");
+ } else {
+ String newFolder = getMAPFolder(msgType);
+ if ((newFolder != null)
+ && (!newFolder
+ .equalsIgnoreCase("outbox"))) {
+ // The message has moved on MAP virtual
+ // folder representation.
+ sendMnsEvent(MESSAGE_SHIFT, id,
+ "TELECOM/MSG/" + newFolder,
+ "TELECOM/MSG/OUTBOX", "SMS_GSM");
+ if ( newFolder.equalsIgnoreCase("sent")) {
+ sendMnsEvent(SENDING_SUCCESS, id,
+ "TELECOM/MSG/" + newFolder,
+ null, "SMS_GSM");
+ }
+ }
+ }
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCRSmsFailed == CR_SMS_FAILED_A) {
+ currentCRSmsFailed = CR_SMS_FAILED_B;
+ } else {
+ currentCRSmsFailed = CR_SMS_FAILED_A;
+ }
+ }
+ }
+
+ /**
+ * This class listens for changes in Sms Content Provider's Queued table
+ * It acts only when a entry gets removed from the table
+ */
+ private class QueuedContentObserverClass extends ContentObserver {
+
+ public QueuedContentObserverClass() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ // Mms doesn't have Queued type
+
+ if (currentCRSmsQueued == CR_SMS_QUEUED_A) {
+ currentItemCount = crSmsQueuedA.getCount();
+ crSmsQueuedB.requery();
+ newItemCount = crSmsQueuedB.getCount();
+ } else {
+ currentItemCount = crSmsQueuedB.getCount();
+ crSmsQueuedA.requery();
+ newItemCount = crSmsQueuedA.getCount();
+ }
+
+ Log.d(TAG, "SMS QUEUED current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (currentItemCount > newItemCount) {
+ crSmsQueuedA.moveToFirst();
+ crSmsQueuedB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crSmsQueuedA,
+ new String[] { "_id" }, crSmsQueuedB,
+ new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCRSmsQueued == CR_SMS_QUEUED_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " SMS DELETED FROM QUEUED ");
+ String body = crSmsQueuedA.getString(crSmsQueuedA
+ .getColumnIndex("body"));
+ String id = crSmsQueuedA.getString(crSmsQueuedA
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED SMS ID " + id + " BODY "
+ + body);
+ int msgType = getMessageType(id);
+ if (msgType == -1) {
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/OUTBOX", null, "SMS_GSM");
+ } else {
+ String newFolder = getMAPFolder(msgType);
+ if ((newFolder != null)
+ && (!newFolder
+ .equalsIgnoreCase("outbox"))) {
+ // The message has moved on MAP virtual
+ // folder representation.
+ sendMnsEvent(MESSAGE_SHIFT, id,
+ "TELECOM/MSG/" + newFolder,
+ "TELECOM/MSG/OUTBOX", "SMS_GSM");
+ if ( newFolder.equalsIgnoreCase("sent")) {
+ sendMnsEvent(SENDING_SUCCESS, id,
+ "TELECOM/MSG/" + newFolder,
+ null, "SMS_GSM");
+ }
+ }
+ }
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCRSmsQueued == CR_SMS_QUEUED_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " SMS DELETED FROM QUEUED ");
+ String body = crSmsQueuedB.getString(crSmsQueuedB
+ .getColumnIndex("body"));
+ String id = crSmsQueuedB.getString(crSmsQueuedB
+ .getColumnIndex("_id"));
+ Log.d(TAG, " DELETED SMS ID " + id + " BODY "
+ + body);
+ int msgType = getMessageType(id);
+ if (msgType == -1) {
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/OUTBOX", null, "SMS_GSM");
+ } else {
+ String newFolder = getMAPFolder(msgType);
+ if ((newFolder != null)
+ && (!newFolder
+ .equalsIgnoreCase("outbox"))) {
+ // The message has moved on MAP virtual
+ // folder representation.
+ sendMnsEvent(MESSAGE_SHIFT, id,
+ "TELECOM/MSG/" + newFolder,
+ "TELECOM/MSG/OUTBOX", "SMS_GSM");
+ if ( newFolder.equalsIgnoreCase("sent")) {
+ sendMnsEvent(SENDING_SUCCESS, id,
+ "TELECOM/MSG/" + newFolder,
+ null, "SMS_GSM");
+ }
+ }
+ }
+
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCRSmsQueued == CR_SMS_QUEUED_A) {
+ currentCRSmsQueued = CR_SMS_QUEUED_B;
+ } else {
+ currentCRSmsQueued = CR_SMS_QUEUED_A;
+ }
+ }
+ }
+
+ /**
+ * Start MNS connection
+ */
+ public void start(BluetoothDevice mRemoteDevice) {
+ /* check Bluetooth enable status */
+ /*
+ * normally it's impossible to reach here if BT is disabled. Just check
+ * for safety
+ */
+ if (!mAdapter.isEnabled()) {
+ Log.e(TAG, "Can't send event when Bluetooth is disabled ");
+ return;
+ }
+
+ mDestination = mRemoteDevice;
+ int channel = -1;
+ // TODO Fix this below
+
+ if (channel != -1) {
+ if (D) Log.d(TAG, "Get MNS channel " + channel + " from cache for "
+ + mDestination);
+ mTimestamp = System.currentTimeMillis();
+ mSessionHandler.obtainMessage(SDP_RESULT, channel, -1, mDestination)
+ .sendToTarget();
+ } else {
+ sendMnsSdp();
+ }
+ }
+
+ /**
+ * Stop the transfer
+ */
+ public void stop() {
+ if (V)
+ Log.v(TAG, "stop");
+ if (mConnectThread != null) {
+ try {
+ mConnectThread.interrupt();
+ if (V) Log.v(TAG, "waiting for connect thread to terminate");
+ mConnectThread.join();
+ } catch (InterruptedException e) {
+ if (V) Log.v(TAG,
+ "Interrupted waiting for connect thread to join");
+ }
+ mConnectThread = null;
+ }
+ if (mSession != null) {
+ if (V)
+ Log.v(TAG, "Stop mSession");
+ mSession.disconnect();
+ mSession = null;
+ }
+ // TODO Do this somewhere else - Should the handler thread be gracefully closed.
+ }
+
+ /**
+ * Connect the MNS Obex client to remote server
+ */
+ private void startObexSession() {
+
+ if (V)
+ Log.v(TAG, "Create Client session with transport "
+ + mTransport.toString());
+ mSession = new BluetoothMnsObexSession(mContext, mTransport);
+ mSession.connect();
+ }
+
+ /**
+ * Check if local database contains remote device's info
+ * Else start sdp query
+ */
+ private void sendMnsSdp() {
+ if (V)
+ Log.v(TAG, "Do Opush SDP request for address " + mDestination);
+
+ mTimestamp = System.currentTimeMillis();
+
+ int channel = -1;
+
+ Method m;
+ try {
+ m = mDestination.getClass().getMethod("getServiceChannel",
+ new Class[] { ParcelUuid.class });
+ channel = (Integer) m.invoke(mDestination, BluetoothUuid_ObexMns);
+ } catch (SecurityException e2) {
+ // TODO Auto-generated catch block
+ e2.printStackTrace();
+ } catch (NoSuchMethodException e2) {
+ // TODO Auto-generated catch block
+ e2.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ // TODO: channel = mDestination.getServiceChannel(BluetoothUuid_ObexMns);
+
+ if (channel != -1) {
+ if (D)
+ Log.d(TAG, "Get MNS channel " + channel + " from SDP for "
+ + mDestination);
+
+ mSessionHandler
+ .obtainMessage(SDP_RESULT, channel, -1, mDestination)
+ .sendToTarget();
+ return;
+
+ } else {
+
+ boolean result = false;
+ if (V)
+ Log.v(TAG, "Remote Service channel not in cache");
+
+ Method m2;
+ try {
+ m2 = mDestination.getClass().getMethod("fetchUuidsWithSdp",
+ new Class[] {});
+ result = (Boolean) m2.invoke(mDestination);
+
+ } catch (SecurityException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ } catch (NoSuchMethodException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ if (result == false) {
+ Log.e(TAG, "Start SDP query failed");
+ } else {
+ // we expect framework send us Intent ACTION_UUID. otherwise we
+ // will fail
+ if (V)
+ Log.v(TAG, "Start new SDP, wait for result");
+ IntentFilter intentFilter = new IntentFilter(
+ "android.bleutooth.device.action.UUID");
+ mContext.registerReceiver(mReceiver, intentFilter);
+ return;
+ }
+ }
+ Message msg = mSessionHandler.obtainMessage(SDP_RESULT, channel, -1,
+ mDestination);
+ mSessionHandler.sendMessageDelayed(msg, 2000);
+ }
+
+ /**
+ * Receives the response of SDP query
+ */
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ Log.d(TAG, " MNS BROADCAST RECV intent: " + intent.getAction());
+
+ if (intent.getAction().equals(
+ "android.bleutooth.device.action.UUID")) {
+ BluetoothDevice device = intent
+ .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ if (V)
+ Log.v(TAG, "ACTION_UUID for device " + device);
+ if (device.equals(mDestination)) {
+ int channel = -1;
+ Parcelable[] uuid = intent
+ .getParcelableArrayExtra("android.bluetooth.device.extra.UUID");
+ if (uuid != null) {
+ ParcelUuid[] uuids = new ParcelUuid[uuid.length];
+ for (int i = 0; i < uuid.length; i++) {
+ uuids[i] = (ParcelUuid) uuid[i];
+ }
+
+ boolean result = false;
+
+ // TODO Fix this error
+ result = true;
+
+ try {
+ Class c = Class
+ .forName("android.bluetooth.BluetoothUuid");
+ Method m = c.getMethod("isUuidPresent",
+ new Class[] { ParcelUuid[].class,
+ ParcelUuid.class });
+
+ Boolean bool = false;
+ bool = (Boolean) m.invoke(c, uuids,
+ BluetoothUuid_ObexMns);
+ result = bool.booleanValue();
+
+ } catch (ClassNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (SecurityException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchMethodException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ if (result) {
+ // TODO: Check if UUID IS PRESENT
+ if (V)
+ Log.v(TAG, "SDP get MNS result for device "
+ + device);
+
+ // TODO: Get channel from mDestination
+ // TODO: .getServiceChannel(BluetoothUuid_ObexMns);
+ Method m1;
+ try {
+
+ m1 = device.getClass().getMethod(
+ "getServiceChannel",
+ new Class[] { ParcelUuid.class });
+ Integer chan = (Integer) m1.invoke(device,
+ BluetoothUuid_ObexMns);
+
+ channel = chan.intValue();
+ Log.d(TAG, " MNS SERVER Channel no " + channel);
+ if (channel == -1) {
+ channel = 2;
+ Log.d(TAG, " MNS SERVER USE TEMP CHANNEL "
+ + channel);
+ }
+ } catch (SecurityException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchMethodException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+ mSessionHandler.obtainMessage(SDP_RESULT, channel, -1,
+ mDestination).sendToTarget();
+ }
+ }
+ }
+ };
+
+ private SocketConnectThread mConnectThread;
+
+ /**
+ * This thread is used to establish rfcomm connection to
+ * remote device
+ */
+ private class SocketConnectThread extends Thread {
+ private final String host;
+
+ private final BluetoothDevice device;
+
+ private final int channel;
+
+ private boolean isConnected;
+
+ private long timestamp;
+
+ private BluetoothSocket btSocket = null;
+
+ /* create a Rfcomm Socket */
+ public SocketConnectThread(BluetoothDevice device, int channel) {
+ super("Socket Connect Thread");
+ this.device = device;
+ this.host = null;
+ this.channel = channel;
+ isConnected = false;
+ }
+
+ public void interrupt() {
+ }
+
+ @Override
+ public void run() {
+
+ timestamp = System.currentTimeMillis();
+
+ /* Use BluetoothSocket to connect */
+ try {
+ try {
+ Method m = device.getClass().getMethod(
+ "createInsecureRfcommSocket",
+ new Class[] { int.class });
+ btSocket = (BluetoothSocket) m.invoke(device, channel);
+ } catch (SecurityException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchMethodException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ } catch (Exception e1) {
+ // TODO Auto-generated catch block
+ Log.e(TAG, "Rfcomm socket create error");
+ markConnectionFailed(btSocket);
+ return;
+ }
+
+ try {
+ btSocket.connect();
+ if (V) Log.v(TAG, "Rfcomm socket connection attempt took "
+ + (System.currentTimeMillis() - timestamp) + " ms");
+ BluetoothMnsRfcommTransport transport;
+ transport = new BluetoothMnsRfcommTransport(btSocket);
+
+ BluetoothMnsPreference.getInstance(mContext).setChannel(device,
+ MNS_UUID16, channel);
+ BluetoothMnsPreference.getInstance(mContext).setName(device,
+ device.getName());
+
+ if (V) Log.v(TAG, "Send transport message "
+ + transport.toString());
+
+ mSessionHandler.obtainMessage(RFCOMM_CONNECTED, transport)
+ .sendToTarget();
+ } catch (IOException e) {
+ Log.e(TAG, "Rfcomm socket connect exception ");
+ BluetoothMnsPreference.getInstance(mContext).removeChannel(
+ device, MNS_UUID16);
+ markConnectionFailed(btSocket);
+ return;
+ }
+ }
+
+ /**
+ * RFCOMM connection failed
+ */
+ private void markConnectionFailed(Socket s) {
+ try {
+ s.close();
+ } catch (IOException e) {
+ Log.e(TAG, "TCP socket close error");
+ }
+ mSessionHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();
+ }
+
+ /**
+ * RFCOMM connection failed
+ */
+ private void markConnectionFailed(BluetoothSocket s) {
+ try {
+ s.close();
+ } catch (IOException e) {
+ if (V) Log.e(TAG, "Error when close socket");
+ }
+ mSessionHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();
+ return;
+ }
+ }
+
+ /**
+ * Check for change in MMS outbox and send a notification if there is a
+ * change
+ */
+ private void checkMmsOutbox() {
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ if (currentCRMmsOutbox == CR_MMS_OUTBOX_A) {
+ currentItemCount = crMmsOutboxA.getCount();
+ crMmsOutboxB.requery();
+ newItemCount = crMmsOutboxB.getCount();
+ } else {
+ currentItemCount = crMmsOutboxB.getCount();
+ crMmsOutboxA.requery();
+ newItemCount = crMmsOutboxA.getCount();
+ }
+
+ Log.d(TAG, "MMS OUTBOX current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (currentItemCount > newItemCount) {
+ crMmsOutboxA.moveToFirst();
+ crMmsOutboxB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crMmsOutboxA,
+ new String[] { "_id" }, crMmsOutboxB,
+ new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCRMmsOutbox == CR_MMS_OUTBOX_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " MMS DELETED FROM OUTBOX ");
+ String id = crMmsOutboxA.getString(crMmsOutboxA
+ .getColumnIndex("_id"));
+ int msgType = getMmsContainingFolder(Integer
+ .parseInt(id));
+ if (msgType == -1) {
+ // Convert to virtual handle for MMS
+ id = Integer.toString(Integer.valueOf(id)
+ + MMS_HDLR_CONSTANT);
+
+ Log.d(TAG, " DELETED MMS ID " + id);
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/OUTBOX", null, "MMS");
+ } else {
+ String newFolder = getMAPFolder(msgType);
+ // Convert to virtual handle for MMS
+ id = Integer.toString(Integer.valueOf(id)
+ + MMS_HDLR_CONSTANT);
+
+ Log.d(TAG, " MESSAGE_SHIFT MMS ID " + id);
+ if ((newFolder != null)
+ && (!newFolder.equalsIgnoreCase("outbox"))) {
+ // The message has moved on MAP virtual
+ // folder representation.
+ sendMnsEvent(MESSAGE_SHIFT, id, "TELECOM/MSG/"
+ + newFolder, "TELECOM/MSG/OUTBOX",
+ "MMS");
+ if ( newFolder.equalsIgnoreCase("sent")) {
+ sendMnsEvent(SENDING_SUCCESS, id,
+ "TELECOM/MSG/" + newFolder,
+ null, "MMS");
+ }
+ }
+ /* Mms doesn't have failed or queued type
+ * Cannot send SENDING_FAILURE as there
+ * is no indication if Sending failed
+ */
+ }
+
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCRMmsOutbox == CR_MMS_OUTBOX_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " MMS DELETED FROM OUTBOX ");
+ String id = crMmsOutboxB.getString(crMmsOutboxB
+ .getColumnIndex("_id"));
+ int msgType = getMmsContainingFolder(Integer
+ .parseInt(id));
+ if (msgType == -1) {
+ // Convert to virtual handle for MMS
+ id = Integer.toString(Integer.valueOf(id)
+ + MMS_HDLR_CONSTANT);
+ Log.d(TAG, " DELETED MMS ID " + id);
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/OUTBOX", null, "MMS");
+ } else {
+ // Convert to virtual handle for MMS
+ id = Integer.toString(Integer.valueOf(id)
+ + MMS_HDLR_CONSTANT);
+ Log.d(TAG, " DELETED MMS ID " + id);
+ String newFolder = getMAPFolder(msgType);
+ if ((newFolder != null)
+ && (!newFolder.equalsIgnoreCase("outbox"))) {
+ // The message has moved on MAP virtual
+ // folder representation.
+ sendMnsEvent(MESSAGE_SHIFT, id, "TELECOM/MSG/"
+ + newFolder, "TELECOM/MSG/OUTBOX",
+ "MMS");
+ if ( newFolder.equalsIgnoreCase("sent")) {
+ sendMnsEvent(SENDING_SUCCESS, id,
+ "TELECOM/MSG/" + newFolder,
+ null, "MMS");
+ }
+ }
+ /* Mms doesn't have failed or queued type
+ * Cannot send SENDING_FAILURE as there
+ * is no indication if Sending failed
+ */
+ }
+
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCRMmsOutbox == CR_MMS_OUTBOX_A) {
+ currentCRMmsOutbox = CR_MMS_OUTBOX_B;
+ } else {
+ currentCRMmsOutbox = CR_MMS_OUTBOX_A;
+ }
+ }
+
+ /**
+ * Check for change in MMS Drafts folder and send a notification if there is
+ * a change
+ */
+ private void checkMmsDrafts() {
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ if (currentCRMmsDraft == CR_MMS_OUTBOX_A) {
+ currentItemCount = crMmsDraftA.getCount();
+ crMmsDraftA.requery();
+ newItemCount = crMmsDraftB.getCount();
+ } else {
+ currentItemCount = crMmsDraftB.getCount();
+ crMmsDraftA.requery();
+ newItemCount = crMmsDraftA.getCount();
+ }
+
+ if (newItemCount != 0) {
+ Log.d(TAG, "MMS DRAFT breakpoint placeholder");
+ }
+
+ Log.d(TAG, "MMS DRAFT current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (currentItemCount > newItemCount) {
+ crMmsDraftA.moveToFirst();
+ crMmsDraftB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crMmsDraftA,
+ new String[] { "_id" }, crMmsDraftB, new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCRMmsDraft == CR_MMS_DRAFT_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " MMS DELETED FROM DRAFT ");
+ String id = crMmsDraftA.getString(crMmsDraftA
+ .getColumnIndex("_id"));
+ int msgType = getMmsContainingFolder(Integer
+ .parseInt(id));
+ if (msgType == -1) {
+ // Convert to virtual handle for MMS
+ id = Integer.toString(Integer.valueOf(id)
+ + MMS_HDLR_CONSTANT);
+ Log.d(TAG, " DELETED MMS ID " + id);
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/DRAFT", null, "MMS");
+ } else {
+ // Convert to virtual handle for MMS
+ id = Integer.toString(Integer.valueOf(id)
+ + MMS_HDLR_CONSTANT);
+ Log.d(TAG, " DELETED MMS ID " + id);
+ String newFolder = getMAPFolder(msgType);
+ if ((newFolder != null)
+ && (!newFolder.equalsIgnoreCase("draft"))) {
+ // The message has moved on MAP virtual
+ // folder representation.
+ sendMnsEvent(MESSAGE_SHIFT, id, "TELECOM/MSG/"
+ + newFolder, "TELECOM/MSG/DRAFT", "MMS");
+ if ( newFolder.equalsIgnoreCase("sent")) {
+ sendMnsEvent(SENDING_SUCCESS, id,
+ "TELECOM/MSG/" + newFolder,
+ null, "MMS");
+ }
+ }
+ }
+
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCRMmsDraft == CR_MMS_DRAFT_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " MMS DELETED FROM DRAFT ");
+ String id = crMmsDraftB.getString(crMmsDraftB
+ .getColumnIndex("_id"));
+ int msgType = getMmsContainingFolder(Integer
+ .parseInt(id));
+ if (msgType == -1) {
+ // Convert to virtual handle for MMS
+ id = Integer.toString(Integer.valueOf(id)
+ + MMS_HDLR_CONSTANT);
+ Log.d(TAG, " DELETED MMS ID " + id);
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/DRAFT", null, "MMS");
+ } else {
+ // Convert to virtual handle for MMS
+ id = Integer.toString(Integer.valueOf(id)
+ + MMS_HDLR_CONSTANT);
+ Log.d(TAG, " DELETED MMS ID " + id);
+ String newFolder = getMAPFolder(msgType);
+ if ((newFolder != null)
+ && (!newFolder.equalsIgnoreCase("draft"))) {
+ // The message has moved on MAP virtual
+ // folder representation.
+ sendMnsEvent(MESSAGE_SHIFT, id, "TELECOM/MSG/"
+ + newFolder, "TELECOM/MSG/DRAFT", "MMS");
+ if ( newFolder.equalsIgnoreCase("sent")) {
+ sendMnsEvent(SENDING_SUCCESS, id,
+ "TELECOM/MSG/" + newFolder,
+ null, "MMS");
+ }
+ }
+ }
+
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCRMmsDraft == CR_MMS_DRAFT_A) {
+ currentCRMmsDraft = CR_MMS_DRAFT_B;
+ } else {
+ currentCRMmsDraft = CR_MMS_DRAFT_A;
+ }
+ }
+
+ /**
+ * Check for change in MMS Inbox folder and send a notification if there is
+ * a change
+ */
+ private void checkMmsInbox() {
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ if (currentCRMmsInbox == CR_MMS_INBOX_A) {
+ currentItemCount = crMmsInboxA.getCount();
+ crMmsInboxB.requery();
+ newItemCount = crMmsInboxB.getCount();
+ } else {
+ currentItemCount = crMmsInboxB.getCount();
+ crMmsInboxA.requery();
+ newItemCount = crMmsInboxA.getCount();
+ }
+
+ Log.d(TAG, "MMS INBOX current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (currentItemCount > newItemCount) {
+ crMmsInboxA.moveToFirst();
+ crMmsInboxB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crMmsInboxA,
+ new String[] { "_id" }, crMmsInboxB, new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCRMmsInbox == CR_MMS_INBOX_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " MMS DELETED FROM INBOX ");
+ String id = crMmsInboxA.getString(crMmsInboxA
+ .getColumnIndex("_id"));
+ int msgType = getMmsContainingFolder(Integer
+ .parseInt(id));
+ if (msgType == -1) {
+ // Convert to virtual handle for MMS
+ id = Integer.toString(Integer.valueOf(id)
+ + MMS_HDLR_CONSTANT);
+ Log.d(TAG, " DELETED MMS ID " + id);
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/INBOX", null, "MMS");
+ } else {
+ Log.d(TAG, "Shouldn't reach here as you cannot "
+ + "move msg from Inbox to any other folder");
+ }
+ } else {
+ // TODO - The current(old) query doesn't have this
+ // row;
+ // implies it was added
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCRMmsInbox == CR_MMS_INBOX_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " MMS DELETED FROM INBOX ");
+ String id = crMmsInboxB.getString(crMmsInboxB
+ .getColumnIndex("_id"));
+ int msgType = getMmsContainingFolder(Integer
+ .parseInt(id));
+ if (msgType == -1) {
+ // Convert to virtual handle for MMS
+ id = Integer.toString(Integer.valueOf(id)
+ + MMS_HDLR_CONSTANT);
+ Log.d(TAG, " DELETED MMS ID " + id);
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/INBOX", null, "MMS");
+ } else {
+ Log.d(TAG, "Shouldn't reach here as you cannot "
+ + "move msg from Inbox to any other folder");
+ }
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCRMmsInbox == CR_MMS_INBOX_A) {
+ currentCRMmsInbox = CR_MMS_INBOX_B;
+ } else {
+ currentCRMmsInbox = CR_MMS_INBOX_A;
+ }
+ }
+ /**
+ * Check for change in MMS Sent folder and send a notification if there is
+ * a change
+ */
+ private void checkMmsSent() {
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ if (currentCRMmsSent == CR_MMS_SENT_A) {
+ currentItemCount = crMmsSentA.getCount();
+ crMmsSentB.requery();
+ newItemCount = crMmsSentB.getCount();
+ } else {
+ currentItemCount = crMmsSentB.getCount();
+ crMmsSentA.requery();
+ newItemCount = crMmsSentA.getCount();
+ }
+
+ Log.d(TAG, "MMS SENT current " + currentItemCount + " new "
+ + newItemCount);
+
+ if (currentItemCount > newItemCount) {
+ crMmsSentA.moveToFirst();
+ crMmsSentB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crMmsSentA,
+ new String[] { "_id" }, crMmsSentB, new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCRMmsSent == CR_MMS_SENT_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " MMS DELETED FROM SENT ");
+ String id = crMmsSentA.getString(crMmsSentA
+ .getColumnIndex("_id"));
+ int msgType = getMmsContainingFolder(Integer
+ .parseInt(id));
+ if (msgType == -1) {
+ // Convert to virtual handle for MMS
+ id = Integer.toString(Integer.valueOf(id)
+ + MMS_HDLR_CONSTANT);
+ Log.d(TAG, " DELETED MMS ID " + id);
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/SENT", null, "MMS");
+ } else {
+ Log.d(TAG, "Shouldn't reach here as you cannot "
+ + "move msg from Sent to any other folder");
+ }
+ } else {
+ // TODO - The current(old) query doesn't have this
+ // row;
+ // implies it was added
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCRMmsSent == CR_MMS_SENT_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ Log.d(TAG, " MMS DELETED FROM SENT ");
+ String id = crMmsSentB.getString(crMmsSentB
+ .getColumnIndex("_id"));
+ int msgType = getMmsContainingFolder(Integer
+ .parseInt(id));
+ if (msgType == -1) {
+ // Convert to virtual handle for MMS
+ id = Integer.toString(Integer.valueOf(id)
+ + MMS_HDLR_CONSTANT);
+ Log.d(TAG, " DELETED MMS ID " + id);
+ sendMnsEvent(MESSAGE_DELETED, id,
+ "TELECOM/MSG/SENT", null, "MMS");
+ } else {
+ Log.d(TAG, "Shouldn't reach here as you cannot "
+ + "move msg from Sent to any other folder");
+ }
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCRMmsSent == CR_MMS_SENT_A) {
+ currentCRMmsSent = CR_MMS_SENT_B;
+ } else {
+ currentCRMmsSent = CR_MMS_SENT_A;
+ }
+ }
+
+ /**
+ * Check for MMS message being added and send a notification if there is a
+ * change
+ */
+ private void checkMmsAdded() {
+
+ int currentItemCount = 0;
+ int newItemCount = 0;
+
+ if (currentCRMms == CR_MMS_A) {
+ currentItemCount = crMmsA.getCount();
+ crMmsB.requery();
+ newItemCount = crMmsB.getCount();
+ } else {
+ currentItemCount = crMmsB.getCount();
+ crMmsA.requery();
+ newItemCount = crMmsA.getCount();
+ }
+
+ Log.d(TAG, "MMS current " + currentItemCount + " new " + newItemCount);
+
+ if (newItemCount > currentItemCount) {
+ crMmsA.moveToFirst();
+ crMmsB.moveToFirst();
+
+ CursorJoiner joiner = new CursorJoiner(crMmsA,
+ new String[] { "_id" }, crMmsB, new String[] { "_id" });
+
+ CursorJoiner.Result joinerResult;
+ while (joiner.hasNext()) {
+ joinerResult = joiner.next();
+ switch (joinerResult) {
+ case LEFT:
+ // handle case where a row in cursor1 is unique
+ if (currentCRMms == CR_MMS_A) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ Log.d(TAG, " MMS ADDED TO INBOX ");
+ String id1 = crMmsA.getString(crMmsA
+ .getColumnIndex("_id"));
+ int msgType = getMmsContainingFolder(Integer
+ .parseInt(id1));
+ String folder = getMAPFolder(msgType);
+ if (folder != null) {
+ // Convert to virtual handle for MMS
+ id1 = Integer.toString(Integer.valueOf(id1)
+ + MMS_HDLR_CONSTANT);
+ Log.d(TAG, " ADDED MMS ID " + id1);
+ sendMnsEvent(NEW_MESSAGE, id1, "TELECOM/MSG/"
+ + folder, null, "MMS");
+ } else {
+ Log.d(TAG, " ADDED TO UNKNOWN FOLDER");
+ }
+ }
+ break;
+ case RIGHT:
+ // handle case where a row in cursor2 is unique
+ if (currentCRMms == CR_MMS_B) {
+ // The new query doesn't have this row; implies it
+ // was deleted
+ } else {
+ // The current(old) query doesn't have this row;
+ // implies it was added
+ Log.d(TAG, " MMS ADDED ");
+ String id1 = crMmsB.getString(crMmsB
+ .getColumnIndex("_id"));
+ int msgType = getMmsContainingFolder(Integer
+ .parseInt(id1));
+ String folder = getMAPFolder(msgType);
+ if (folder != null) {
+ // Convert to virtual handle for MMS
+ id1 = Integer.toString(Integer.valueOf(id1)
+ + MMS_HDLR_CONSTANT);
+
+ Log.d(TAG, " ADDED MMS ID " + id1);
+ sendMnsEvent(NEW_MESSAGE, id1, "TELECOM/MSG/"
+ + folder, null, "MMS");
+ } else {
+ Log.d(TAG, " ADDED TO UNKNOWN FOLDER");
+ }
+ }
+ break;
+ case BOTH:
+ // handle case where a row with the same key is in both
+ // cursors
+ break;
+ }
+ }
+ }
+ if (currentCRMms == CR_MMS_A) {
+ currentCRMms = CR_MMS_B;
+ } else {
+ currentCRMms = CR_MMS_A;
+ }
+ }
+ /**
+ * Get the folder name (MAP representation) based on the message Handle
+ */
+ private int getMmsContainingFolder(int msgID) {
+ int folderNum = -1;
+ String whereClause = " _id= " + msgID;
+ Uri uri = Uri.parse("content://mms/");
+ ContentResolver cr = mContext.getContentResolver();
+ Cursor cursor = cr.query(uri, null, whereClause, null, null);
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ int msgboxInd = cursor.getColumnIndex("msg_box");
+ folderNum = cursor.getInt(msgboxInd);
+ }
+ cursor.close();
+ return folderNum;
+ }
+
+}
diff --git a/src/com/android/bluetooth/map/BluetoothMnsObexSession.java b/src/com/android/bluetooth/map/BluetoothMnsObexSession.java
new file mode 100644
index 000000000..6d3c663ee
--- /dev/null
+++ b/src/com/android/bluetooth/map/BluetoothMnsObexSession.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.util.Log;
+
+import javax.obex.*;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.FileInputStream;
+import java.io.File;
+
+import org.apache.http.Header;
+/**
+ * This class runs as an OBEX client
+ */
+
+// TBD - Do applications need to do anything for power management?
+
+public class BluetoothMnsObexSession {
+
+ private static final String TAG = "BtMns ObexClient";
+ private static final boolean D = BluetoothMasService.DEBUG;
+ private static final boolean V = BluetoothMasService.VERBOSE;
+
+ public final static int MSG_SESSION_ERROR = 1;
+ public final static int MSG_CONNECT_TIMEOUT = 2;
+
+ private ObexTransport mTransport;
+
+ private Context mContext;
+
+
+ private volatile boolean mWaitingForRemote;
+
+ private Handler mCallback;
+
+ private static final String TYPE_EVENT = "x-bt/MAP-event-report";
+
+ public BluetoothMnsObexSession(Context context, ObexTransport transport) {
+ if (transport == null) {
+ throw new NullPointerException("transport is null");
+ }
+ mContext = context;
+ mTransport = transport;
+ }
+
+
+ private ClientSession mCs;
+
+
+ private boolean mConnected = false;
+
+ public void disconnect() {
+ try {
+ if (mCs != null) {
+ mCs.disconnect(null);
+ }
+ mCs = null;
+ if (D) Log.d(TAG, "OBEX session disconnected");
+ } catch (IOException e) {
+ Log.w(TAG, "OBEX session disconnect error" + e);
+ }
+ try {
+ if (mCs != null) {
+ if (D) Log.d(TAG, "OBEX session close mCs");
+ mCs.close();
+ if (D) Log.d(TAG, "OBEX session closed");
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "OBEX session close error" + e);
+ }
+ if (mTransport != null) {
+ try {
+ mTransport.close();
+ } catch (IOException e) {
+ Log.e(TAG, "mTransport.close error");
+ }
+
+ }
+ }
+
+ private HeaderSet hsConnect = null;
+
+ public void connect() {
+ Log.d(TAG, "Create ClientSession with transport " + mTransport.toString());
+ try {
+ mCs = new ClientSession(mTransport);
+ mConnected = true;
+ } catch (IOException e1) {
+ Log.e(TAG, "OBEX session create error");
+ }
+ if (mConnected) {
+ mConnected = false;
+ HeaderSet hs = new HeaderSet();
+ // bb582b41-420c-11db-b0de-0800200c9a66
+ byte[] mnsTarget = { (byte) 0xbb, (byte) 0x58, (byte) 0x2b, (byte) 0x41,
+ (byte) 0x42, (byte) 0x0c, (byte) 0x11, (byte) 0xdb,
+ (byte) 0xb0, (byte) 0xde, (byte) 0x08, (byte) 0x00,
+ (byte) 0x20, (byte) 0x0c, (byte) 0x9a, (byte) 0x66 };
+ hs.setHeader(HeaderSet.TARGET, mnsTarget);
+
+ synchronized (this) {
+ mWaitingForRemote = true;
+ }
+ try {
+ hsConnect = mCs.connect(hs);
+ if (D) Log.d(TAG, "OBEX session created");
+ mConnected = true;
+ } catch (IOException e) {
+ Log.e(TAG, "OBEX session connect error");
+ }
+ }
+ synchronized (this) {
+ mWaitingForRemote = false;
+ }
+ }
+
+ public int sendEvent(File file, byte masInstanceId) {
+
+ boolean error = false;
+ int responseCode = -1;
+ HeaderSet request;
+ byte[] val = new byte[1];
+ val[0] = masInstanceId;
+ request = new HeaderSet();
+ ApplicationParameter ap = new ApplicationParameter();
+ ap.addAPPHeader((byte)BluetoothMasSpecParams.MAS_TAG_MAS_INSTANCE_ID,
+ (byte)BluetoothMasSpecParams.MAS_TAG_MAS_INSTANCE_ID_LEN,
+ val);
+ request.setHeader(HeaderSet.TYPE, TYPE_EVENT);
+ request.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());
+
+ request.mConnectionID = new byte[4];
+ System.arraycopy(hsConnect.mConnectionID, 0, request.mConnectionID, 0, 4);
+
+ ClientOperation putOperation = null;
+ OutputStream outputStream = null;
+ InputStream inputStream = null;
+ try {
+ synchronized (this) {
+ mWaitingForRemote = true;
+ }
+ // Send the header first and then the body
+ try {
+ if (V) Log.v(TAG, "Send headerset Event ");
+ putOperation = (ClientOperation)mCs.put(request);
+ // TODO - Should this be kept or Removed
+
+ } catch (IOException e) {
+ Log.e(TAG, "Error when put HeaderSet ");
+ error = true;
+ }
+ synchronized (this) {
+ mWaitingForRemote = false;
+ }
+ if (!error) {
+ try {
+ if (V) Log.v(TAG, "Send headerset Event ");
+ outputStream = putOperation.openOutputStream();
+ inputStream = putOperation.openInputStream();
+ } catch (IOException e) {
+ Log.e(TAG, "Error when opening OutputStream");
+ error = true;
+ }
+ }
+
+ if (!error) {
+ int position = 0;
+ int readLength = 0;
+ boolean okToProceed = true;
+ long timestamp = 0;
+ int outputBufferSize = putOperation.getMaxPacketSize();
+ byte[] buffer = new byte[outputBufferSize];
+
+
+ FileInputStream fileInputStream = new FileInputStream(file);
+ BufferedInputStream a = new BufferedInputStream(fileInputStream, 0x4000);
+
+ while (okToProceed && (position != file.length())) {
+ if (V) timestamp = System.currentTimeMillis();
+
+ readLength = a.read(buffer, 0, outputBufferSize);
+ outputStream.write(buffer, 0, readLength);
+
+ /* check remote abort */
+ responseCode = putOperation.getResponseCode();
+ responseCode = ResponseCodes.OBEX_HTTP_OK;
+
+ if (V) Log.v(TAG, "Response code is " + responseCode);
+ if (responseCode != ResponseCodes.OBEX_HTTP_CONTINUE
+ && responseCode != ResponseCodes.OBEX_HTTP_OK) {
+ /* abort happens */
+ okToProceed = false;
+ } else {
+ position += readLength;
+ if (V) {
+ Log.v(TAG, "Sending file position = " + position
+ + " readLength " + readLength + " bytes took "
+ + (System.currentTimeMillis() - timestamp) + " ms");
+ }
+ }
+ }
+ if (position == file.length()) {
+ Log.i(TAG, "SendFile finished send out file " + file.length()
+ + " length " + file.length());
+ outputStream.close();
+ } else {
+ error = true;
+ // TBD - Is Output stream close needed here
+ putOperation.abort();
+ Log.i(TAG, "SendFile interrupted when send out file "
+ + " at " + position + " of " + file.length());
+ }
+ }
+ } catch (IOException e) {
+ handleSendException(e.toString());
+ } catch (NullPointerException e) {
+ handleSendException(e.toString());
+ } catch (IndexOutOfBoundsException e) {
+ handleSendException(e.toString());
+ } finally {
+ try {
+ if (!error) {
+ responseCode = putOperation.getResponseCode();
+ if (responseCode != -1) {
+ if (V) Log.v(TAG, "Get response code " + responseCode);
+ if (responseCode != ResponseCodes.OBEX_HTTP_OK) {
+ Log.i(TAG, "Response error code is " + responseCode);
+ }
+ }
+ }
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ if (putOperation != null) {
+ putOperation.close();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error when closing stream after send");
+ }
+ }
+
+ return responseCode;
+ }
+
+ private void handleSendException(String exception) {
+ Log.e(TAG, "Error when sending event: " + exception);
+ }
+}
diff --git a/src/com/android/bluetooth/map/BluetoothMnsPreference.java b/src/com/android/bluetooth/map/BluetoothMnsPreference.java
new file mode 100644
index 000000000..0da490f16
--- /dev/null
+++ b/src/com/android/bluetooth/map/BluetoothMnsPreference.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map;
+
+import java.util.HashMap;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.util.Log;
+
+/**
+ * This class cache Bluetooth device name and channel locally. Its a temp
+ * solution which should be replaced by bluetooth_devices in SettingsProvider
+ */
+public class BluetoothMnsPreference {
+
+ private static final String TAG = "BluetoothMnsPreference";
+ private static final boolean D = BluetoothMasService.DEBUG;
+ private static final boolean V = BluetoothMasService.VERBOSE;
+
+ private static BluetoothMnsPreference INSTANCE;
+
+ /* Used when obtaining a reference to the singleton instance. */
+ private static Object INSTANCE_LOCK = new Object();
+
+ private boolean mInitialized;
+
+ private Context mContext;
+
+ private SharedPreferences mNamePreference;
+
+ private SharedPreferences mChannelPreference;
+
+ private HashMap<String, Integer> mChannels = new HashMap<String, Integer>();
+
+ private HashMap<String, String> mNames = new HashMap<String, String>();
+
+ public static final String BLUETOOTHMNS_NAME_PREFERENCE = "btmns_names";
+
+ public static final String BLUETOOTHMNS_CHANNEL_PREFERENCE = "btmns_channels";
+
+
+ public static BluetoothMnsPreference getInstance(Context context) {
+ synchronized (INSTANCE_LOCK) {
+ if (INSTANCE == null) {
+ INSTANCE = new BluetoothMnsPreference();
+ }
+ if (!INSTANCE.init(context)) {
+ return null;
+ }
+ return INSTANCE;
+ }
+ }
+
+ private boolean init(Context context) {
+ if (mInitialized)
+ return true;
+ mInitialized = true;
+
+ mContext = context;
+
+ mNamePreference = mContext.getSharedPreferences(
+ BLUETOOTHMNS_NAME_PREFERENCE, Context.MODE_PRIVATE);
+ mChannelPreference = mContext.getSharedPreferences(
+ BLUETOOTHMNS_CHANNEL_PREFERENCE, Context.MODE_PRIVATE);
+
+ mNames = (HashMap<String, String>) mNamePreference.getAll();
+ mChannels = (HashMap<String, Integer>) mChannelPreference.getAll();
+
+ return true;
+ }
+
+ private String getChannelKey(BluetoothDevice remoteDevice, int uuid) {
+ return remoteDevice.getAddress() + "_" + Integer.toHexString(uuid);
+ }
+
+ public String getName(BluetoothDevice remoteDevice) {
+ if (remoteDevice.getAddress().equals("FF:FF:FF:00:00:00")) {
+ return "localhost";
+ }
+ if (!mNames.isEmpty()) {
+ String name = mNames.get(remoteDevice.getAddress());
+ if (name != null) {
+ return name;
+ }
+ }
+ return null;
+ }
+
+ public int getChannel(BluetoothDevice remoteDevice, int uuid) {
+ String key = getChannelKey(remoteDevice, uuid);
+ if (V) Log.v(TAG, "getChannel " + key);
+ Integer channel = null;
+ if (mChannels != null) {
+ channel = mChannels.get(key);
+ if (V) Log.v(TAG, "getChannel for " + remoteDevice + "_"
+ + Integer.toHexString(uuid) + " as " + channel);
+ }
+ return (channel != null) ? channel : -1;
+ }
+
+ public void setName(BluetoothDevice remoteDevice, String name) {
+ if (V) Log.v(TAG, "Setname for " + remoteDevice + " to " + name);
+ if (!name.equals(getName(remoteDevice))) {
+ Editor ed = mNamePreference.edit();
+ ed.putString(remoteDevice.getAddress(), name);
+ ed.commit();
+ mNames.put(remoteDevice.getAddress(), name);
+ }
+ }
+
+ public void setChannel(BluetoothDevice remoteDevice, int uuid, int channel) {
+ if (V) Log.v(TAG, "Setchannel for " + remoteDevice + "_"
+ + Integer.toHexString(uuid) + " to " + channel);
+ if (channel != getChannel(remoteDevice, uuid)) {
+ String key = getChannelKey(remoteDevice, uuid);
+ Editor ed = mChannelPreference.edit();
+ ed.putInt(key, channel);
+ ed.commit();
+ mChannels.put(key, channel);
+ }
+ }
+
+ public void removeChannel(BluetoothDevice remoteDevice, int uuid) {
+ String key = getChannelKey(remoteDevice, uuid);
+ Editor ed = mChannelPreference.edit();
+ ed.remove(key);
+ ed.commit();
+ mChannels.remove(key);
+ }
+
+ public void dump() {
+ Log.d(TAG, "Dumping Names: ");
+ Log.d(TAG, mNames.toString());
+ Log.d(TAG, "Dumping Channels: ");
+ Log.d(TAG, mChannels.toString());
+ }
+}
diff --git a/src/com/android/bluetooth/map/BluetoothMnsRfcommTransport.java b/src/com/android/bluetooth/map/BluetoothMnsRfcommTransport.java
new file mode 100644
index 000000000..d1a48bf4f
--- /dev/null
+++ b/src/com/android/bluetooth/map/BluetoothMnsRfcommTransport.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.obex.*;
+import android.bluetooth.BluetoothSocket;
+
+
+
+public class BluetoothMnsRfcommTransport implements ObexTransport {
+
+ private final BluetoothSocket mSocket;
+
+ public BluetoothMnsRfcommTransport(BluetoothSocket socket) {
+ super();
+ this.mSocket = socket;
+ }
+
+ public void close() throws IOException {
+ mSocket.close();
+ }
+
+ public DataInputStream openDataInputStream() throws IOException {
+ return new DataInputStream(openInputStream());
+ }
+
+ public DataOutputStream openDataOutputStream() throws IOException {
+ return new DataOutputStream(openOutputStream());
+ }
+
+ public InputStream openInputStream() throws IOException {
+ return mSocket.getInputStream();
+ }
+
+ public OutputStream openOutputStream() throws IOException {
+ return mSocket.getOutputStream();
+ }
+
+ public void connect() throws IOException {
+ }
+
+ public void create() throws IOException {
+ }
+
+ public void disconnect() throws IOException {
+ }
+
+ public void listen() throws IOException {
+ }
+
+ public boolean isConnected() throws IOException {
+ // TODO: add implementation
+ return true;
+ }
+
+ public String getRemoteAddress() {
+ if (mSocket == null)
+ return null;
+ return mSocket.getRemoteDevice().getAddress();
+ }
+
+}
diff --git a/src/com/android/bluetooth/map/MapUtils/BmessageConsts.java b/src/com/android/bluetooth/map/MapUtils/BmessageConsts.java
new file mode 100644
index 000000000..fa5ee6fb0
--- /dev/null
+++ b/src/com/android/bluetooth/map/MapUtils/BmessageConsts.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map.MapUtils;
+
+public class BmessageConsts {
+
+ public String bmsg_version = null;
+ public String status = null;
+ public String type = null;
+ public String folder = null;
+ public String vcard_version = null;
+ public String recipient_vcard_name = null;
+ public String recipient_vcard_phone_number = null;
+ public String recipient_vcard_email = null;
+ public String originator_vcard_name = null;
+ public String originator_vcard_phone_number = null;
+ public String originator_vcard_email = null;
+ public String body_encoding = null;
+ public int body_length = 0;
+ public String body_msg = null;
+ public String body_part_ID = null;
+ public String body_language = null;
+ public String body_charset = null;
+ public String subject = null;
+
+ // Setters and Getters
+
+ public String getBody_language() {
+ return body_language;
+ }
+
+ public void setBody_language(String body_language) {
+ this.body_language = body_language;
+ }
+
+ public String getBody_part_ID() {
+ return body_part_ID;
+ }
+
+ public void setBody_part_ID(String body_part_ID) {
+ this.body_part_ID = body_part_ID;
+ }
+
+ public String getBmsg_version() {
+ return bmsg_version;
+ }
+
+ public void setBmsg_version(String bmsg_version) {
+ this.bmsg_version = bmsg_version;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getFolder() {
+ return folder;
+ }
+
+ public void setFolder(String folder) {
+ this.folder = folder;
+ }
+
+ public String getVcard_version() {
+ return vcard_version;
+ }
+
+ public void setVcard_version(String vcard_version) {
+ this.vcard_version = vcard_version;
+ }
+
+ // Name
+ public String getRecipientVcard_name() {
+ return recipient_vcard_name;
+ }
+
+ public String getOriginatorVcard_name() {
+ return originator_vcard_name;
+ }
+
+ // recipient_vcard_name
+
+ public void setRecipientVcard_name(String vcard_name) {
+ this.recipient_vcard_name = vcard_name;
+ }
+
+ public void setOriginatorVcard_name(String vcard_name) {
+ this.originator_vcard_name = vcard_name;
+ }
+
+ // Name
+
+ // Phone
+ public String getRecipientVcard_email() {
+ return recipient_vcard_email;
+ }
+
+ public String getOriginatorVcard_email() {
+ return originator_vcard_email;
+ }
+
+ public void setRecipientVcard_email(String email) {
+ this.recipient_vcard_email = email;
+ }
+
+ public void setOriginatorVcard_email(String email) {
+ this.originator_vcard_email = email;
+ }
+
+ // Phone
+ //Email
+ public String getRecipientVcard_phone_number() {
+ return recipient_vcard_phone_number;
+ }
+
+ public String getOriginatorVcard_phone_number() {
+ return originator_vcard_phone_number;
+ }
+
+ public void setRecipientVcard_phone_number(String vcard_phone_number) {
+ this.recipient_vcard_phone_number = vcard_phone_number;
+ }
+
+ public void setOriginatorVcard_phone_number(String vcard_phone_number) {
+ this.originator_vcard_phone_number = vcard_phone_number;
+ }
+
+
+
+
+ //end Email
+
+ public String getBody_charset() {
+ return body_charset;
+ }
+
+ public void setBody_charset(String body_charset) {
+ this.body_charset = body_charset;
+ }
+
+ public String getBody_encoding() {
+ return body_encoding;
+ }
+
+ public void setBody_encoding(String body_encoding) {
+ this.body_encoding = body_encoding;
+ }
+
+ public int getBody_length() {
+ return body_length;
+ }
+
+ public void setBody_length(int body_length) {
+ this.body_length = body_length;
+ }
+
+ public String getBody_msg() {
+ return body_msg;
+ }
+
+ public void setBody_msg(String body_msg) {
+ this.body_msg = body_msg;
+ }
+ public String getSubject() {
+ return subject;
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+}
diff --git a/src/com/android/bluetooth/map/MapUtils/CommonUtils.java b/src/com/android/bluetooth/map/MapUtils/CommonUtils.java
new file mode 100644
index 000000000..b76b17555
--- /dev/null
+++ b/src/com/android/bluetooth/map/MapUtils/CommonUtils.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map.MapUtils;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.Context;
+
+public class CommonUtils {
+
+ public final String TAG = "CommonUtils";
+
+ public String getFullPath(String child, Context context, List<String> folderList, String CurrentPath) {
+
+ String tempPath = null;
+ List<String> completeFolderList = new ArrayList<String>();
+ EmailUtils eu = new EmailUtils();
+ completeFolderList = eu.folderListEmail(folderList, context);
+
+ if (child != null) {
+ if (CurrentPath == null) {
+ if (child.equals("telecom")) {
+ // Telecom is fine
+ tempPath = new String("telecom");
+ }
+ }
+ else if (CurrentPath.equals("telecom")) {
+ if (child.equals("msg")) {
+ tempPath = CurrentPath + "/" + child;
+ }
+ }
+ else if (CurrentPath.equals("telecom/msg")) {
+ for (String folder : completeFolderList) { //TODO NEED TO LOOK INTO THIS
+ if(child.toUpperCase().contains("GMAIL")){
+ if (folder.equalsIgnoreCase(child)
+ || folder.toUpperCase().contains(child.toUpperCase())) {
+ //added second condition above for gmail sent folder
+ tempPath = CurrentPath + "/" + folder;
+ break;
+ }
+ }
+ else{
+ if (folder.equalsIgnoreCase(child)) {
+ tempPath = CurrentPath + "/" + folder;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return tempPath;
+ }
+
+}
diff --git a/src/com/android/bluetooth/map/MapUtils/EmailUtils.java b/src/com/android/bluetooth/map/MapUtils/EmailUtils.java
new file mode 100644
index 000000000..d3a98b970
--- /dev/null
+++ b/src/com/android/bluetooth/map/MapUtils/EmailUtils.java
@@ -0,0 +1,561 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map.MapUtils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.text.format.Time;
+import android.util.Log;
+import android.util.TimeFormatException;
+
+import com.android.bluetooth.map.BluetoothMasAppIf.BluetoothMasMessageRsp;
+import com.android.bluetooth.map.BluetoothMasAppParams;
+
+
+public class EmailUtils {
+
+ public final String TAG = "EmailUtils";
+ public static final int BIT_SUBJECT = 0x1;
+ public static final int BIT_DATETIME = 0x2;
+ public static final int BIT_SENDER_NAME = 0x4;
+ public static final int BIT_SENDER_ADDRESSING = 0x8;
+
+ public static final int BIT_RECIPIENT_NAME = 0x10;
+ public static final int BIT_RECIPIENT_ADDRESSING = 0x20;
+ public static final int BIT_TYPE = 0x40;
+ public static final int BIT_SIZE = 0x80;
+
+ public static final int BIT_RECEPTION_STATUS = 0x100;
+ public static final int BIT_TEXT = 0x200;
+ public static final int BIT_ATTACHMENT_SIZE = 0x400;
+ public static final int BIT_PRIORITY = 0x800;
+
+ public static final int BIT_READ = 0x1000;
+ public static final int BIT_SENT = 0x2000;
+ public static final int BIT_PROTECTED = 0x4000;
+ public static final int BIT_REPLYTO_ADDRESSING = 0x8000;
+
+ public static final int EMAIL_HDLR_CONSTANT = 200000;
+
+ // TODO: Is this needed? public static int deletedEmailFolderId = -1;
+
+ public List<String> folderListEmail(List<String> folderList, Context context) {
+ String[] projection = new String[] {"displayName"};
+ Uri uri = Uri.parse("content://com.android.email.provider/mailbox");
+ Cursor cr = context.getContentResolver().query(uri, projection, null, null, null);
+
+ if ( cr.moveToFirst()) {
+ do {
+ Log.d(TAG, " Column Name: "+ cr.getColumnName(0) + " Value: " + cr.getString(0));
+ int folderFlag = 0;
+ for(int i=0; i< folderList.size(); i++){
+ if(folderList.get(i).equalsIgnoreCase(cr.getString(0))){
+ folderFlag = 1;
+ break;
+ }
+ }
+ if(cr.getString(0).equalsIgnoreCase("Drafts")){ //TODO need to remove this hardcoded value
+ folderFlag = 1;
+ }
+ if(folderFlag == 0){
+ folderList.add(cr.getString(0));
+ }
+
+ } while ( cr.moveToNext());
+ }
+ Log.d(TAG, " Folder Listing of SMS,MMS and EMAIL: "+folderList);
+
+ return folderList;
+
+ }
+
+ public String getWhereIsQueryForTypeEmail(String folder, Context context) {
+
+ Log.d(TAG, ":: Inside getWhereIsQueryForTypeEmail ::"+ folder);
+ String query = "mailboxKey = -1";
+ String folderId;
+ Uri uri = Uri.parse("content://com.android.email.provider/mailbox");
+
+ Cursor cr = context.getContentResolver().query(
+ uri, null, "(UPPER(displayName) = '"+ folder.toUpperCase()+"')" , null, null);
+
+ if ( cr.moveToFirst()) {
+ do {
+ Log.d(TAG, ":: Inside getWhereIsQueryForTypeEmail Folder Name ::"+ cr.getString(cr.getColumnIndex("displayName")));
+ folderId = cr.getString(cr.getColumnIndex("_id"));
+ query = "mailboxKey = "+ folderId;
+ break;
+ } while ( cr.moveToNext());
+ }
+
+ return query;
+ }
+
+ public int getMessageSizeEmail(int messageId, Context context) {
+
+ Log.d(TAG, ":: Inside getMessageSizeEmail ::"+ messageId);
+ int msgSize = 0;
+ String textContent;
+ Uri uri = Uri.parse("content://com.android.email.provider/body");
+
+ Cursor cr = context.getContentResolver().query(
+ uri, null, "messageKey = "+ messageId , null, null);
+
+ if ( cr.moveToFirst()) {
+ do {
+ textContent = cr.getString(cr.getColumnIndex("textContent"));
+ msgSize = textContent.length();
+ break;
+ } while ( cr.moveToNext());
+ }
+
+ return msgSize;
+ }
+
+ public String getFolderName(String[] splitStringsEmail) {
+ String folderName;
+ Log.d(TAG, ":: Inside getFolderName ::"+ splitStringsEmail);
+
+ if(splitStringsEmail[2].trim().equalsIgnoreCase("[Gmail]") || splitStringsEmail[2].trim().contains("Gmail")){
+ folderName = splitStringsEmail[2]+"/"+splitStringsEmail[3];
+ }
+ else{
+ folderName = splitStringsEmail[2];
+ }
+
+ Log.d(TAG, "folderName :: " + folderName);
+
+ return folderName;
+ }
+
+ public String getConditionString(String folderName, Context context, BluetoothMasAppParams appParams) {
+ String whereClauseEmail = getWhereIsQueryForTypeEmail(folderName, context);
+
+ /* Filter readstatus: 0 no filtering, 0x01 get unread, 0x10 get read */
+ if (appParams.FilterReadStatus != 0) {
+ Log.d(TAG, "##Inside Filter Read Status.....##:");
+ if ((appParams.FilterReadStatus & 0x1) != 0) {
+ if (whereClauseEmail != "") {
+ whereClauseEmail += " AND ";
+ }
+ whereClauseEmail += " flagRead = 0 ";
+ }
+ Log.d(TAG, "##Filter Read Status Value##:"+appParams.FilterReadStatus);
+ Log.d(TAG, "##Filter Read Status Condition Value##:"+(appParams.FilterReadStatus & 0x10));
+ if ((appParams.FilterReadStatus & 0x10) != 0) {
+ if (whereClauseEmail != "") {
+ whereClauseEmail += " AND ";
+ }
+ Log.d(TAG, "##Filter Read Status Value Appended to Query##:");
+ whereClauseEmail += " flagRead = 1 ";
+ }
+ }
+
+ Log.d(TAG, "##Filter Recipient Value##:"+appParams.FilterRecipient);
+ Log.d(TAG, "##Filter Recipient Condition 1 ##:"+(appParams.FilterRecipient != null));
+ Log.d(TAG, "##Filter Recipient Condition 2 ##:"+(appParams.FilterRecipient != ""));
+ //Filter Recipient
+ if ((appParams.FilterRecipient != null) && (appParams.FilterRecipient != "") && (appParams.FilterRecipient.length() > 0)) {
+ if (whereClauseEmail != "") {
+ whereClauseEmail += " AND ";
+ }
+ whereClauseEmail += " toList LIKE '%"+appParams.FilterRecipient.trim()+"%'";
+ }
+
+ // TODO Filter Originator
+
+ if ((appParams.FilterOriginator != null)
+ && (appParams.FilterOriginator.length() != 0)) {
+
+ // For incoming message
+ if (whereClauseEmail != "") {
+ whereClauseEmail += " AND ";
+ }
+ String str1 = appParams.FilterOriginator;
+ whereClauseEmail += "fromList LIKE '%"+appParams.FilterOriginator.trim()+"%'";
+ }
+ Log.d(TAG, "##11 whereClauseEmail 11##:" + whereClauseEmail);
+ // TODO Filter priority?
+
+ /* Filter Period Begin */
+ if ((appParams.FilterPeriodBegin != null)
+ && (appParams.FilterPeriodBegin != "")) {
+ Time time = new Time();
+
+ try {
+ time.parse(appParams.FilterPeriodBegin);
+ if (whereClauseEmail != "") {
+ whereClauseEmail += " AND ";
+ }
+ whereClauseEmail += "timeStamp > " + time.toMillis(false);
+
+ } catch (TimeFormatException e) {
+ Log.d(TAG, "Bad formatted FilterPeriodBegin, Ignore"
+ + appParams.FilterPeriodBegin);
+ }
+ }
+
+ /* Filter Period End */
+ if ((appParams.FilterPeriodEnd != null)
+ && (appParams.FilterPeriodEnd != "")) {
+ Time time = new Time();
+ try {
+ time.parse(appParams.FilterPeriodEnd);
+ if (whereClauseEmail != "") {
+ whereClauseEmail += " AND ";
+ }
+ whereClauseEmail += "timeStamp < " + time.toMillis(false);
+ } catch (TimeFormatException e) {
+ Log.d(TAG, "Bad formatted FilterPeriodEnd, Ignore"
+ + appParams.FilterPeriodEnd);
+ }
+ }
+
+ return whereClauseEmail;
+ }
+
+ public MsgListingConsts bldEmailMsgLstItem(Context context, String folderName,
+ BluetoothMasAppParams appParams, String subject, String timestamp,
+ String senderName, String senderAddressing, String recipientName,
+ String recipientAddressing, String msgId, String readStatus, String replyToStr) {
+
+ MsgListingConsts emailMsg = new MsgListingConsts();
+ emailMsg.setMsg_handle(Integer.valueOf(msgId)+ EMAIL_HDLR_CONSTANT);
+
+ Time time = new Time();
+ time.set(Long.valueOf(timestamp));
+
+ String datetimeStr = time.toString().substring(0, 15);
+
+ emailMsg.msgInfo.setDateTime(datetimeStr);
+
+ if ((appParams.ParameterMask & BIT_SUBJECT) != 0) {
+
+ Log.d(TAG, "Fileter Subject Length ::"+appParams.SubjectLength);
+ if ((subject != null && appParams.SubjectLength > 0)
+ && (subject.length() > appParams.SubjectLength)) {
+ subject = subject.substring(0,
+ appParams.SubjectLength);
+ }
+ emailMsg.setSubject(subject.trim());
+ emailMsg.sendSubject = true;
+ }
+
+ if ((appParams.ParameterMask & BIT_DATETIME) != 0) {
+ // TODO Clarify if OFFSET is needed.
+ emailMsg.setDatetime(datetimeStr);
+ }
+
+ if ((appParams.ParameterMask & BIT_SENDER_NAME) != 0) {
+ if(senderName.contains("")){
+ String[] senderStr = senderName.split("");
+ if(senderStr !=null && senderStr.length > 0){
+ Log.d(TAG, " ::Sender name split String 0:: " + senderStr[0]
+ + "::Sender name split String 1:: " + senderStr[1]);
+ emailMsg.setSender_name(senderStr[1].trim());
+ }
+ }
+ else{
+ emailMsg.setSender_name(senderName.trim());
+ }
+ }
+
+ if ((appParams.ParameterMask & BIT_SENDER_ADDRESSING) != 0) {
+ if(senderAddressing.contains("")){
+ String[] senderAddrStr = senderAddressing.split("");
+ if(senderAddrStr !=null && senderAddrStr.length > 0){
+ Log.d(TAG, " ::Sender Addressing split String 0:: " + senderAddrStr[0]
+ + "::Sender Addressing split String 1:: " + senderAddrStr[1]);
+ emailMsg.setSender_addressing(senderAddrStr[0].trim());
+ }
+ }
+ else{
+ emailMsg.setSender_addressing(senderAddressing.trim());
+ }
+
+ }
+
+ if ((appParams.ParameterMask & BIT_RECIPIENT_NAME) != 0) {
+ String multiRecepients = null;
+
+ if(recipientName.contains("")){
+ String[] recepientStr = recipientName.split("");
+ if(recepientStr !=null && recepientStr.length > 0){
+ Log.d(TAG, " ::Recepient name split String 0:: " + recepientStr[0]
+ + "::Recepient name split String 1:: " + recepientStr[1]);
+ emailMsg.setRecepient_name(recepientStr[1].trim());
+ }
+ }
+ else if(recipientName.contains("")){
+ multiRecepients = recipientName.replace('', ';');
+ Log.d(TAG, " ::Recepient name :: " + multiRecepients);
+ emailMsg.setRecepient_name(multiRecepients.trim());
+ }
+ else{
+ emailMsg.setRecepient_name(recipientName.trim());
+ }
+ }
+
+ if ((appParams.ParameterMask & BIT_RECIPIENT_ADDRESSING) != 0) {
+ String multiRecepientAddrs = null;
+
+ if(recipientAddressing.contains("")){
+ String[] recepientAddrStr = recipientAddressing.split("");
+ if(recepientAddrStr !=null && recepientAddrStr.length > 0){
+ Log.d(TAG, " ::Recepient addressing split String 0:: " + recepientAddrStr[0]
+ + "::Recepient addressing split String 1:: " + recepientAddrStr[1]);
+ emailMsg.setRecepient_addressing(recepientAddrStr[0].trim());
+ }
+ }
+ else if(recipientAddressing.contains("")){
+ multiRecepientAddrs = recipientAddressing.replace('', ';');
+ Log.d(TAG, " ::Recepient Address:: " + multiRecepientAddrs);
+ emailMsg.setRecepient_addressing(multiRecepientAddrs.trim());
+ }
+ else{
+ emailMsg.setRecepient_addressing(recipientAddressing.trim());
+ }
+ }
+
+ if ((appParams.ParameterMask & BIT_TYPE) != 0) {
+ emailMsg.setType("EMAIL");
+ }
+
+ if ((appParams.ParameterMask & BIT_SIZE) != 0) {
+ int msgSize = 0;
+ msgSize = getMessageSizeEmail(Integer.parseInt(msgId), context);
+ emailMsg.setSize(msgSize);
+ }
+
+ if ((appParams.ParameterMask & BIT_RECEPTION_STATUS) != 0) {
+ emailMsg.setReception_status("complete");
+ }
+
+ if ((appParams.ParameterMask & BIT_TEXT) != 0) {
+ // TODO Set text to "yes"
+ emailMsg.setContains_text("yes");
+ }
+
+ if ((appParams.ParameterMask & BIT_ATTACHMENT_SIZE) != 0) {
+ emailMsg.setAttachment_size(0);
+ }
+
+ if ((appParams.ParameterMask & BIT_PRIORITY) != 0) {
+ // TODO Get correct priority
+ emailMsg.setPriority("no");
+ }
+
+ if ((appParams.ParameterMask & BIT_READ) != 0) {
+ Log.d(TAG, " ::Read Status:: " + readStatus);
+ if (readStatus.equalsIgnoreCase("1")) {
+ emailMsg.setRead("yes");
+ } else {
+ emailMsg.setRead("no");
+ }
+ }
+
+ if ((appParams.ParameterMask & BIT_SENT) != 0) {
+ // TODO Get sent status?
+ if (folderName.equalsIgnoreCase("sent") || folderName.toUpperCase().contains("SENT")) {
+ emailMsg.setSent("yes");
+ } else {
+ emailMsg.setSent("no");
+ }
+ }
+
+ if ((appParams.ParameterMask & BIT_PROTECTED) != 0) {
+ emailMsg.setMsg_protected("no");
+ }
+
+ if ((appParams.ParameterMask & BIT_REPLYTO_ADDRESSING) != 0) {
+ //TODO need to test
+ Log.d(TAG, " ::Reply To addressing:: " + replyToStr);
+ emailMsg.setReplyTo_addressing(replyToStr);
+ }
+
+ return emailMsg;
+ }
+ public String bldEmailBmsg(int msgHandle, BluetoothMasMessageRsp rsp, Context context, MapUtils mu) {
+ Log.d(TAG, "Inside bldEmailBmsg:");
+ String str = null;
+
+ //Query the message table for obtaining the message related details
+ Cursor cr1 = null;
+ int folderId;
+ String timeStamp = null;
+ String subjectText = null;
+ Uri uri1 = Uri.parse("content://com.android.email.provider/message");
+ String whereClause = " _id = " + msgHandle;
+ cr1 = context.getContentResolver().query(uri1, null, whereClause, null,
+ null);
+
+ if (cr1.getCount() > 0) {
+ cr1.moveToFirst();
+ folderId = cr1.getInt(cr1.getColumnIndex("mailboxKey"));
+ String containingFolder = getContainingFolderEmail(folderId, context);
+ timeStamp = cr1.getString(cr1.getColumnIndex("timeStamp"));
+ subjectText = cr1.getString(cr1.getColumnIndex("subject"));
+ BmessageConsts bmsg = new BmessageConsts();
+
+ // Create a bMessage
+
+ // TODO Get Current type
+ bmsg.setType("EMAIL");
+
+ bmsg.setBmsg_version("1.0");
+ if (cr1.getString(cr1.getColumnIndex("flagRead")).equalsIgnoreCase("1")) {
+ bmsg.setStatus("READ");
+ } else {
+ bmsg.setStatus("UNREAD");
+ }
+
+ bmsg.setFolder("TELECOM/MSG/" + containingFolder);
+
+ bmsg.setVcard_version("2.1");
+
+ String senderName = null;
+ senderName = cr1.getString(cr1.getColumnIndex("fromList"));
+ if(senderName.contains("")){
+ String[] senderStr = senderName.split("");
+ if(senderStr !=null && senderStr.length > 0){
+ bmsg.setOriginatorVcard_name(senderStr[1].trim());
+ bmsg.setOriginatorVcard_email(senderStr[0].trim());
+ }
+ }
+ else{
+ bmsg.setOriginatorVcard_name(senderName.trim());
+ bmsg.setOriginatorVcard_email(senderName.trim());
+ }
+
+ String recipientName = null;
+ String multiRecepients = null;
+ recipientName = cr1.getString(cr1.getColumnIndex("toList"));
+ if(recipientName.contains("")){
+ String[] recepientStr = recipientName.split("");
+ if(recepientStr !=null && recepientStr.length > 0){
+ bmsg.setRecipientVcard_name(recepientStr[1].trim());
+ bmsg.setRecipientVcard_email(recepientStr[0].trim());
+ }
+ }
+ else if(recipientName.contains("")){
+ multiRecepients = recipientName.replace('', ';');
+ Log.d(TAG, " ::Recepient name :: " + multiRecepients);
+ bmsg.setRecipientVcard_name(multiRecepients.trim());
+ bmsg.setRecipientVcard_email(multiRecepients.trim());
+ }
+ else{
+ bmsg.setRecipientVcard_name(recipientName.trim());
+ bmsg.setRecipientVcard_email(recipientName.trim());
+ }
+
+
+ // TODO Set either Encoding or Native
+
+ // TODO how to get body for MMS? This is for SMS only
+
+ StringBuilder sb = new StringBuilder();
+ Date date = new Date(Long.parseLong(timeStamp));
+ sb.append("Date: ").append(date.toString()).append("\r\n");
+ sb.append("To:").append(bmsg.getRecipientVcard_email()).append("\r\n");
+ sb.append("From:").append(bmsg.getOriginatorVcard_email()).append("\r\n");
+ sb.append("Subject:").append(subjectText).append("\r\n");
+
+ sb.append("Mime-Version: 1.0").append("\r\n");
+ sb.append(
+ "Content-Type: multipart/mixed; boundary=\"RPI-Messaging.123456789.0\"")
+ .append("\r\n");
+ sb.append("Content-Transfer-Encoding: 7bit").append("\r\n")
+ .append("\r\n");
+ sb.append("MIME Message").append("\r\n");
+ sb.append("--RPI-Messaging.123456789.0").append("\r\n");
+ sb.append("Content-Type: text/plain; charset=\"UTF-8\"").append("\r\n");
+ sb.append("Content-Transfer-Encoding: 8bit").append("\r\n");
+ sb.append("Content-Disposition:inline").append("\r\n")
+ .append("\r\n");
+
+ //Query the body table for obtaining the message body
+ Cursor cr2 = null;
+ String emailBody = null;
+ Uri uri2 = Uri.parse("content://com.android.email.provider/body");
+ String whereStr = " messageKey = " + msgHandle;
+ cr2 = context.getContentResolver().query(uri2, null, whereStr, null,
+ null);
+
+ if (cr2.getCount() > 0) {
+ cr2.moveToFirst();
+ emailBody = cr2.getString(cr2.getColumnIndex("textContent"));
+ }
+ sb.append(emailBody).append("\r\n");
+
+ sb.append("--RPI-Messaging.123456789.0--").append("\r\n");
+ bmsg.setBody_msg(sb.toString());
+ bmsg.setBody_length(sb.length() + 22);
+ Log.d(TAG, "bMessageEmail test 44444444\n");
+ // Send a bMessage
+ Log.d(TAG, "bMessageEmail test\n");
+ Log.d(TAG, "=======================\n\n");
+ str = mu.toBmessageEmail(bmsg);
+ Log.d(TAG, str);
+ Log.d(TAG, "\n\n");
+ }
+
+ return str;
+ }
+
+ /**
+ * Get the folder name (MAP representation) for Email based on the
+ * mailboxKey value in message table
+ */
+ public String getContainingFolderEmail(int folderId, Context context) {
+ Cursor cr;
+ String folderName = null;
+ String whereClause = "_id = " + folderId;
+ cr = context.getContentResolver().query(
+ Uri.parse("content://com.android.email.provider/mailbox"),
+ null, whereClause, null, null);
+ if (cr.getCount() > 0) {
+ cr.moveToFirst();
+ folderName = cr.getString(cr.getColumnIndex("displayName"));
+ return folderName;
+ }
+ return null;
+ }
+
+
+}
diff --git a/src/com/android/bluetooth/map/MapUtils/MapUtils.java b/src/com/android/bluetooth/map/MapUtils/MapUtils.java
new file mode 100644
index 000000000..b98bd6c11
--- /dev/null
+++ b/src/com/android/bluetooth/map/MapUtils/MapUtils.java
@@ -0,0 +1,1623 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package com.android.bluetooth.map.MapUtils;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.List;
+import android.util.Log;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import android.util.Xml;
+
+/**
+ * MapUtils is a class of utility methods that provide routines for converting
+ * data to either XML or bMessage formats. The class shall also support parsing
+ * XML and bMessage formatted data.
+ * <p>
+ * The following methods are currently supported:
+ * <p>
+ * folderListingXML()
+ *
+ * @version 0.1
+ *
+ */
+
+public class MapUtils {
+
+ private final String CRLF = "\r\n";
+
+ /**
+ * folderListingXML
+ *
+ * This method takes a list of folder names and returns a String with the
+ * XML version of the List
+ *
+ * @param list
+ * An array of strings where each element represents a folder
+ * name
+ * @return This method returns either null or a String
+ */
+
+ public String folderListingXML(List<String> list) {
+
+ String str = "<?xml version=\"1.0\"?><!DOCTYPE folder-listing SYSTEM \"obex-folder-listing.dtd\"><folder-listing version=\"1.0\">";
+
+ for (String s : list) {
+ str += "<folder name=\"";
+ str += s;
+ str += "\"/>";
+ }
+
+ str += "</folder-listing>";
+
+ return str;
+ }
+
+ /**
+ * messageListingXML
+ *
+ * This method takes a list of message objects and returns a String with the
+ * XML version of the List
+ *
+ * @param list
+ * An array of message objects where each element represents a
+ * message
+ * @return This method returns either null or a String
+ */
+
+ public String messageListingXML(List<MsgListingConsts> list) {
+ XmlSerializer serializer = Xml.newSerializer();
+ StringWriter writer = new StringWriter();
+ try {
+ String str1;
+ String str2 = "<?xml version=\"1.0\"?>";
+ serializer.setOutput(writer);
+ serializer.startDocument(null, null);
+ serializer.text("\n");
+ serializer.startTag("", "MAP-msg-listing");
+ serializer.attribute("", "version", "1.0");
+ for (MsgListingConsts msg : list) {
+ serializer.startTag("", "");
+
+ serializer.attribute("", "msg handle", ("" + msg.msg_handle));
+ if (msg.sendSubject == true) {
+ if (msg.subject == null){
+ serializer.attribute("", "subject", "");
+ } else {
+ serializer.attribute("", "subject", msg.subject);
+ }
+
+ }
+ if (msg.datetime != null) {
+ serializer.attribute("", "datetime", msg.datetime);
+ }
+ if (msg.sender_name != null) {
+ serializer.attribute("", "sender_name", msg.sender_name);
+ }
+
+ if (msg.sender_addressing != null) {
+ serializer.attribute("", "sender_addressing",
+ msg.sender_addressing);
+ }
+
+ if (msg.replyto_addressing != null) {
+ serializer.attribute("", "replyto_addressing",
+ msg.replyto_addressing);
+ }
+
+ if (msg.recepient_name != null) {
+ serializer.attribute("", "recipient_name",
+ msg.recepient_name);
+ }
+ if (msg.sendRecipient_addressing == true) {
+ if (msg.recepient_addressing != null) {
+ serializer.attribute("", "recipient_addressing",
+ msg.recepient_addressing);
+ } else {
+ serializer.attribute("", "recipient_addressing", "");
+ }
+ }
+ if (msg.type != null) {
+ serializer.attribute("", "type", msg.type);
+ }
+ if (msg.size != 0) {
+ serializer.attribute("", "size", ("" + msg.size));
+ }
+
+ if (msg.contains_text != null) {
+ serializer.attribute("", "text", msg.contains_text);
+ }
+
+ if (msg.reception_status != null) {
+ serializer.attribute("", "reception_status",
+ msg.reception_status);
+ }
+
+ if (msg.attachment_size != -1) {
+ serializer.attribute("", "attachment_size",
+ ("" + Integer.toString(msg.attachment_size)));
+ }
+
+ if (msg.priority != null) {
+ serializer.attribute("", "priority", msg.priority);
+ }
+
+ if (msg.read != null) {
+ serializer.attribute("", "read", msg.read);
+ }
+
+ if (msg.sent != null) {
+ serializer.attribute("", "sent", msg.sent);
+ }
+
+ if (msg.msg_protected != null) {
+ serializer.attribute("", "protected", msg.msg_protected);
+ }
+
+ serializer.endTag("", "");
+
+ }
+ serializer.endTag("", "MAP-msg-listing");
+ serializer.endDocument();
+ str1 = writer.toString();
+
+ str1 = removeMsgHdrSpace(str1);
+
+ int line1 = 0;
+ line1 = str1.indexOf("\n");
+ str2 += str1.substring(line1 + 1);
+ if (list.size() > 0) {
+ int indxHandle = str2.indexOf("msg handle");
+ String str3 = "<" + str2.substring(indxHandle);
+ str2 = str2.substring(0, (indxHandle - 1)) + str3;
+ }
+ return str2;
+
+ } catch (IllegalArgumentException e) {
+
+ e.printStackTrace();
+ } catch (IllegalStateException e) {
+
+ e.printStackTrace();
+ } catch (IOException e) {
+
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * msgListingGetHdrXML
+ *
+ * This method returns a String with the XML header
+ *
+ * @return This method returns a String
+ */
+
+ public String msgListingGetHdrXML() {
+ String str1 = "<MAP-msg-listing version = \"1.0\">\n";
+ return str1;
+ }
+
+ /**
+ * msgListingGetFooterXML
+ *
+ * This method returns a String with the XML footer
+ *
+ * @return This method returns a String
+ */
+
+ public String msgListingGetFooterXML() {
+ String str1 = "</MAP-msg-listing>\n";
+ return str1;
+ }
+
+ /**
+ * msgListingGetMsgsXML
+ *
+ * This method takes a list of message objects and returns a String with the
+ * XML messages
+ *
+ * @param list
+ * An array of message objects where each element represents a
+ * message
+ * @return This method returns either null or a String
+ */
+
+ public String msgListingGetMsgsXML(List<MsgListingConsts> list) {
+ XmlSerializer serializer = Xml.newSerializer();
+ StringWriter writer = new StringWriter();
+ try {
+ String str1;
+ serializer.setOutput(writer);
+ serializer.startDocument("", false);
+ serializer.text("\n");
+ for (MsgListingConsts msg : list) {
+ serializer.startTag("", "");
+ serializer.attribute("", "msg handle", ("" + msg.msg_handle));
+ if (msg.subject != null) {
+ serializer.attribute("", "subject", msg.subject);
+ } else {
+
+ }
+ if (msg.datetime != null) {
+ serializer.attribute("", "datetime", msg.datetime);
+ } else {
+
+ }
+ if (msg.sender_name != null) {
+ serializer.attribute("", "sender_name", msg.sender_name);
+ } else {
+
+ }
+
+ if (msg.sender_addressing != null) {
+ serializer.attribute("", "sender_addressing",
+ msg.sender_addressing);
+ } else {
+
+ }
+ if (msg.recepient_name != null) {
+ serializer.attribute("", "recipient_name",
+ msg.recepient_name);
+ } else {
+
+ }
+ if (msg.recepient_addressing != null) {
+ serializer.attribute("", "recipient_addressing",
+ msg.recepient_addressing);
+ } else {
+
+ }
+ if (msg.type != null) {
+ serializer.attribute("", "type", msg.type);
+ } else {
+
+ }
+ if (msg.size != 0) {
+ serializer.attribute("", "size", ("" + msg.size));
+ } else {
+
+ }
+ if (msg.attachment_size != -1) {
+ serializer.attribute("", "attachment_size",
+ ("" + Integer.toString(msg.attachment_size)));
+ } else {
+
+ }
+ if (msg.contains_text != null) {
+ serializer.attribute("", "text", msg.contains_text);
+ } else {
+
+ }
+ if (msg.priority != null) {
+ serializer.attribute("", "priority", msg.priority);
+ } else {
+
+ }
+ if (msg.read != null) {
+ serializer.attribute("", "read", msg.read);
+ } else {
+
+ }
+ if (msg.sent != null) {
+ serializer.attribute("", "sent", msg.sent);
+ } else {
+
+ }
+
+ if (msg.replyto_addressing != null) {
+ serializer.attribute("", "replyto_addressing",
+ msg.replyto_addressing);
+ } else {
+
+ }
+
+ serializer.endTag("", "");
+
+ }
+ serializer.endDocument();
+ str1 = writer.toString();
+
+ str1 = removeMsgHdrSpace(str1);
+ int line1 = 0;
+ line1 = str1.indexOf("\n");
+ if (line1 > 0) {
+ return (str1.substring((line1)));
+ } else {
+ return str1;
+ }
+
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * mapEventReportXML
+ *
+ * This method takes a list of map event report object and returns a String
+ * with the XML message
+ *
+ * @param type
+ * Report type (e.g. NewMessage)
+ * @param handle
+ * handle created by caller
+ * @param folder
+ * Path to folder to use
+ * @param Oldfolder
+ * Path to old folder to use
+ * @param msgType
+ * Type of message (SMS_GSM, SMS_CDMA)
+ *
+ * @return This method returns either null or a String
+ */
+
+ public String mapEventReportXML(String type, String handle, String folder,
+ String oldFolder, String msgType) {
+ XmlSerializer serializer = Xml.newSerializer();
+ StringWriter writer = new StringWriter();
+
+ try {
+ String str1;
+ serializer.setOutput(writer);
+ serializer.startDocument("", false);
+ serializer.text("\n");
+ serializer.startTag("", "MAP-event-report");
+ serializer.attribute("", "version", "1.0");
+ serializer.text("\n");
+
+ serializer.startTag("", "");
+ if (type != null) {
+ serializer.attribute("", "event type", ("" + type));
+ } else {
+
+ }
+ if (handle != null) {
+ serializer.attribute("", "handle", ("" + handle));
+ } else {
+
+ }
+ if (folder != null) {
+ serializer.attribute("", "folder", ("" + folder));
+ } else {
+
+ }
+ if (oldFolder != null) {
+ serializer.attribute("", "old_folder", ("" + oldFolder));
+ } else {
+
+ }
+
+ if (msgType != null) {
+ serializer.attribute("", "msg_type", ("" + msgType));
+ } else {
+
+ }
+ serializer.endTag("", "");
+ serializer.text("\n");
+ serializer.endTag("", "MAP-event-report");
+ serializer.endDocument();
+ str1 = writer.toString();
+ int line1 = 0;
+ line1 = str1.indexOf("\n");
+ if (line1 > 0) {
+ int index = str1.indexOf("event type");
+ String str2 = "<" + str1.substring(index);
+ str1 = "<MAP-event-report version=\"1.0\">" + "\n" + str2;
+ return str1;
+ } else {
+ return str1;
+ }
+
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ /**
+ * toBmessageSMS
+ *
+ * This method takes as input a list of BmessageConsts objects and creates a
+ * String in the bMessage format.
+ *
+ * @param list
+ * An array of message objects where each element represents a
+ * message
+ * @return This method returns either null or a String
+ */
+
+ public String toBmessageSMS(BmessageConsts bmsg) {
+
+ StringBuilder sb = new StringBuilder();
+
+ try {
+ sb.append("BEGIN:BMSG");
+ sb.append("\r\n");
+ if (bmsg.bmsg_version != null) {
+ sb.append("VERSION:").append(bmsg.bmsg_version).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.status != null) {
+ sb.append("STATUS:").append(bmsg.status).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.type != null) {
+ sb.append("TYPE:").append(bmsg.type).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.folder != null) {
+ sb.append("FOLDER:").append(bmsg.folder).append("\r\n");
+ } else {
+
+ }
+
+ // Originator
+ sb.append("BEGIN:VCARD").append("\r\n");
+
+ if (bmsg.vcard_version != null) {
+ sb.append("VERSION:").append(bmsg.vcard_version).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.originator_vcard_name != null) {
+ sb.append("N:").append(bmsg.originator_vcard_name)
+ .append("\r\n");
+ } else {
+
+ }
+ if (bmsg.originator_vcard_phone_number != null) {
+ sb.append("TEL:").append(bmsg.originator_vcard_phone_number)
+ .append("\r\n");
+ } else {
+
+ }
+
+ sb.append("END:VCARD").append("\r\n");
+ // End Originator
+
+ sb.append("BEGIN:BENV").append("\r\n");
+
+ // Recipient
+ sb.append("BEGIN:VCARD").append("\r\n");
+
+ if (bmsg.vcard_version != null) {
+ sb.append("VERSION:").append(bmsg.vcard_version).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.recipient_vcard_name != null) {
+ sb.append("N:").append(bmsg.recipient_vcard_name)
+ .append("\r\n");
+ } else {
+
+ }
+ if (bmsg.recipient_vcard_phone_number != null) {
+ sb.append("TEL:").append(bmsg.recipient_vcard_phone_number)
+ .append("\r\n");
+ } else {
+
+ }
+ sb.append("END:VCARD").append("\r\n");
+ // End Recipient
+
+ sb.append("BEGIN:BBODY").append("\r\n");
+
+ if (bmsg.body_charset != null) {
+ sb.append("CHARSET:").append(bmsg.body_charset)
+ .append("\r\n");
+ } else {
+
+ }
+
+ if (bmsg.body_encoding != null) {
+ sb.append("ENCODING:").append(bmsg.body_encoding)
+ .append("\r\n");
+ } else {
+
+ }
+ if (bmsg.body_length != 0) {
+ sb.append("LENGTH:").append(bmsg.body_length).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.body_msg != null) {
+ sb.append("BEGIN:MSG\r\n");
+ sb.append(bmsg.body_msg).append("\r\n");
+ sb.append("END:MSG\r\n");
+
+ } else {
+
+ }
+
+ sb.append("END:BBODY").append("\r\n");
+ sb.append("END:BENV").append("\r\n");
+
+ sb.append("END:BMSG");
+ sb.append("\r\n");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return sb.toString();
+
+ }
+
+ /**
+ * toBmessageMMS
+ *
+ * This method takes as input a list of BmessageConsts objects and creates a
+ * String in the bMessage format.
+ *
+ * @param list
+ * An array of message objects where each element represents a
+ * message
+ * @return This method returns either null or a String
+ */
+
+ public String toBmessageMMS(BmessageConsts bmsg) {
+
+ StringBuilder sb = new StringBuilder();
+
+ try {
+ sb.append("BEGIN:BMSG");
+ sb.append("\r\n");
+ if (bmsg.bmsg_version != null) {
+ sb.append("VERSION:").append(bmsg.bmsg_version).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.status != null) {
+ sb.append("STATUS:").append(bmsg.status).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.type != null) {
+ sb.append("TYPE:").append(bmsg.type).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.folder != null) {
+ sb.append("FOLDER:").append(bmsg.folder).append("\r\n");
+ } else {
+
+ }
+
+ // Originator
+ sb.append("BEGIN:VCARD").append("\r\n");
+
+ if (bmsg.vcard_version != null) {
+ sb.append("VERSION:").append(bmsg.vcard_version).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.originator_vcard_name != null) {
+ sb.append("N:").append(bmsg.originator_vcard_name)
+ .append("\r\n");
+ } else {
+
+ }
+ if (bmsg.originator_vcard_phone_number != null) {
+ sb.append("TEL:").append(bmsg.originator_vcard_phone_number)
+ .append("\r\n");
+ } else {
+
+ }
+
+ sb.append("END:VCARD").append("\r\n");
+ // End Originator
+
+ sb.append("BEGIN:BENV").append("\r\n");
+
+ // Recipient
+ sb.append("BEGIN:VCARD").append("\r\n");
+
+ if (bmsg.vcard_version != null) {
+ sb.append("VERSION:").append(bmsg.vcard_version).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.recipient_vcard_name != null) {
+ sb.append("N:").append(bmsg.recipient_vcard_name)
+ .append("\r\n");
+ } else {
+
+ }
+ if (bmsg.recipient_vcard_phone_number != null) {
+ sb.append("TEL:").append(bmsg.recipient_vcard_phone_number)
+ .append("\r\n");
+ } else {
+
+ }
+ sb.append("END:VCARD").append("\r\n");
+ // End Recipient
+
+ sb.append("BEGIN:BBODY").append("\r\n");
+
+ sb.append("PARTID:26988").append("\r\n");
+
+ if (bmsg.body_encoding != null) {
+ sb.append("ENCODING:").append(bmsg.body_encoding)
+ .append("\r\n");
+ } else {
+
+ }
+ sb.append("CHARSET:UTF-8").append("\r\n");
+ sb.append("LANGUAGE:").append("\r\n");
+
+ if (bmsg.body_length != 0) {
+ sb.append("LENGTH:").append(bmsg.body_length).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.body_msg != null) {
+ sb.append("BEGIN:MSG\r\n");
+ sb.append(bmsg.body_msg).append("\r\n");
+ sb.append("END:MSG\r\n");
+
+ } else {
+
+ }
+
+ sb.append("END:BBODY").append("\r\n");
+ sb.append("END:BENV").append("\r\n");
+
+ sb.append("END:BMSG");
+ sb.append("\r\n");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return sb.toString();
+
+ }
+
+ /**
+ * toBmessageEmail
+ *
+ * This method takes as input a list of BmessageConsts objects and creates a
+ * String in the bMessage format.
+ *
+ * @param list
+ * An array of message objects where each element represents a
+ * message
+ * @return This method returns either null or a String
+ */
+
+ public String toBmessageEmail(BmessageConsts bmsg) {
+
+ StringBuilder sb = new StringBuilder();
+
+ try {
+ sb.append("BEGIN:BMSG");
+ sb.append("\r\n");
+ if (bmsg.bmsg_version != null) {
+ sb.append("VERSION:").append(bmsg.bmsg_version).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.status != null) {
+ sb.append("STATUS:").append(bmsg.status).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.type != null) {
+ sb.append("TYPE:").append(bmsg.type).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.folder != null) {
+ sb.append("FOLDER:").append(bmsg.folder).append("\r\n");
+ } else {
+
+ }
+
+ // Originator
+ sb.append("BEGIN:VCARD").append("\r\n");
+
+ if (bmsg.vcard_version != null) {
+ sb.append("VERSION:").append(bmsg.vcard_version).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.originator_vcard_name != null) {
+ sb.append("N:").append(bmsg.originator_vcard_name)
+ .append("\r\n");
+ } else {
+
+ }
+ sb.append("TEL:").append("\r\n");
+ if (bmsg.originator_vcard_email != null) {
+ sb.append("EMAIL:").append(bmsg.originator_vcard_email)
+ .append("\r\n");
+ } else {
+
+ }
+
+ sb.append("END:VCARD").append("\r\n");
+ // End Originator
+
+ sb.append("BEGIN:BENV").append("\r\n");
+
+ // Recipient
+ sb.append("BEGIN:VCARD").append("\r\n");
+
+ if (bmsg.vcard_version != null) {
+ sb.append("VERSION:").append(bmsg.vcard_version).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.recipient_vcard_name != null) {
+ sb.append("N:").append(bmsg.recipient_vcard_name)
+ .append("\r\n");
+ } else {
+
+ }
+ if (bmsg.recipient_vcard_name != null) {
+ sb.append("FN:").append(bmsg.recipient_vcard_name)
+ .append("\r\n");
+ } else {
+
+ }
+ sb.append("TEL:").append("\r\n");
+ if (bmsg.recipient_vcard_email != null) {
+ sb.append("EMAIL:").append(bmsg.recipient_vcard_email)
+ .append("\r\n");
+ } else {
+
+ }
+ sb.append("END:VCARD").append("\r\n");
+ // End Recipient
+
+ sb.append("BEGIN:BBODY").append("\r\n");
+
+ if (bmsg.body_encoding != null) {
+ sb.append("ENCODING:").append(bmsg.body_encoding)
+ .append("\r\n");
+ } else {
+ sb.append("ENCODING:8BIT").append("\r\n");
+ }
+
+ sb.append("CHARSET:UTF-8").append("\r\n");
+
+ sb.append("LANGUAGE:English").append("\r\n");
+ if (bmsg.body_length != 0) {
+ sb.append("LENGTH:").append(bmsg.body_length).append("\r\n");
+ } else {
+
+ }
+ if (bmsg.body_msg != null) {
+ sb.append("BEGIN:MSG\r\n");
+ sb.append(bmsg.body_msg).append("\r\n");
+ sb.append("END:MSG\r\n");
+
+ } else {
+
+ }
+
+ sb.append("END:BBODY").append("\r\n");
+ sb.append("END:BENV").append("\r\n");
+
+ sb.append("END:BMSG");
+ sb.append("\r\n");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return sb.toString();
+
+ }
+
+
+ /**
+ * fromBmessageSMS
+ *
+ * This method takes as input a String in the bMessage format. It parses the
+ * String and loads a BmessageConsts object that is returned
+ *
+ * @param String
+ * - which is a bMessage formatted SMS message
+ * @return This method returns a BmessageConsts object
+ */
+
+ public BmessageConsts fromBmessageSMS(String bmsg) {
+
+ BmessageConsts bMsgObj = new BmessageConsts();
+
+ // Extract Telephone number of sender
+ String phoneNumber = null;
+ String vCard = null;
+ vCard = fetchRecipientVcard(bmsg);
+ phoneNumber = fetchVcardTel(vCard);
+ bMsgObj.setRecipientVcard_phone_number(phoneNumber);
+
+ // Extract vCard Version
+ bMsgObj.setVcard_version(fetchVcardVersion(vCard));
+
+ // Extract vCard Name
+ bMsgObj.setVcard_version(fetchVcardVersion(vCard));
+
+ // Extract bMessage Version
+ bMsgObj.setBmsg_version(fetchVersion(bmsg));
+
+ // Extract Message Status
+ bMsgObj.setStatus(fetchReadStatus(bmsg));
+
+ // Extract Message Type
+ bMsgObj.setType(fetchType(bmsg));
+
+ // Extract Message Folder
+ bMsgObj.setFolder(fetchFolder(bmsg));
+
+ // Fetch Message Length
+ bMsgObj.setBody_length(fetchBodyLength(bmsg));
+
+ // Extract Message
+ bMsgObj.setBody_msg(fetchBodyMsg(bmsg));
+
+ // Extract Message encoding
+ bMsgObj.setBody_encoding(fetchBodyEncoding(bmsg));
+
+ return bMsgObj;
+
+ }
+
+ /**
+ * fromBmessageMMS
+ *
+ * This method takes as input a String in the bMessage format. It parses the
+ * String and loads a BmessageConsts object that is returned
+ *
+ * @param String
+ * - which is a bMessage formatted SMS message
+ * @return This method returns a BmessageConsts object
+ */
+
+ public BmessageConsts fromBmessageMMS(String bmsg) {
+
+ BmessageConsts bMsgObj = new BmessageConsts();
+
+ // Extract Telephone number of sender
+ String phoneNumber = null;
+ String vCard = null;
+ vCard = fetchRecipientVcard(bmsg);
+
+ if (vCard.indexOf("EMAIL:") > 0) {
+ phoneNumber = fetchVcardEmailforMms(vCard);
+ } else {
+ phoneNumber = fetchVcardTel(vCard);
+ }
+
+ bMsgObj.setRecipientVcard_phone_number(phoneNumber);
+
+ // Extract vCard Version
+ bMsgObj.setVcard_version(fetchVcardVersion(vCard));
+
+ // Extract vCard Name
+ bMsgObj.setVcard_version(fetchVcardVersion(vCard));
+
+ // Extract bMessage Version
+ bMsgObj.setBmsg_version(fetchVersion(bmsg));
+
+ // Extract Message Status
+ bMsgObj.setStatus(fetchReadStatus(bmsg));
+
+ // Extract Message Type
+ bMsgObj.setType(fetchType(bmsg));
+
+ // Extract Message Folder
+ bMsgObj.setFolder(fetchFolder(bmsg));
+
+ // Fetch Message Length
+ bMsgObj.setBody_length(fetchBodyLength(bmsg));
+
+ // Extract Message
+ bMsgObj.setBody_msg(fetchBodyMsg(bmsg));
+
+ // Extract Message encoding
+ bMsgObj.setBody_encoding(fetchBodyEncoding(bmsg));
+
+ return bMsgObj;
+
+ }
+
+ /**
+ * fromBmessageEmail
+ *
+ * This method takes as input a String in the bMessage format. It parses the
+ * String and loads a BmessageConsts object that is returned
+ *
+ * @param String
+ * - which is a bMessage formatted Email message
+ * @return This method returns a BmessageConsts object
+ */
+
+ public BmessageConsts fromBmessageEmail(String bmsg) {
+
+ BmessageConsts bMsgObj = new BmessageConsts();
+ Log.d("MapUtils", "Inside fromBmessageEmail method::");
+ // Extract Telephone number of sender
+ String email = null;
+ String vCard = null;
+ vCard = fetchRecepientVcardEmail(bmsg);
+ Log.d("MapUtils", "vCard Info:: "+vCard);
+ email = fetchVcardEmail(vCard);
+
+ Log.d("MapUtils", "email Info:: "+email);
+ bMsgObj.setRecipientVcard_email(email);
+
+ String vcardOrig = fetchOriginatorVcardEmail(bmsg);
+ String emailOrig = fetchVcardEmail(vcardOrig);
+ Log.d("MapUtils", "Vcard Originator Email:: "+emailOrig);
+ bMsgObj.setOriginatorVcard_email(emailOrig);
+
+ Log.d("MapUtils", "Vcard Originatore Name:: "+fetchVcardName(vcardOrig));
+ String nameOrig = fetchVcardName(vcardOrig);
+ bMsgObj.setOriginatorVcard_name(nameOrig);
+
+ Log.d("MapUtils", "Vcard version:: "+fetchVcardVersion(vCard));
+ // Extract vCard Version
+ bMsgObj.setVcard_version(fetchVcardVersion(vCard));
+
+ // Extract vCard Name
+
+ Log.d("MapUtils", "Bmsg version:: "+fetchVersion(bmsg));
+ // Extract bMessage Version
+ bMsgObj.setBmsg_version(fetchVersion(bmsg));
+
+ Log.d("MapUtils", "Read status:: "+fetchReadStatus(bmsg));
+ // Extract Message Status
+ bMsgObj.setStatus(fetchReadStatus(bmsg));
+
+ Log.d("MapUtils", "Message Type:: "+fetchType(bmsg));
+ // Extract Message Type
+ bMsgObj.setType(fetchType(bmsg));
+
+ Log.d("MapUtils", "Folder:: "+fetchFolder(bmsg));
+ // Extract Message Folder
+ bMsgObj.setFolder(fetchFolder(bmsg));
+
+ Log.d("MapUtils", "body length:: "+fetchBodyLength(bmsg));
+ // Fetch Message Length
+ bMsgObj.setBody_length(fetchBodyLength(bmsg));
+
+ Log.d("MapUtils", "Message body:: "+fetchBodyMsgEmail(bmsg));
+ // Extract Message
+ bMsgObj.setBody_msg(fetchBodyMsgEmail(bmsg));
+
+ Log.d("MapUtils", "Message encoding:: "+fetchBodyEncoding(bmsg));
+ // Extract Message encoding
+ bMsgObj.setBody_encoding(fetchBodyEncoding(bmsg));
+
+ // Extract Subject of the email
+ bMsgObj.setSubject(fetchSubjectEmail(bmsg));
+
+ return bMsgObj;
+
+ }
+ /**
+ * fetchVcardEmail
+ *
+ * This method takes as input a vCard formatted String. It parses the String
+ * and returns the vCard Email as a String
+ *
+ * @param
+ * @return String This method returns a vCard Email String
+ */
+
+ private String fetchVcardEmail(String vCard) {
+
+ int pos = vCard.indexOf(("EMAIL:"));
+ int beginVersionPos = pos + (("EMAIL:").length());
+ Log.d("Map Utils","Begin Version Position Email:: "+beginVersionPos);
+ int endVersionPos = vCard.indexOf("\n", beginVersionPos);
+ Log.d("Map Utils","End version Pos Email:: "+endVersionPos);
+ return vCard.substring(beginVersionPos, endVersionPos);
+ }
+ private String fetchRecepientVcardEmail(String bmsg) {
+
+ // Find the position of the first vCard in the string
+ int pos = bmsg.indexOf("BEGIN:BENV");
+ Log.d("fetchOriginatorVcard", "vCard start position:: "+pos);
+ if (pos > 0) {
+ Log.d("fetchOriginatorVcard", "vCard start position greater than 0::");
+ int beginVcardPos = pos + ("\r\n".length());
+ int endVcardPos = bmsg.indexOf("END:BENV");
+
+ return bmsg.substring(beginVcardPos, endVcardPos);
+
+ } else {
+
+ return null;
+
+ }
+ }
+
+ private String fetchOriginatorVcardEmail(String bmsg) {
+
+ // Find the position of the first vCard in the string
+ int pos = bmsg.indexOf("BEGIN:VCARD");
+ Log.d("fetchOriginatorVcard", "vCard start position:: "+pos);
+ if (pos > 0) {
+ Log.d("fetchOriginatorVcard", "vCard start position greater than 0::");
+ int beginVcardPos = pos + ("\r\n".length());
+ int endVcardPos = bmsg.indexOf("END:VCARD");
+
+ return bmsg.substring(beginVcardPos, endVcardPos);
+
+ } else {
+
+ return null;
+
+ }
+ }
+ private String fetchSubjectEmail(String body) {
+
+ int pos = body.indexOf("Subject:");
+
+ if (pos > 0) {
+ int beginVersionPos = pos + (("Subject:").length());
+ int endVersionPos = body.indexOf("\n", beginVersionPos);
+ return body.substring(beginVersionPos, endVersionPos);
+
+ } else {
+
+ return null;
+ }
+ }
+
+
+ /**
+ * fetchVersion
+ *
+ * This method takes as input a String in the bMessage format. It parses the
+ * String and returns the bMessage version string
+ *
+ * @param
+ * @return String This method returns a Version String
+ */
+
+ private String fetchVersion(String bmsg) {
+ int pos = bmsg.indexOf("VERSION:");
+ if (pos > 0) {
+
+ int beginVersionPos = pos + (("VERSION:").length());
+ int endVersionPos = bmsg.indexOf(CRLF, beginVersionPos);
+ return bmsg.substring(beginVersionPos, endVersionPos);
+
+ } else {
+
+ return null;
+
+ }
+ }
+
+ /**
+ * fetchOriginatorVcard
+ *
+ * This method takes as input a String in the bMessage format. It parses the
+ * String and returns the orginator vCard string
+ *
+ * @param
+ * @return String This method returns a vCard String
+ */
+
+ private String fetchOriginatorVcard(String bmsg) {
+
+ // Find the position of the first vCard in the string
+ int pos = bmsg.indexOf("\r\nBEGIN:VCARD");
+ if (pos > 0) {
+ int beginVcardPos = pos + ("\r\n".length());
+ int endVcardPos = bmsg.indexOf("END:VCARD");
+
+ return bmsg.substring(beginVcardPos, endVcardPos);
+
+ } else {
+
+ return null;
+
+ }
+ }
+
+ /**
+ * fetchRecipientVcard
+ *
+ * This method takes as input a String in the bMessage format. It parses the
+ * String looking for the first envelope. Once it finds the envelop, it then
+ * looks for the first vCard and returns it as a String
+ *
+ * @param
+ * @return String This method returns a Vcard String
+ */
+
+ @SuppressWarnings("unused")
+ private String fetchRecipientVcard(String bmsg) {
+
+ // Locate BENV
+ int locBENV = 0;
+ int pos = 0;
+ locBENV = bmsg.indexOf(CRLF + "BEGIN:BENV");
+ pos = bmsg.indexOf(CRLF + "BEGIN:VCARD", locBENV);
+ if (locBENV < pos) {
+ pos = bmsg.indexOf(CRLF + "BEGIN:VCARD", locBENV);
+ } else {
+ pos = bmsg.indexOf(CRLF + "BEGIN:VCARD");
+ }
+ if (pos > 0) {
+ int beginVcardPos = pos;
+ int endVcardPos = bmsg.indexOf("END:VCARD", pos);
+ return bmsg.substring(beginVcardPos, endVcardPos);
+
+ } else {
+ return null;
+ }
+
+ }
+
+ /**
+ * fetchReadStatus
+ *
+ * This method takes as input a String in the bMessage format. It parses the
+ * String and returns the bMessage read status String
+ *
+ * @param
+ * @return String This method returns a Read Status String
+ */
+
+ private String fetchReadStatus(String bmsg) {
+ int pos = bmsg.indexOf("STATUS:");
+ if (pos > 0) {
+
+ int beginStatusPos = pos + (("STATUS:").length());
+ int endStatusPos = bmsg.indexOf(CRLF, beginStatusPos);
+ return bmsg.substring(beginStatusPos, endStatusPos);
+
+ } else {
+
+ return null;
+
+ }
+ }
+
+ /**
+ * fetchType
+ *
+ * This method takes as input a String in the bMessage format. It parses the
+ * String and returns the bMessage type String
+ *
+ * @param
+ * @return String This method returns a message type String
+ */
+
+ public String fetchType(String bmsg) {
+ int pos = bmsg.indexOf("TYPE:");
+ if (pos > 0) {
+ int beginTypePos = pos + (("TYPE:").length());
+ int endTypePos = bmsg.indexOf(CRLF, beginTypePos);
+
+ return bmsg.substring(beginTypePos, endTypePos);
+
+ } else {
+ return null;
+ }
+
+ }
+
+ /**
+ * fetchFolder
+ *
+ * This method takes as input a String in the bMessage format. It parses the
+ * String and returns the bMessage Folder path String
+ *
+ * @param
+ * @return String This method returns a Folder path String
+ */
+
+ private String fetchFolder(String bmsg) {
+ int pos = bmsg.indexOf("FOLDER:");
+ if (pos > 0) {
+ int beginVersionPos = pos + (("FOLDER:").length());
+ int endVersionPos = bmsg.indexOf(CRLF, beginVersionPos);
+
+ return bmsg.substring(beginVersionPos, endVersionPos);
+
+ } else {
+ return null;
+ }
+
+ }
+
+ /**
+ * fetchBody
+ *
+ * This method takes as input a String in the bMessage format. It parses the
+ * String and returns the bMessage Body as a String
+ *
+ * @param
+ * @return String This method returns a Body String
+ */
+
+ @SuppressWarnings("unused")
+ private String fetchBody(String bmsg) {
+ int pos = bmsg.indexOf("BEGIN:BBODY");
+ if (pos > 0) {
+ int beginVersionPos = pos + (("BEGIN:BBODY").length());
+ int endVersionPos = bmsg.indexOf("END:BBODY", beginVersionPos);
+
+ return bmsg.substring(beginVersionPos, endVersionPos);
+
+ } else {
+ return null;
+ }
+
+ }
+
+ /**
+ * fetchBodyPartID
+ *
+ * This method takes as input a String consisting of the body portion of the
+ * bMessage. It parses the String and returns the bMessage Body Part ID as a
+ * String
+ *
+ * @param
+ * @return String This method returns a Body Part ID String
+ */
+
+ @SuppressWarnings("unused")
+ private String fetchBodyPartID(String body) {
+ int pos = body.indexOf("PARTID:");
+ if (pos > 0) {
+ int beginVersionPos = pos + (("PARTID:").length());
+ int endVersionPos = body.indexOf(CRLF, beginVersionPos);
+ return body.substring(beginVersionPos, endVersionPos);
+
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * fetchCharset
+ *
+ * This method takes as input a String consisting of the body portion of the
+ * bMessage. It parses the String and returns the bMessage Charset as a
+ * String
+ *
+ * @param
+ * @return String This method returns a Charset String
+ */
+
+ @SuppressWarnings("unused")
+ private String fetchCharset(String body) {
+
+ int pos = body.indexOf("CHARSET:");
+ if (pos > 0) {
+
+ int beginVersionPos = pos + (("CHARSET:").length());
+ int endVersionPos = body.indexOf(CRLF, beginVersionPos);
+
+ return body.substring(beginVersionPos, endVersionPos);
+
+ } else {
+
+ return null;
+ }
+
+ }
+
+ /**
+ * fetchBodyEncoding
+ *
+ * This method takes as input a String consisting of the body portion of the
+ * bMessage. It parses the String and returns the bMessage Body Encoding as
+ * a String
+ *
+ * @param
+ * @return String This method returns a Body Encoding String
+ */
+
+ private String fetchBodyEncoding(String body) {
+ int pos = body.indexOf("ENCODING:");
+ if (pos > 0) {
+ int beginVersionPos = pos + (("ENCODING:").length());
+ int endVersionPos = body.indexOf(CRLF, beginVersionPos);
+ return body.substring(beginVersionPos, endVersionPos);
+
+ } else {
+
+ return null;
+ }
+ }
+
+ /**
+ * fetchBodyLanguage
+ *
+ * This method takes as input a String consisting of the body portion of the
+ * bMessage. It parses the String and returns the bMessage Body Language as
+ * a String
+ *
+ * @param
+ * @return String This method returns a Body Language String
+ */
+
+ @SuppressWarnings("unused")
+ private String fetchBodyLanguage(String body) {
+ int pos = body.indexOf("LANGUAGE:");
+ if (pos > 0) {
+ int beginVersionPos = pos + (("LANGUAGE:").length());
+ int endVersionPos = body.indexOf(CRLF, beginVersionPos);
+ return body.substring(beginVersionPos, endVersionPos);
+
+ } else {
+
+ return null;
+ }
+
+ }
+
+ /**
+ * fetchBodyLength
+ *
+ * This method takes as input a String consisting of the body portion of the
+ * bMessage. It parses the String and returns the bMessage Body Length as an
+ * Integer
+ *
+ * @param
+ * @return String This method returns a Body Length Integer
+ */
+
+ private Integer fetchBodyLength(String body) {
+
+ int pos = body.indexOf("LENGTH:");
+ if (pos > 0) {
+ int beginVersionPos = pos + (("LENGTH:").length());
+ int endVersionPos = body.indexOf(CRLF, beginVersionPos);
+ String bd = body.substring(beginVersionPos,
+ endVersionPos);
+ Integer bl = Integer.valueOf(bd);
+ return bl;
+
+ } else {
+
+ return null;
+ }
+
+ }
+
+ /**
+ * fetchBodyMsg
+ *
+ * This method takes as input a String consisting of the body portion of the
+ * bMessage. It parses the String and returns the bMessage Body Message as a
+ * String
+ *
+ * @param
+ * @return String This method returns a Body Message String
+ */
+
+ private String fetchBodyMsg(String body) {
+ int pos = body.indexOf("BEGIN:MSG");
+ if (pos > 0) {
+ int beginVersionPos = pos
+ + (("BEGIN:MSG").length() + CRLF.length());
+ int endVersionPos = (body.indexOf("END:MSG", beginVersionPos) - CRLF
+ .length());
+
+ return body.substring(beginVersionPos, endVersionPos);
+
+ } else {
+
+ return null;
+ }
+ }
+
+ private String fetchBodyMsgEmail(String body) {
+ Log.d("MapUtils", "bMessageEmail inside fetch body ::"+body);
+ int pos = body.indexOf("Content-Disposition:inline");
+ if (pos > 0) {
+ int beginVersionPos = pos
+ + (("Content-Disposition:inline").length() + CRLF.length());
+ int endVersionPos = (body.indexOf("--RPI-Messaging", beginVersionPos) - CRLF
+ .length());
+
+ return body.substring(beginVersionPos, endVersionPos);
+
+ } else {
+
+ return null;
+ }
+ }
+
+ /**
+ * fetchNumEnv
+ *
+ * This method takes as input a String in the bMessage format. It parses the
+ * String and returns the number of envelope headers that it finds as an
+ * Integer
+ *
+ * @param
+ * @return String This method returns the number of Envelope headers as an
+ * Integer
+ */
+
+ @SuppressWarnings("unused")
+ private Integer fetchNumEnv(String bmsg) {
+ int envCnt = 0;
+ int pos = 0;
+ int loopCnt = 0;
+ pos = bmsg.indexOf((CRLF + "BEGIN:BENV"), pos);
+ if (pos < 0) {
+ loopCnt = 4;
+ } else {
+ envCnt = envCnt + 1;
+ }
+ while (loopCnt < 4) {
+ pos = bmsg.indexOf((CRLF + "BEGIN:BENV"), pos + CRLF.length());
+ if (pos < 0) {
+ loopCnt = 4;
+ } else {
+ envCnt = envCnt + 1;
+ }
+ }
+
+ return envCnt;
+ }
+
+ /**
+ * fetchVcardVersion
+ *
+ * This method takes as input a vCard formatted String. It parses the String
+ * and returns the vCard version as a String
+ *
+ * @param
+ * @return String This method returns a vCard version String
+ */
+
+ private String fetchVcardVersion(String vCard) {
+
+ int pos = vCard.indexOf(CRLF + "VERSION:");
+ int beginVersionPos = pos + (("VERSION:").length() + CRLF.length());
+ int endVersionPos = vCard.indexOf(CRLF, beginVersionPos);
+
+ return vCard.substring(beginVersionPos, endVersionPos);
+ }
+
+ /**
+ * fetchVcardName
+ *
+ * This method takes as input a vCard formatted String. It parses the String
+ * and returns the vCard name as a String
+ *
+ * @param
+ * @return String This method returns a vCard name String
+ */
+
+ @SuppressWarnings("unused")
+ private String fetchVcardName(String vCard) {
+
+ int pos = vCard.indexOf((CRLF + "N:"));
+ int beginNPos = pos + "N:".length() + CRLF.length();
+ int endNPos = vCard.indexOf(CRLF, beginNPos);
+ return vCard.substring(beginNPos, endNPos);
+ }
+
+ /**
+ * fetchVcardTel
+ *
+ * This method takes as input a vCard formatted String. It parses the String
+ * and returns the vCard phone number as a String
+ *
+ * @param
+ * @return String This method returns a vCard phone number String
+ */
+
+ private String fetchVcardTel(String vCard) {
+
+ int pos = vCard.indexOf((CRLF + "TEL:"));
+ int beginVersionPos = pos + (("TEL:").length() + CRLF.length());
+ int endVersionPos = vCard.indexOf(CRLF, beginVersionPos);
+ return vCard.substring(beginVersionPos, endVersionPos);
+ }
+
+ /**
+ * removeMsgHdrSpace
+ *
+ * This method takes as input a String that contains message listings and
+ * removes a space between the < and the msg handle value. This space is
+ * inserted by the serializer causing an error in processing the message
+ * correctly. This private method parses the message and removes the spaces.
+ *
+ * @param String
+ * Message to be parsed
+ * @return String This method returns the message String
+ */
+
+ private String removeMsgHdrSpace(String message) {
+ String str1 = null;
+ String str2 = null;
+
+ Integer index = 0;
+ Integer endSubstring = 0;
+
+ index = message.indexOf("< msg handle");
+ if (index < 0) {
+ str2 = message;
+
+ } else {
+ str2 = message.substring(0, index);
+ str2 = str2 + "\n";
+ index = 0;
+ while ((index = message.indexOf("msg handle", index)) > 0) {
+ endSubstring = message.indexOf("/>", index) + "/>".length();
+ str1 = "<" + message.substring(index, endSubstring);
+ index = index + 1;
+ str2 = str2 + str1;
+
+ }
+ }
+
+ return str2 + "</MAP-msg-listing>";
+
+ }
+ /**
+ * fetchVcardEmail
+ *
+ * This method takes as input a vCard formatted String. It parses the String
+ * and returns the vCard phone number as a String
+ *
+ * @param
+ * @return String This method returns a vCard phone number String
+ */
+
+ private String fetchVcardEmailforMms(String vCard) {
+
+ int pos = vCard.indexOf((CRLF + "EMAIL:"));
+ int beginVersionPos = pos + (("EMAIL:").length() + CRLF.length());
+ int endVersionPos = vCard.indexOf(CRLF, beginVersionPos);
+ return vCard.substring(beginVersionPos, endVersionPos);
+ }
+
+}
diff --git a/src/com/android/bluetooth/map/MapUtils/MapUtilsConsts.java b/src/com/android/bluetooth/map/MapUtils/MapUtilsConsts.java
new file mode 100644
index 000000000..729b89129
--- /dev/null
+++ b/src/com/android/bluetooth/map/MapUtils/MapUtilsConsts.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map.MapUtils;
+
+/**
+ * List of strings used to construct Bmessages
+ *
+ */
+public final class MapUtilsConsts {
+ public static final String BEGIN_BMSG = "BEGIN:BMSG";
+ public static final String END_BMSG = "END:BMSG";
+ public static final String BEGIN_BENV = "BEGIN:BENV";
+ public static final String END_BBENV = "END:BENV";
+
+}
diff --git a/src/com/android/bluetooth/map/MapUtils/MsgListingConsts.java b/src/com/android/bluetooth/map/MapUtils/MsgListingConsts.java
new file mode 100644
index 000000000..a9dccd6d5
--- /dev/null
+++ b/src/com/android/bluetooth/map/MapUtils/MsgListingConsts.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map.MapUtils;
+
+public class MsgListingConsts {
+
+ public class MsgInfo {
+ public String dateTime = null;
+
+ public String getDateTime() {
+ return dateTime;
+ }
+
+ public void setDateTime(String dateTime) {
+ this.dateTime = dateTime;
+ }
+
+ }
+ public MsgInfo msgInfo = new MsgInfo();
+
+ public int msg_handle = 0;
+ public String subject = null;
+ public boolean sendSubject = false;
+ public String datetime = null;
+ public String sender_name = null;
+ public String sender_addressing = null;
+ public String recepient_name = null;
+ public boolean sendRecipient_addressing = false;
+ public String recepient_addressing = null;
+ public String type = null;
+ public int size = 0;
+ public String reception_status = null;
+ public int attachment_size = -1;
+ public String contains_text = null;
+ public String priority = null;
+ public String read = null;
+ public String sent = null;
+ public String msg_protected = null;
+ public String replyto_addressing = null;
+
+ public int getMsg_handle() {
+ return msg_handle;
+ }
+
+ public void setMsg_handle(int msg_handle) {
+ this.msg_handle = msg_handle;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ public void setSendSubject(boolean flag) {
+ this.sendSubject = flag;
+ }
+
+ public String getDatetime() {
+ return datetime;
+ }
+
+ public void setDatetime(String datetime) {
+ this.datetime = datetime;
+ }
+
+ public String getSender_name() {
+ return sender_name;
+ }
+
+ public void setSender_name(String sender_name) {
+ this.sender_name = sender_name;
+ }
+
+ public String getSender_addressing() {
+ return sender_addressing;
+ }
+
+ public void setSender_addressing(String sender_addressing) {
+ this.sender_addressing = sender_addressing;
+ }
+
+ public String getRecepient_name() {
+ return recepient_name;
+ }
+
+ public void setRecepient_name(String recepient_name) {
+ this.recepient_name = recepient_name;
+ }
+
+ public String getRecepient_addressing() {
+ return recepient_addressing;
+ }
+
+ public void setSendRecipient_addressing(boolean flag) {
+ this.sendRecipient_addressing = flag;
+ }
+
+
+ public void setRecepient_addressing(String recepient_addressing) {
+ this.recepient_addressing = recepient_addressing;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public void setSize(int size) {
+ this.size = size;
+ }
+
+ public String getReception_status() {
+ return reception_status;
+ }
+
+ public void setReception_status(String reception_status) {
+ this.reception_status = reception_status;
+ }
+
+ public int getAttachment_size() {
+ return attachment_size;
+ }
+
+ public void setAttachment_size(int attachment_size) {
+ this.attachment_size = attachment_size;
+ }
+
+ public String getContains_text() {
+ return contains_text;
+ }
+
+ public void setContains_text(String contains_text) {
+ this.contains_text = contains_text;
+ }
+
+ public String getPriority() {
+ return priority;
+ }
+
+ public void setPriority(String priority) {
+ this.priority = priority;
+ }
+
+ public String getRead() {
+ return read;
+ }
+
+ public void setRead(String read) {
+ this.read = read;
+ }
+
+ public void setSent(String sent) {
+ this.sent = sent;
+ }
+
+ public String getSent() {
+ return sent;
+ }
+
+ public String getMsg_protected() {
+ return msg_protected;
+ }
+
+ public void setMsg_protected(String msg_protected) {
+ this.msg_protected = msg_protected;
+ }
+
+ public String getReplyTo_addressing() {
+ return sender_addressing;
+ }
+
+ public void setReplyTo_addressing(String replyto_addressing) {
+ this.replyto_addressing = replyto_addressing;
+ }
+
+}
diff --git a/src/com/android/bluetooth/map/MapUtils/SmsMmsUtils.java b/src/com/android/bluetooth/map/MapUtils/SmsMmsUtils.java
new file mode 100644
index 000000000..d00573ec1
--- /dev/null
+++ b/src/com/android/bluetooth/map/MapUtils/SmsMmsUtils.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map.MapUtils;
+
+import java.util.List;
+import java.util.Set;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.text.format.Time;
+import android.util.Log;
+import android.util.TimeFormatException;
+
+import com.android.bluetooth.map.BluetoothMasAppParams;
+
+
+
+public class SmsMmsUtils {
+
+ public final String TAG = "SmsMmsUtils";
+ public static final int BIT_SUBJECT = 0x1;
+ public static final int BIT_DATETIME = 0x2;
+ public static final int BIT_SENDER_NAME = 0x4;
+ public static final int BIT_SENDER_ADDRESSING = 0x8;
+
+ public static final int BIT_RECIPIENT_NAME = 0x10;
+ public static final int BIT_RECIPIENT_ADDRESSING = 0x20;
+ public static final int BIT_TYPE = 0x40;
+ public static final int BIT_SIZE = 0x80;
+
+ public static final int BIT_RECEPTION_STATUS = 0x100;
+ public static final int BIT_TEXT = 0x200;
+ public static final int BIT_ATTACHMENT_SIZE = 0x400;
+ public static final int BIT_PRIORITY = 0x800;
+
+ public static final int BIT_READ = 0x1000;
+ public static final int BIT_SENT = 0x2000;
+ public static final int BIT_PROTECTED = 0x4000;
+ public static final int BIT_REPLYTO_ADDRESSING = 0x8000;
+
+ private final String Inbox = "inbox";
+ private final String Outbox = "outbox";
+ private final String Sent = "sent";
+ private final String Deleted = "deleted";
+ private final String Draft = "draft";
+ private final String Drafts = "drafts";
+ private final String Undelivered = "undelivered";
+ private final String Failed = "failed";
+ private final String Queued = "queued";
+
+ private final int DELETED_THREAD_ID = -1;
+
+ static final int PHONELOOKUP_ID_COLUMN_INDEX = 0;
+ static final int PHONELOOKUP_LOOKUP_KEY_COLUMN_INDEX = 1;
+ static final int PHONELOOKUP_DISPLAY_NAME_COLUMN_INDEX = 2;
+
+ static final int EMAIL_DATA_COLUMN_INDEX = 0;
+
+ private List<VcardContent> list;
+
+ private class VcardContent {
+ public String name = "";
+ public String tel = "";
+ public String email = "";
+ }
+
+ public List<String> folderListSmsMms(List<String> folderList) {
+ folderList.add(Inbox);
+ folderList.add(Outbox);
+ folderList.add(Sent);
+ folderList.add(Deleted);
+ folderList.add(Draft);
+
+ return folderList;
+
+ }
+ public String getWhereIsQueryForType(String folder) {
+
+ String query = null;
+
+ if (folder.equalsIgnoreCase(Inbox)) {
+ query = "type = 1 AND thread_id <> " + DELETED_THREAD_ID;
+ }
+ else if (folder.equalsIgnoreCase(Outbox)) {
+ query = "(type = 4 OR type = 5 OR type = 6) AND thread_id <> " + DELETED_THREAD_ID;
+ }
+ else if (folder.equalsIgnoreCase(Sent)) {
+ query = "type = 2 AND thread_id <> " + DELETED_THREAD_ID;
+ }
+ else if (folder.equalsIgnoreCase(Draft)) {
+ query = "type = 3 AND thread_id <> " + DELETED_THREAD_ID;
+ }
+ else if (folder.equalsIgnoreCase(Deleted)) {
+ query = "thread_id = " + DELETED_THREAD_ID;
+ }
+ else{
+ query = "type = -1";
+ }
+ return query;
+
+ }
+ public String getConditionStringSms(String folderName, BluetoothMasAppParams appParams) {
+ String whereClause = getWhereIsQueryForType(folderName);
+
+ /* Filter readstatus: 0 no filtering, 0x01 get unread, 0x10 get read */
+ if (appParams.FilterReadStatus != 0) {
+ if ((appParams.FilterReadStatus & 0x1) != 0) {
+ if (whereClause != "") {
+ whereClause += " AND ";
+ }
+ whereClause += " read=0 ";
+ }
+ if ((appParams.FilterReadStatus & 0x02) != 0) {
+ if (whereClause != "") {
+ whereClause += " AND ";
+ }
+ whereClause += " read=1 ";
+ }
+ }
+ // TODO Filter priority?
+
+ /* Filter Period Begin */
+ if ((appParams.FilterPeriodBegin != null)
+ && (appParams.FilterPeriodBegin.length() > 0)) {
+ Time time = new Time();
+ try {
+ time.parse(appParams.FilterPeriodBegin);
+ if (whereClause != "") {
+ whereClause += " AND ";
+ }
+ whereClause += "date >= " + time.toMillis(false);
+ } catch (TimeFormatException e) {
+ Log.d(TAG, "Bad formatted FilterPeriodBegin "
+ + appParams.FilterPeriodBegin);
+ }
+ }
+
+ /* Filter Period End */
+ if ((appParams.FilterPeriodEnd != null)
+ && (appParams.FilterPeriodEnd.length() > 0 )) {
+ Time time = new Time();
+ try {
+ time.parse(appParams.FilterPeriodEnd);
+ if (whereClause != "") {
+ whereClause += " AND ";
+ }
+ whereClause += "date < " + time.toMillis(false);
+ } catch (TimeFormatException e) {
+ Log.d(TAG, "Bad formatted FilterPeriodEnd "
+ + appParams.FilterPeriodEnd);
+ }
+ }
+ return whereClause;
+
+ }
+
+}
diff --git a/src/com/android/bluetooth/map/MapUtils/SortMsgListByDate.java b/src/com/android/bluetooth/map/MapUtils/SortMsgListByDate.java
new file mode 100644
index 000000000..0b31e4a75
--- /dev/null
+++ b/src/com/android/bluetooth/map/MapUtils/SortMsgListByDate.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.map.MapUtils;
+
+import java.util.Comparator;
+import com.android.bluetooth.map.MapUtils.MsgListingConsts;
+
+public class SortMsgListByDate implements Comparator<MsgListingConsts> {
+
+ public int compare(MsgListingConsts object1, MsgListingConsts object2) {
+
+ return object2.msgInfo.getDateTime().compareTo(object1.msgInfo.getDateTime());
+ }
+
+}