summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoey Grover <joeygrover@gmail.com>2016-06-14 14:09:29 -0400
committerGitHub <noreply@github.com>2016-06-14 14:09:29 -0400
commit98ae50dba3d8dd4db923e7dc0d812e9d40b2fc3f (patch)
treef1d6b193338c1bd85a3070f95fb4c50fa279b77a
parentb72572664e9bab13eaa9936b5944deab5f7c7b57 (diff)
parent6f9da90e202949216c915c128c6a2bb5740f6cf6 (diff)
downloadsdl_android-98ae50dba3d8dd4db923e7dc0d812e9d40b2fc3f.tar.gz
Merge pull request #78 from smartdevicelink/feature/multiplexing
Merging Feature/multiplexing
-rw-r--r--HelloSdl/.settings/org.eclipse.jdt.core.prefs4
-rw-r--r--HelloSdl/AndroidManifest.xml50
-rw-r--r--HelloSdl/ic_launcher-web.pngbin0 -> 37133 bytes
-rw-r--r--HelloSdl/libs/android-support-v4.jarbin0 -> 995624 bytes
-rw-r--r--HelloSdl/lint.xml3
-rw-r--r--HelloSdl/proguard-project.txt20
-rw-r--r--HelloSdl/project.properties15
-rw-r--r--HelloSdl/res/drawable-hdpi/ic_launcher.pngbin0 -> 5729 bytes
-rw-r--r--HelloSdl/res/drawable-mdpi/ic_launcher.pngbin0 -> 3022 bytes
-rw-r--r--HelloSdl/res/drawable-xhdpi/ic_launcher.pngbin0 -> 9073 bytes
-rw-r--r--HelloSdl/res/drawable-xxhdpi/ic_launcher.pngbin0 -> 17609 bytes
-rw-r--r--HelloSdl/res/layout/activity_main.xml16
-rw-r--r--HelloSdl/res/menu/main.xml11
-rw-r--r--HelloSdl/res/values-w820dp/dimens.xml10
-rw-r--r--HelloSdl/res/values/dimens.xml7
-rw-r--r--HelloSdl/res/values/strings.xml8
-rw-r--r--HelloSdl/res/values/styles.xml20
-rw-r--r--HelloSdl/src/com/hellosdl/MainActivity.java42
-rw-r--r--HelloSdl/src/com/hellosdl/sdl/SdlReceiver.java24
-rw-r--r--HelloSdl/src/com/hellosdl/sdl/SdlRouterService.java8
-rw-r--r--HelloSdl/src/com/hellosdl/sdl/SdlService.java688
-rw-r--r--sdl_android_lib/res/drawable-hdpi/sdl_128.pngbin0 -> 15594 bytes
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/SdlConnection/SdlConnection.java203
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/SdlConnection/SdlSession.java31
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/protocol/AbstractProtocol.java46
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/protocol/BinaryFrameHeader.java2
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/protocol/IProtocolListener.java2
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/protocol/ProtocolFrameHeader.java159
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/protocol/ProtocolFrameHeaderFactory.java144
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/protocol/ProtocolMessage.java13
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/protocol/SdlPacket.aidl5
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/protocol/SdlPacket.java349
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/protocol/SdlPacketFactory.java92
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/protocol/WiProProtocol.java402
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/proxy/RPCMessage.java2
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxy.java404
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyALM.java52
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyBase.java36
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyFactory.java27
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/proxy/rpc/enums/SdlDisconnectedReason.java8
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/streaming/StreamRPCPacketizer.java5
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/trace/SdlTrace.java26
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/BTTransport.java124
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/ITransportListener.java6
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/MultiplexBluetoothTransport.java888
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/MultiplexTransport.java314
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/MultiplexTransportConfig.java51
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/RouterServiceValidator.java575
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/SdlBroadcastReceiver.java272
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/SdlPsm.java247
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/SdlRouterService.java2448
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/SdlTransport.java25
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/TCPTransport.java65
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/TransportBroker.java597
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/TransportConstants.java196
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/USBTransport.java71
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/enums/TransportType.java5
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/utl/ByteAraryMessageAssembler.java71
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/transport/utl/ByteArrayMessageSpliter.java107
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/util/BitConverter.java18
-rw-r--r--sdl_android_lib/src/com/smartdevicelink/util/HttpRequestTask.java172
61 files changed, 7983 insertions, 1203 deletions
diff --git a/HelloSdl/.settings/org.eclipse.jdt.core.prefs b/HelloSdl/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..b080d2ddc
--- /dev/null
+++ b/HelloSdl/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/HelloSdl/AndroidManifest.xml b/HelloSdl/AndroidManifest.xml
new file mode 100644
index 000000000..d15dbe2a6
--- /dev/null
+++ b/HelloSdl/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.hellosdl"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="8"
+ android:targetSdkVersion="19" />
+
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name=".MainActivity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+
+ <service android:name=".sdl.SdlService" ></service>
+
+ <service android:name=".sdl.SdlRouterService"
+ android:exported="true"
+ android:process="com.smartdevicelink.router"></service>
+
+
+ <receiver android:name=".sdl.SdlReceiver" android:enabled="true" >
+ <intent-filter>
+ <action android:name="android.intent.action.BOOT_COMPLETED" />
+ <action android:name="android.bluetooth.device.action.ACL_CONNECTED"/>
+ <action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
+ <action android:name = "sdl.router.startservice"/>
+ </intent-filter>
+ </receiver>
+
+
+ </application>
+
+</manifest>
diff --git a/HelloSdl/ic_launcher-web.png b/HelloSdl/ic_launcher-web.png
new file mode 100644
index 000000000..3e493438c
--- /dev/null
+++ b/HelloSdl/ic_launcher-web.png
Binary files differ
diff --git a/HelloSdl/libs/android-support-v4.jar b/HelloSdl/libs/android-support-v4.jar
new file mode 100644
index 000000000..4ebdaa9ed
--- /dev/null
+++ b/HelloSdl/libs/android-support-v4.jar
Binary files differ
diff --git a/HelloSdl/lint.xml b/HelloSdl/lint.xml
new file mode 100644
index 000000000..ee0eead5b
--- /dev/null
+++ b/HelloSdl/lint.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lint>
+</lint> \ No newline at end of file
diff --git a/HelloSdl/proguard-project.txt b/HelloSdl/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/HelloSdl/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/HelloSdl/project.properties b/HelloSdl/project.properties
new file mode 100644
index 000000000..f76c1b9a4
--- /dev/null
+++ b/HelloSdl/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
+android.library.reference.1=../sdl_android_lib
diff --git a/HelloSdl/res/drawable-hdpi/ic_launcher.png b/HelloSdl/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..903e3d12d
--- /dev/null
+++ b/HelloSdl/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/HelloSdl/res/drawable-mdpi/ic_launcher.png b/HelloSdl/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..8415d3068
--- /dev/null
+++ b/HelloSdl/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/HelloSdl/res/drawable-xhdpi/ic_launcher.png b/HelloSdl/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..962976da1
--- /dev/null
+++ b/HelloSdl/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/HelloSdl/res/drawable-xxhdpi/ic_launcher.png b/HelloSdl/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..f4c378997
--- /dev/null
+++ b/HelloSdl/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/HelloSdl/res/layout/activity_main.xml b/HelloSdl/res/layout/activity_main.xml
new file mode 100644
index 000000000..5056c3b98
--- /dev/null
+++ b/HelloSdl/res/layout/activity_main.xml
@@ -0,0 +1,16 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ tools:context="com.hellosdl.MainActivity" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/hello_world" />
+
+</RelativeLayout>
diff --git a/HelloSdl/res/menu/main.xml b/HelloSdl/res/menu/main.xml
new file mode 100644
index 000000000..e3df24bab
--- /dev/null
+++ b/HelloSdl/res/menu/main.xml
@@ -0,0 +1,11 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:context="com.hellosdl.MainActivity" >
+
+ <item
+ android:id="@+id/action_settings"
+ android:orderInCategory="100"
+ android:title="@string/action_settings"/>
+
+</menu>
diff --git a/HelloSdl/res/values-w820dp/dimens.xml b/HelloSdl/res/values-w820dp/dimens.xml
new file mode 100644
index 000000000..f3e70203b
--- /dev/null
+++ b/HelloSdl/res/values-w820dp/dimens.xml
@@ -0,0 +1,10 @@
+<resources>
+
+ <!--
+ Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively).
+ -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+
+</resources>
diff --git a/HelloSdl/res/values/dimens.xml b/HelloSdl/res/values/dimens.xml
new file mode 100644
index 000000000..55c1e5908
--- /dev/null
+++ b/HelloSdl/res/values/dimens.xml
@@ -0,0 +1,7 @@
+<resources>
+
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
diff --git a/HelloSdl/res/values/strings.xml b/HelloSdl/res/values/strings.xml
new file mode 100644
index 000000000..a7f78e961
--- /dev/null
+++ b/HelloSdl/res/values/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">HelloSdl</string>
+ <string name="hello_world">Hello Car!</string>
+ <string name="action_settings">Settings</string>
+
+</resources>
diff --git a/HelloSdl/res/values/styles.xml b/HelloSdl/res/values/styles.xml
new file mode 100644
index 000000000..4d175139d
--- /dev/null
+++ b/HelloSdl/res/values/styles.xml
@@ -0,0 +1,20 @@
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
diff --git a/HelloSdl/src/com/hellosdl/MainActivity.java b/HelloSdl/src/com/hellosdl/MainActivity.java
new file mode 100644
index 000000000..da6b8ecaf
--- /dev/null
+++ b/HelloSdl/src/com/hellosdl/MainActivity.java
@@ -0,0 +1,42 @@
+package com.hellosdl;
+
+import com.hellosdl.sdl.SdlReceiver;
+import com.hellosdl.sdl.SdlService;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+
+public class MainActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ if(SdlReceiver.isTransportConnected(getBaseContext())){
+ Intent startIntent = new Intent(getBaseContext(), SdlService.class);
+ startService(startIntent);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+ if (id == R.id.action_settings) {
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/HelloSdl/src/com/hellosdl/sdl/SdlReceiver.java b/HelloSdl/src/com/hellosdl/sdl/SdlReceiver.java
new file mode 100644
index 000000000..2162516f5
--- /dev/null
+++ b/HelloSdl/src/com/hellosdl/sdl/SdlReceiver.java
@@ -0,0 +1,24 @@
+package com.hellosdl.sdl;
+
+
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+
+public class SdlReceiver extends com.smartdevicelink.transport.SdlBroadcastReceiver {
+ private static final String TAG = "BCast Receiver";
+
+ @Override
+ public Class defineLocalSdlRouterClass() {
+ return SdlRouterService.class;
+ }
+
+ @Override
+ public void onSdlEnabled(Context context, Intent intent) {
+ Log.e(TAG, "SDL Enabled");
+ intent.setClass(context, SdlService.class);
+ context.startService(intent);
+ }
+}
diff --git a/HelloSdl/src/com/hellosdl/sdl/SdlRouterService.java b/HelloSdl/src/com/hellosdl/sdl/SdlRouterService.java
new file mode 100644
index 000000000..b80d30c4e
--- /dev/null
+++ b/HelloSdl/src/com/hellosdl/sdl/SdlRouterService.java
@@ -0,0 +1,8 @@
+package com.hellosdl.sdl;
+
+
+public class SdlRouterService extends com.smartdevicelink.transport.SdlRouterService {
+
+
+
+}
diff --git a/HelloSdl/src/com/hellosdl/sdl/SdlService.java b/HelloSdl/src/com/hellosdl/sdl/SdlService.java
new file mode 100644
index 000000000..eba5bc780
--- /dev/null
+++ b/HelloSdl/src/com/hellosdl/sdl/SdlService.java
@@ -0,0 +1,688 @@
+package com.hellosdl.sdl;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.hellosdl.MainActivity;
+import com.hellosdl.R;
+import com.smartdevicelink.exception.SdlException;
+import com.smartdevicelink.proxy.SdlProxyALM;
+import com.smartdevicelink.proxy.callbacks.OnServiceEnded;
+import com.smartdevicelink.proxy.callbacks.OnServiceNACKed;
+import com.smartdevicelink.proxy.interfaces.IProxyListenerALM;
+import com.smartdevicelink.proxy.rpc.AddCommandResponse;
+import com.smartdevicelink.proxy.rpc.AddSubMenuResponse;
+import com.smartdevicelink.proxy.rpc.AlertManeuverResponse;
+import com.smartdevicelink.proxy.rpc.AlertResponse;
+import com.smartdevicelink.proxy.rpc.ChangeRegistrationResponse;
+import com.smartdevicelink.proxy.rpc.CreateInteractionChoiceSetResponse;
+import com.smartdevicelink.proxy.rpc.DeleteCommandResponse;
+import com.smartdevicelink.proxy.rpc.DeleteFileResponse;
+import com.smartdevicelink.proxy.rpc.DeleteInteractionChoiceSetResponse;
+import com.smartdevicelink.proxy.rpc.DeleteSubMenuResponse;
+import com.smartdevicelink.proxy.rpc.DiagnosticMessageResponse;
+import com.smartdevicelink.proxy.rpc.DialNumberResponse;
+import com.smartdevicelink.proxy.rpc.EndAudioPassThruResponse;
+import com.smartdevicelink.proxy.rpc.GenericResponse;
+import com.smartdevicelink.proxy.rpc.GetDTCsResponse;
+import com.smartdevicelink.proxy.rpc.GetVehicleDataResponse;
+import com.smartdevicelink.proxy.rpc.ListFilesResponse;
+import com.smartdevicelink.proxy.rpc.OnAudioPassThru;
+import com.smartdevicelink.proxy.rpc.OnButtonEvent;
+import com.smartdevicelink.proxy.rpc.OnButtonPress;
+import com.smartdevicelink.proxy.rpc.OnCommand;
+import com.smartdevicelink.proxy.rpc.OnDriverDistraction;
+import com.smartdevicelink.proxy.rpc.OnHMIStatus;
+import com.smartdevicelink.proxy.rpc.OnHashChange;
+import com.smartdevicelink.proxy.rpc.OnKeyboardInput;
+import com.smartdevicelink.proxy.rpc.OnLanguageChange;
+import com.smartdevicelink.proxy.rpc.OnLockScreenStatus;
+import com.smartdevicelink.proxy.rpc.OnPermissionsChange;
+import com.smartdevicelink.proxy.rpc.OnStreamRPC;
+import com.smartdevicelink.proxy.rpc.OnSystemRequest;
+import com.smartdevicelink.proxy.rpc.OnTBTClientState;
+import com.smartdevicelink.proxy.rpc.OnTouchEvent;
+import com.smartdevicelink.proxy.rpc.OnVehicleData;
+import com.smartdevicelink.proxy.rpc.PerformAudioPassThruResponse;
+import com.smartdevicelink.proxy.rpc.PerformInteractionResponse;
+import com.smartdevicelink.proxy.rpc.PutFile;
+import com.smartdevicelink.proxy.rpc.PutFileResponse;
+import com.smartdevicelink.proxy.rpc.ReadDIDResponse;
+import com.smartdevicelink.proxy.rpc.ResetGlobalPropertiesResponse;
+import com.smartdevicelink.proxy.rpc.ScrollableMessageResponse;
+import com.smartdevicelink.proxy.rpc.SendLocationResponse;
+import com.smartdevicelink.proxy.rpc.SetAppIconResponse;
+import com.smartdevicelink.proxy.rpc.SetDisplayLayoutResponse;
+import com.smartdevicelink.proxy.rpc.SetGlobalPropertiesResponse;
+import com.smartdevicelink.proxy.rpc.SetMediaClockTimerResponse;
+import com.smartdevicelink.proxy.rpc.ShowConstantTbtResponse;
+import com.smartdevicelink.proxy.rpc.ShowResponse;
+import com.smartdevicelink.proxy.rpc.SliderResponse;
+import com.smartdevicelink.proxy.rpc.SpeakResponse;
+import com.smartdevicelink.proxy.rpc.StreamRPCResponse;
+import com.smartdevicelink.proxy.rpc.SubscribeButtonResponse;
+import com.smartdevicelink.proxy.rpc.SubscribeVehicleDataResponse;
+import com.smartdevicelink.proxy.rpc.SystemRequestResponse;
+import com.smartdevicelink.proxy.rpc.UnsubscribeButtonResponse;
+import com.smartdevicelink.proxy.rpc.UnsubscribeVehicleDataResponse;
+import com.smartdevicelink.proxy.rpc.UpdateTurnListResponse;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import com.smartdevicelink.proxy.rpc.enums.SdlDisconnectedReason;
+import com.smartdevicelink.proxy.rpc.enums.TextAlignment;
+import com.smartdevicelink.transport.MultiplexTransportConfig;
+import com.smartdevicelink.transport.TransportConstants;
+
+/**
+ * While this class is just an extension off the base Android Service class, we hope in the future we can offer something
+ * better that takes care of a lot of life cycle stuff.
+ * <p>
+ * For now this shows the most basic of operations to take when connecting via SDL
+ * Here's what it covers
+ * 1. Basic Sdl proxy life cycle
+ * 2. Sending app icon
+ * 3. When to launch activity and dealing with different HMI_STATUS's
+ *
+ *
+ * @author Joey Grover
+ *
+ */
+public class SdlService extends Service implements IProxyListenerALM{
+ private static final String TAG = "SdlService";
+ private static final String APP_NAME = "Hello Car"; //TODO enter your own app name here
+ private static final String APP_ID = "7331"; //TODO enter your own app id here
+ private static final String ICON_SYNC_FILENAME = "icon.png";
+ private static final String ICON_FILENAME_SUFFIX = ".png";
+
+
+ // variable to create and call functions of the SdlProxy
+ private SdlProxyALM proxy = null;
+
+ // variable used to increment correlation ID for every request sent to a SDL system
+ public int autoIncCorrId = 0;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ startProxy();
+ }
+
+
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if(proxy == null){
+ startProxy();
+ }else if (intent != null && intent.hasExtra(TransportConstants.FORCE_TRANSPORT_CONNECTED)){
+ proxy.forceOnConnected();
+ }
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ disposeSyncProxy();
+ super.onDestroy();
+ }
+
+
+ /* ***********************************************************************************************************************************************************************
+ * ******************************************************* Methods for SdlProxy Management *******************************************************************************
+ *************************************************************************************************************************************************************************/
+
+ public void startProxy() {
+ if (proxy == null) {
+ try {
+ proxy = new SdlProxyALM(this, APP_NAME,
+ true, APP_ID,
+ new MultiplexTransportConfig(getBaseContext(), APP_ID));
+
+ } catch (SdlException e) {
+ e.printStackTrace();
+ // error creating proxy, returned proxy = null
+ if (proxy == null) {
+ stopSelf();
+ }
+ }
+ }
+ }
+
+ public void disposeSyncProxy() {
+ if (proxy != null) {
+ try {
+ proxy.dispose();
+ } catch (SdlException e) {
+ e.printStackTrace();
+ }
+ proxy = null;
+ }
+ }
+
+ /**
+ * This will send the app icon over to the SDL system
+ * @throws SdlException
+ */
+ private void sendIcon() throws SdlException {
+ PutFile putFile = new PutFile();
+ putFile.setFileType(FileType.GRAPHIC_PNG);
+ putFile.setSdlFileName(ICON_SYNC_FILENAME);
+ putFile.setCorrelationID(autoIncCorrId++);
+ putFile.setBulkData(contentsOfResource(R.drawable.ic_launcher));
+
+ proxy.sendRPCRequest(putFile);
+ }
+
+ /**
+ * This is a convience method that will grab all the binary data from a resource
+ * to be able to be sent to the SDL sytem
+ * @param resource
+ * @return
+ */
+ private byte[] contentsOfResource(int resource) {
+ InputStream is = null;
+ try {
+ is = getResources().openRawResource(resource);
+ ByteArrayOutputStream os = new ByteArrayOutputStream(is.available());
+ final int buffersize = 4096;
+ final byte[] buffer = new byte[buffersize];
+ int available = 0;
+ while ((available = is.read(buffer)) >= 0) {
+ os.write(buffer, 0, available);
+ }
+ return os.toByteArray();
+ } catch (IOException e) {
+ Log.w("SDL Service", "Can't read icon file", e);
+ return null;
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /* ***********************************************************************************************************************************************************************
+ * ******************************************************* Methods for IProxyListenerALM *******************************************************************************
+ *************************************************************************************************************************************************************************/
+
+ @Override
+ public void onOnHMIStatus(OnHMIStatus notification) {
+
+ switch(notification.getHmiLevel()) {
+ case HMI_FULL:
+ Log.i(TAG, "HMI_FULL");
+ //When we get this notification it means we have main access to the SDL System
+ Intent start = new Intent(this,MainActivity.class);
+ start.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP + Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(start);
+
+ if (notification.getFirstRun()) {
+ // send welcome message if applicable
+ try {
+ proxy.show(APP_NAME + " this is the first", "show command", TextAlignment.CENTERED, autoIncCorrId++);
+ }catch (SdlException e) {
+ e.printStackTrace();
+ }
+
+ // TODO this is where the developer should send addcommands and subsribe to buttons
+ }else { //If this isn't our first time receiving HMI_FULL
+ try {
+ proxy.show("SdlProxy is", "Alive", TextAlignment.CENTERED, autoIncCorrId++);
+ } catch (SdlException e) {
+ e.printStackTrace();
+ }
+ }
+ break;
+ case HMI_LIMITED:
+ Log.i(TAG, "HMI_LIMITED");
+ break;
+ case HMI_BACKGROUND:
+ Log.i(TAG, "HMI_BACKGROUND");
+ break;
+ case HMI_NONE:
+ Log.i(TAG, "HMI_NONE");
+ //Since the first HMI status sent to an app is HMI_NONE we take this as we have just established a connection and we want to send our icon
+ if(notification.getFirstRun()){
+ try {
+ sendIcon();
+ } catch (SdlException e) {
+ e.printStackTrace();
+ }
+ }
+ break;
+ default:
+ return;
+ }
+
+ }
+
+
+ @Override
+ public void onProxyClosed(String info, Exception e, SdlDisconnectedReason reason) {
+ //After we are asked to close the proxy we should shut down our service
+ stopSelf();
+ }
+
+
+ @Override
+ public void onError(String info, Exception e) {
+
+
+ }
+
+
+ @Override
+ public void onGenericResponse(GenericResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onOnCommand(OnCommand notification) {
+
+
+ }
+
+
+ @Override
+ public void onAddCommandResponse(AddCommandResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onAddSubMenuResponse(AddSubMenuResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onCreateInteractionChoiceSetResponse(
+ CreateInteractionChoiceSetResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onAlertResponse(AlertResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onDeleteCommandResponse(DeleteCommandResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onDeleteInteractionChoiceSetResponse(
+ DeleteInteractionChoiceSetResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onDeleteSubMenuResponse(DeleteSubMenuResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onPerformInteractionResponse(PerformInteractionResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onResetGlobalPropertiesResponse(
+ ResetGlobalPropertiesResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onSetGlobalPropertiesResponse(
+ SetGlobalPropertiesResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onSetMediaClockTimerResponse(SetMediaClockTimerResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onShowResponse(ShowResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onSpeakResponse(SpeakResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onOnButtonEvent(OnButtonEvent notification) {
+
+
+ }
+
+
+ @Override
+ public void onOnButtonPress(OnButtonPress notification) {
+
+
+ }
+
+
+ @Override
+ public void onSubscribeButtonResponse(SubscribeButtonResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onUnsubscribeButtonResponse(UnsubscribeButtonResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onOnPermissionsChange(OnPermissionsChange notification) {
+
+
+ }
+
+
+ @Override
+ public void onSubscribeVehicleDataResponse(
+ SubscribeVehicleDataResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onUnsubscribeVehicleDataResponse(
+ UnsubscribeVehicleDataResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onGetVehicleDataResponse(GetVehicleDataResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onOnVehicleData(OnVehicleData notification) {
+
+
+ }
+
+
+ @Override
+ public void onPerformAudioPassThruResponse(
+ PerformAudioPassThruResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onEndAudioPassThruResponse(EndAudioPassThruResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onOnAudioPassThru(OnAudioPassThru notification) {
+
+
+ }
+
+
+ @Override
+ public void onPutFileResponse(PutFileResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onDeleteFileResponse(DeleteFileResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onListFilesResponse(ListFilesResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onSetAppIconResponse(SetAppIconResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onScrollableMessageResponse(ScrollableMessageResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onChangeRegistrationResponse(ChangeRegistrationResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onSetDisplayLayoutResponse(SetDisplayLayoutResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onOnLanguageChange(OnLanguageChange notification) {
+
+
+ }
+
+
+ @Override
+ public void onOnHashChange(OnHashChange notification) {
+
+
+ }
+
+
+ @Override
+ public void onSliderResponse(SliderResponse response) {
+
+
+ }
+
+
+ @Override
+ public void onOnDriverDistraction(OnDriverDistraction notification) {
+
+
+ }
+
+
+ @Override
+ public void onOnTBTClientState(OnTBTClientState notification) {
+
+
+ }
+
+
+ @Override
+ public void onOnSystemRequest(OnSystemRequest notification) {
+
+
+ }
+
+
+ @Override
+ public void onSystemRequestResponse(SystemRequestResponse response) {
+
+ }
+
+
+ @Override
+ public void onOnKeyboardInput(OnKeyboardInput notification) {
+
+ }
+
+
+ @Override
+ public void onOnTouchEvent(OnTouchEvent notification) {
+
+ }
+
+
+ @Override
+ public void onDiagnosticMessageResponse(DiagnosticMessageResponse response) {
+
+ }
+
+
+ @Override
+ public void onReadDIDResponse(ReadDIDResponse response) {
+
+ }
+
+
+ @Override
+ public void onGetDTCsResponse(GetDTCsResponse response) {
+
+ }
+
+
+ @Override
+ public void onOnLockScreenNotification(OnLockScreenStatus notification) {
+
+ }
+
+
+ @Override
+ public void onOnStreamRPC(OnStreamRPC notification) {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ @Override
+ public void onStreamRPCResponse(StreamRPCResponse response) {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ @Override
+ public void onDialNumberResponse(DialNumberResponse response) {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ @Override
+ public void onSendLocationResponse(SendLocationResponse response) {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ @Override
+ public void onServiceEnded(OnServiceEnded serviceEnded) {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ @Override
+ public void onServiceNACKed(OnServiceNACKed serviceNACKed) {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ @Override
+ public void onShowConstantTbtResponse(ShowConstantTbtResponse response) {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ @Override
+ public void onAlertManeuverResponse(AlertManeuverResponse response) {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ @Override
+ public void onUpdateTurnListResponse(UpdateTurnListResponse response) {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ @Override
+ public void onServiceDataACK() {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/sdl_android_lib/res/drawable-hdpi/sdl_128.png b/sdl_android_lib/res/drawable-hdpi/sdl_128.png
new file mode 100644
index 000000000..552ac8aef
--- /dev/null
+++ b/sdl_android_lib/res/drawable-hdpi/sdl_128.png
Binary files differ
diff --git a/sdl_android_lib/src/com/smartdevicelink/SdlConnection/SdlConnection.java b/sdl_android_lib/src/com/smartdevicelink/SdlConnection/SdlConnection.java
index caca68eac..60d6e5362 100644
--- a/sdl_android_lib/src/com/smartdevicelink/SdlConnection/SdlConnection.java
+++ b/sdl_android_lib/src/com/smartdevicelink/SdlConnection/SdlConnection.java
@@ -5,10 +5,11 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
+import java.io.PipedOutputStream;
import java.util.concurrent.CopyOnWriteArrayList;
import android.annotation.SuppressLint;
+import android.content.ComponentName;
import android.os.Build;
import android.util.Log;
import android.view.Surface;
@@ -18,17 +19,20 @@ import com.smartdevicelink.exception.SdlException;
import com.smartdevicelink.protocol.AbstractProtocol;
import com.smartdevicelink.protocol.IProtocolListener;
import com.smartdevicelink.protocol.ProtocolMessage;
+import com.smartdevicelink.protocol.SdlPacket;
import com.smartdevicelink.protocol.WiProProtocol;
import com.smartdevicelink.protocol.enums.SessionType;
import com.smartdevicelink.proxy.RPCRequest;
import com.smartdevicelink.streaming.IStreamListener;
import com.smartdevicelink.streaming.StreamPacketizer;
-import com.smartdevicelink.streaming.StreamRPCPacketizer;
+import com.smartdevicelink.streaming.StreamRPCPacketizer;
import com.smartdevicelink.transport.*;
import com.smartdevicelink.transport.enums.TransportType;
public class SdlConnection implements IProtocolListener, ITransportListener, IStreamListener {
+ private static final String TAG = "SdlConnection";
+
SdlTransport _transport = null;
AbstractProtocol _protocol = null;
ISdlConnectionListener _connectionListener = null;
@@ -39,12 +43,14 @@ public class SdlConnection implements IProtocolListener, ITransportListener, ISt
SdlEncoder mSdlEncoder = null;
// Thread safety locks
- Object TRANSPORT_REFERENCE_LOCK = new Object();
+ static Object TRANSPORT_REFERENCE_LOCK = new Object();
Object PROTOCOL_REFERENCE_LOCK = new Object();
-
+
+ private Object SESSION_LOCK = new Object();
private CopyOnWriteArrayList<SdlSession> listenerList = new CopyOnWriteArrayList<SdlSession>();
-
+ private static TransportType legacyTransportRequest = null;
private final static int BUFF_READ_SIZE = 1000000;
+ protected static MultiplexTransportConfig cachedMultiConfig = null;
/**
* Constructor.
@@ -53,6 +59,25 @@ public class SdlConnection implements IProtocolListener, ITransportListener, ISt
* @param transportConfig Transport configuration for this connection.
*/
public SdlConnection(BaseTransportConfig transportConfig) {
+ RouterServiceValidator vlad = null;
+ //Let's check if we can even do multiplexing
+ if(transportConfig.getTransportType() == TransportType.MULTIPLEX){
+ ComponentName tempCompName = SdlBroadcastReceiver.consumeQueuedRouterService();
+ if(tempCompName!=null){
+ vlad =new RouterServiceValidator(((MultiplexTransportConfig)transportConfig).getContext(),tempCompName);
+ }else{
+ vlad =new RouterServiceValidator(((MultiplexTransportConfig)transportConfig).getContext());
+ }
+ //vlad.setFlags(RouterServiceValidator.FLAG_DEBUG_VERSION_CHECK);
+ }
+ constructor(transportConfig,vlad);
+ }
+ //For unit tests
+ protected SdlConnection(BaseTransportConfig transportConfig,RouterServiceValidator rsvp){
+ constructor(transportConfig,rsvp);
+ }
+
+ private void constructor(BaseTransportConfig transportConfig,RouterServiceValidator rsvp){
_connectionListener = new InternalMsgDispatcher();
// Initialize the transport
@@ -65,10 +90,34 @@ public class SdlConnection implements IProtocolListener, ITransportListener, ISt
_transport = null;
}
- if (transportConfig.getTransportType() == TransportType.BLUETOOTH)
- {
- BTTransportConfig myConfig = (BTTransportConfig) transportConfig;
- _transport = new BTTransport(this, myConfig.getKeepSocketActive());
+ //Let's check if we can even do multiplexing
+ if(!isLegacyModeEnabled() &&
+ rsvp!= null &&
+ transportConfig.getTransportType() == TransportType.MULTIPLEX){
+ //rsvp = new RouterServiceValidator(((MultiplexTransportConfig)transportConfig).getContext());
+ //vlad.setFlags(RouterServiceValidator.FLAG_DEBUG_VERSION_CHECK);
+ if(rsvp.validate()){
+ Log.w(TAG, "SDL Router service is valid; attempting to connect");
+ ((MultiplexTransportConfig)transportConfig).setService(rsvp.getService());//Let thes the transport broker know which service to connect to
+ }else{
+ Log.w(TAG, "SDL Router service isn't trusted. Enabling legacy bluetooth connection.");
+ if(cachedMultiConfig == null){
+ cachedMultiConfig = (MultiplexTransportConfig) transportConfig;
+ cachedMultiConfig.setService(null);
+ }
+ enableLegacyMode(true,TransportType.BLUETOOTH); //We will use legacy bluetooth connection for this attempt
+ Log.d(TAG, "Legacy transport : " + legacyTransportRequest);
+ }
+ }
+
+ if(!isLegacyModeEnabled() && //Make sure legacy mode is not enabled
+ (transportConfig.getTransportType() == TransportType.MULTIPLEX)){
+ _transport = new MultiplexTransport((MultiplexTransportConfig)transportConfig,this);
+ }else if(isLegacyModeEnabled() && legacyTransportRequest == TransportType.BLUETOOTH){
+ Log.d(TAG, "Creating legacy bluetooth connection");
+ _transport = new BTTransport(this, true);
+ }else if(transportConfig.getTransportType() == TransportType.BLUETOOTH){
+ _transport = new BTTransport(this,((BTTransportConfig)transportConfig).getKeepSocketActive());
}
else if (transportConfig.getTransportType() == TransportType.TCP)
{
@@ -98,6 +147,7 @@ public class SdlConnection implements IProtocolListener, ITransportListener, ISt
private void closeConnection(boolean willRecycle, byte rpcSessionID, int sessionHashId) {
synchronized(PROTOCOL_REFERENCE_LOCK) {
+
if (_protocol != null) {
// If transport is still connected, sent EndProtocolSessionMessage
if (_transport != null && _transport.getIsConnected()) {
@@ -108,7 +158,6 @@ public class SdlConnection implements IProtocolListener, ITransportListener, ISt
}
} // end-if
}
-
synchronized (TRANSPORT_REFERENCE_LOCK) {
if (willRecycle) {
if (_transport != null) {
@@ -157,12 +206,11 @@ public class SdlConnection implements IProtocolListener, ITransportListener, ISt
}
@Override
- public void onTransportBytesReceived(byte[] receivedBytes,
- int receivedBytesLength) {
+ public void onTransportPacketReceived(SdlPacket packet) {
// Send bytes to protocol to be interpreted
synchronized(PROTOCOL_REFERENCE_LOCK) {
if (_protocol != null) {
- _protocol.HandleReceivedBytes(receivedBytes, receivedBytesLength);
+ _protocol.handlePacketReceived(packet);
}
}
}
@@ -171,8 +219,12 @@ public class SdlConnection implements IProtocolListener, ITransportListener, ISt
public void onTransportConnected() {
synchronized(PROTOCOL_REFERENCE_LOCK){
if(_protocol != null){
+ boolean shouldRequestSession = _transport !=null && _transport.getTransportType()== TransportType.MULTIPLEX;
for (SdlSession s : listenerList) {
if (s.getSessionId() == 0) {
+ if(shouldRequestSession){
+ ((MultiplexTransport)_transport).requestNewSession();
+ }
startHandShake();
}
}
@@ -193,12 +245,11 @@ public class SdlConnection implements IProtocolListener, ITransportListener, ISt
}
@Override
- public void onProtocolMessageBytesToSend(byte[] msgBytes, int offset,
- int length) {
+ public void onProtocolMessageBytesToSend(SdlPacket packet) {
// Protocol has packaged bytes to send, pass to transport for transmission
synchronized(TRANSPORT_REFERENCE_LOCK) {
if (_transport != null) {
- _transport.sendBytes(msgBytes, offset, length);
+ _transport.sendBytes(packet);
}
}
}
@@ -447,12 +498,14 @@ public class SdlConnection implements IProtocolListener, ITransportListener, ISt
}
}
}
- void registerSession(SdlSession registerListener) throws SdlException {
- listenerList.addIfAbsent(registerListener);
-
+ void registerSession(SdlSession registerListener) throws SdlException {
+ boolean didAdd = listenerList.addIfAbsent(registerListener);
if (!this.getIsConnected()) {
this.startTransport();
} else {
+ if(didAdd && _transport !=null && _transport.getTransportType()== TransportType.MULTIPLEX){ //If we're connected we can request the extra session now
+ ((MultiplexTransport)_transport).requestNewSession();
+ }
this.startHandShake();
}
}
@@ -463,7 +516,10 @@ public class SdlConnection implements IProtocolListener, ITransportListener, ISt
}
public void unregisterSession(SdlSession registerListener) {
- listenerList.remove(registerListener);
+ boolean didRemove = listenerList.remove(registerListener);
+ if(didRemove && _transport !=null && _transport.getTransportType()== TransportType.MULTIPLEX){ //If we're connected we can request the extra session now
+ ((MultiplexTransport)_transport).removeSession(registerListener.getSessionId());
+ }
closeConnection(listenerList.size() == 0, registerListener.getSessionId(), registerListener.getSessionHashId());
}
@@ -484,13 +540,48 @@ public class SdlConnection implements IProtocolListener, ITransportListener, ISt
for (SdlSession session : listenerList) {
session.onTransportDisconnected(info);
}
+ if(cachedMultiConfig!=null ){
+ if(cachedMultiConfig.getService()!=null){
+ synchronized(TRANSPORT_REFERENCE_LOCK) {
+ // Ensure transport is null
+ if (_transport != null) {
+ if (_transport.getIsConnected()) {
+ _transport.disconnect();
+ }
+ _transport = null;
+ }
+ _transport = new MultiplexTransport(cachedMultiConfig, SdlConnection.this);
+ try {
+ startTransport();
+ } catch (SdlException e) {
+ e.printStackTrace();
+ }
+ ((MultiplexTransport)_transport).forceHardwareConnectEvent(TransportType.BLUETOOTH);
+ }
+ }else{ //The service must be null or already consumed. Let's see if we can find the connection that consumed it
+ for (SdlSession session : listenerList) {
+ session.checkForOpenMultiplexConnection(SdlConnection.this);;
+ }
+ }
+ }
}
@Override
public void onTransportError(String info, Exception e) {
+ //If there's an error with the transport we want to make sure we clear out any reference to it held by the static list in sessions
+ SdlSession.removeConnection(SdlConnection.this);
+ //If we are erroring out to go into legacy mode, lets cache our multiplexing
+ if(isLegacyModeEnabled() && _transport!=null && TransportType.MULTIPLEX.equals(_transport.getTransportType())){
+ MultiplexTransport multi = ((MultiplexTransport)_transport);
+ cachedMultiConfig = multi.getConfig();
+ cachedMultiConfig.setService(null); //Make sure we're clearning this out
+ }else{
+ cachedMultiConfig = null; //It should now be consumed
+ }
for (SdlSession session : listenerList) {
session.onTransportError(info, e);
}
+
}
@Override
@@ -628,6 +719,78 @@ public class SdlConnection implements IProtocolListener, ITransportListener, ISt
mySession._incomingHeartbeatMonitor.notifyTransportActivity();
}
}
+
+ public void forceHardwareConnectEvent(TransportType type){
+ if(_transport == null){
+ Log.w(TAG, "Unable to force connect, transport was null!");
+ return;
+ }
+ if(isLegacyModeEnabled()){//We know we should no longer be in legacy mode for future connections, so lets clear out that flag
+ enableLegacyMode(false,null);
+ }
+ if(_transport!=null && (_transport.getTransportType()==TransportType.MULTIPLEX)){ //This is only valid for the multiplex connection
+ MultiplexTransport multi = ((MultiplexTransport)_transport);
+ MultiplexTransportConfig config = multi.getConfig();
+ ComponentName tempCompName = SdlBroadcastReceiver.consumeQueuedRouterService();
+ //Log.d(TAG, "Consumed component name: " +tempCompName );
+ if(config.getService().equals(tempCompName)){ //If this is the same service that just connected that we are already looking at. Attempt to reconnect
+ boolean forced = multi.forceHardwareConnectEvent(TransportType.BLUETOOTH);
+
+ if(!forced && multi.isDisconnecting() ){ //If we aren't able to force a connection it means the
+ //Log.d(TAG, "Recreating our multiplexing transport");
+ _transport = new MultiplexTransport(config,this);
+ ((MultiplexTransport)_transport).forceHardwareConnectEvent(TransportType.BLUETOOTH);
+ }//else{Log.w(TAG, "Guess we're just calling it a day");}
+ }else if(tempCompName!=null){
+ //We have a conflicting service request
+ Log.w(TAG, "Conflicting services. Disconnecting from current and connecting to new");
+ Log.w(TAG, "Old service " + config.getService().toShortString());
+ Log.w(TAG, "New Serivce " + tempCompName.toString());
+ multi.disconnect();
+ config.setService(tempCompName);
+ _transport = new MultiplexTransport(config,this);
+ try {
+ startTransport();
+ } catch (SdlException e) {
+ e.printStackTrace();
+ }
+ ((MultiplexTransport)_transport).forceHardwareConnectEvent(TransportType.BLUETOOTH);
+
+ }
+ }else if(_transport.getTransportType()==TransportType.BLUETOOTH
+ && !_transport.getIsConnected()){
+ if(cachedMultiConfig!=null){
+ //We are in legacy mode, but just received a force connect. The router service should never be pointing us here if we are truely in legacy mode
+ ComponentName tempCompName = SdlBroadcastReceiver.consumeQueuedRouterService();
+ cachedMultiConfig.setService(tempCompName);
+ //We are not connected yet so we should be able to close down
+ _transport.disconnect(); //This will force us into the
+ }else{
+ Log.i(TAG, "No cached multiplexing config, transport error being called");
+ _transport.disconnect();
+ }
+ Log.w(TAG, "Using own transport, but not connected. Attempting to join multiplexing");
+ }else{
+ Log.w(TAG, "Currently in legacy mode connected to own transport service. Nothing will take place on trnasport cycle");
+ }
+ }
+
+ public static void enableLegacyMode(boolean enable, TransportType type){
+ synchronized(TRANSPORT_REFERENCE_LOCK) {
+ if(enable){
+ legacyTransportRequest = type;
+ }else{
+ legacyTransportRequest = null;
+ }
+ }
+ }
+ public static boolean isLegacyModeEnabled(){
+ synchronized(TRANSPORT_REFERENCE_LOCK) {
+ return (legacyTransportRequest!=null);
+ }
+ }
+
+
@Override
public void onProtocolSessionEndedNACKed(SessionType sessionType,
diff --git a/sdl_android_lib/src/com/smartdevicelink/SdlConnection/SdlSession.java b/sdl_android_lib/src/com/smartdevicelink/SdlConnection/SdlSession.java
index c029e62b6..7f1359963 100644
--- a/sdl_android_lib/src/com/smartdevicelink/SdlConnection/SdlSession.java
+++ b/sdl_android_lib/src/com/smartdevicelink/SdlConnection/SdlSession.java
@@ -11,6 +11,7 @@ import com.smartdevicelink.protocol.heartbeat.IHeartbeatMonitor;
import com.smartdevicelink.protocol.heartbeat.IHeartbeatMonitorListener;
import com.smartdevicelink.proxy.LockScreenManager;
import com.smartdevicelink.transport.BaseTransportConfig;
+import com.smartdevicelink.transport.MultiplexTransport;
import com.smartdevicelink.transport.enums.TransportType;
public class SdlSession implements ISdlConnectionListener, IHeartbeatMonitorListener {
@@ -183,8 +184,9 @@ public class SdlSession implements ISdlConnectionListener, IHeartbeatMonitorList
this.sessionListener.onProtocolSessionStarted(sessionType, sessionID, version, correlationID, hashID);
//if (version == 3)
initialiseSession();
- if (sessionType.eq(SessionType.RPC) )
+ if (sessionType.eq(SessionType.RPC)){
sessionHashId = hashID;
+ }
}
@Override
@@ -245,4 +247,31 @@ public class SdlSession implements ISdlConnectionListener, IHeartbeatMonitorList
public void onProtocolServiceDataACK(SessionType sessionType, byte sessionID) {
this.sessionListener.onProtocolServiceDataACK(sessionType, sessionID);
}
+
+ public void clearConnection(){
+ _sdlConnection = null;
+ }
+
+ public void checkForOpenMultiplexConnection(SdlConnection connection){
+ removeConnection(connection);
+ connection.unregisterSession(this);
+ _sdlConnection = null;
+ for (SdlConnection c : shareConnections) {
+ if (c.getCurrentTransportType() == TransportType.MULTIPLEX) {
+ if(c.getIsConnected() || ((MultiplexTransport)c._transport).isPendingConnected()){
+ _sdlConnection = c;
+ try {
+ _sdlConnection.registerSession(this);//Handshake will start when register.
+ } catch (SdlException e) {
+ e.printStackTrace();
+ }
+ return;
+ }
+
+ }
+ }
+ }
+ public static boolean removeConnection(SdlConnection connection){
+ return shareConnections.remove(connection);
+ }
}
diff --git a/sdl_android_lib/src/com/smartdevicelink/protocol/AbstractProtocol.java b/sdl_android_lib/src/com/smartdevicelink/protocol/AbstractProtocol.java
index 6af53e26b..bcc2e1df5 100644
--- a/sdl_android_lib/src/com/smartdevicelink/protocol/AbstractProtocol.java
+++ b/sdl_android_lib/src/com/smartdevicelink/protocol/AbstractProtocol.java
@@ -23,10 +23,6 @@ public abstract class AbstractProtocol {
_protocolListener = protocolListener;
} // end-ctor
- // This method receives raw bytes as they arrive from transport. Those bytes
- // are then collected by the protocol and assembled into complete messages and
- // handled internally by the protocol or propagated to the protocol listener.
- public abstract void HandleReceivedBytes(byte[] receivedBytes, int length);
// This method receives a protocol message (e.g. RPC, BULK, etc.) and processes
// it for transmission over the transport. The results of this processing will
@@ -36,6 +32,9 @@ public abstract class AbstractProtocol {
// over which to send the message, etc.
public abstract void SendMessage(ProtocolMessage msg);
+
+ public abstract void handlePacketReceived(SdlPacket packet);
+
// This method starts a protocol session. A corresponding call to the protocol
// listener onProtocolSessionStarted() method will be made when the protocol
// session has been established.
@@ -63,11 +62,11 @@ public abstract class AbstractProtocol {
public abstract void SendHeartBeatACK(byte sessionID);
// This method is called whenever the protocol receives a complete frame
- protected void handleProtocolFrameReceived(ProtocolFrameHeader header, byte[] data, MessageFrameAssembler assembler) {
- SdlTrace.logProtocolEvent(InterfaceActivityDirection.Receive, header, data,
- 0, data.length, SDL_LIB_TRACE_KEY);
+ protected void handleProtocolFrameReceived(SdlPacket packet, MessageFrameAssembler assembler) {
+ //FIXME SdlTrace.logProtocolEvent(InterfaceActivityDirection.Receive, header, data,
+ // 0, packet.dataSize, SDL_LIB_TRACE_KEY);
- assembler.handleFrame(header, data);
+ assembler.handleFrame(packet);
}
private synchronized void resetOutgoingHeartbeat(SessionType sessionType, byte sessionID) {
@@ -83,26 +82,25 @@ public abstract class AbstractProtocol {
}
// This method is called whenever a protocol has an entire frame to send
- protected void handleProtocolFrameToSend(ProtocolFrameHeader header, byte[] data, int offset, int length) {
- SdlTrace.logProtocolEvent(InterfaceActivityDirection.Transmit, header, data,
- offset, length, SDL_LIB_TRACE_KEY);
- resetOutgoingHeartbeat(header.getSessionType(), header.getSessionID());
+ /**
+ * SdlPacket should have included payload at this point.
+ * @param header
+ */
+ protected void handlePacketToSend(SdlPacket header) {
+ //FIXME SdlTrace.logProtocolEvent(InterfaceActivityDirection.Transmit, header, data,
+ // offset, length, SDL_LIB_TRACE_KEY);
+ resetOutgoingHeartbeat(SessionType.valueOf((byte)header.getServiceType()), (byte)header.getSessionId());
+
synchronized(_frameLock) {
- byte[] frameHeader = header.assembleHeaderBytes();
- handleProtocolMessageBytesToSend(frameHeader, 0, frameHeader.length);
- if (data != null) {
- handleProtocolMessageBytesToSend(data, offset, length);
- } // end-if
+ //byte[] frameHeader = header.constructPacket();
+ if(header!=null){
+ _protocolListener.onProtocolMessageBytesToSend(header);
+ }//TODO else log out error
+
}
}
-
- // This method handles protocol message bytes that are ready to send.
- // A callback is sent to the protocol listener.
- protected void handleProtocolMessageBytesToSend(byte[] bytesToSend,
- int offset, int length) {
- _protocolListener.onProtocolMessageBytesToSend(bytesToSend, offset, length);
- }
+
// This method handles received protocol messages.
protected void handleProtocolMessageReceived(ProtocolMessage message) {
diff --git a/sdl_android_lib/src/com/smartdevicelink/protocol/BinaryFrameHeader.java b/sdl_android_lib/src/com/smartdevicelink/protocol/BinaryFrameHeader.java
index 3f40509ab..b3c21a340 100644
--- a/sdl_android_lib/src/com/smartdevicelink/protocol/BinaryFrameHeader.java
+++ b/sdl_android_lib/src/com/smartdevicelink/protocol/BinaryFrameHeader.java
@@ -43,7 +43,7 @@ public class BinaryFrameHeader {
return msg;
}
- protected byte[] assembleHeaderBytes() {
+ public byte[] assembleHeaderBytes() {
int binHeader = _functionID;
// reset the 4 leftmost bits, for _rpcType
binHeader &= 0xFFFFFFFF >>> 4;
diff --git a/sdl_android_lib/src/com/smartdevicelink/protocol/IProtocolListener.java b/sdl_android_lib/src/com/smartdevicelink/protocol/IProtocolListener.java
index 7efebf0e8..47317a086 100644
--- a/sdl_android_lib/src/com/smartdevicelink/protocol/IProtocolListener.java
+++ b/sdl_android_lib/src/com/smartdevicelink/protocol/IProtocolListener.java
@@ -5,7 +5,7 @@ import com.smartdevicelink.protocol.enums.*;
public interface IProtocolListener {
// Called to indicate that these bytes are to be sent as part of a message.
// This call includes the part of the message.
- void onProtocolMessageBytesToSend(byte[] msgBytes, int offset, int length);
+ void onProtocolMessageBytesToSend(SdlPacket packet);
// Called to indicate that a complete message (RPC, BULK, etc.) has been
// received. This call includes the message.
diff --git a/sdl_android_lib/src/com/smartdevicelink/protocol/ProtocolFrameHeader.java b/sdl_android_lib/src/com/smartdevicelink/protocol/ProtocolFrameHeader.java
deleted file mode 100644
index 09609fadf..000000000
--- a/sdl_android_lib/src/com/smartdevicelink/protocol/ProtocolFrameHeader.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package com.smartdevicelink.protocol;
-
-import com.smartdevicelink.protocol.enums.FrameType;
-import com.smartdevicelink.protocol.enums.SessionType;
-import com.smartdevicelink.util.BitConverter;
-
-public class ProtocolFrameHeader {
- private byte version = 1;
- private boolean compressed = false;
- private FrameType frameType = FrameType.Control;
- private SessionType sessionType = SessionType.RPC;
- private byte frameData = 0;
- private byte sessionID;
- private int dataSize;
- private int messageID;
-
- public static final byte FrameDataSingleFrame = 0x00;
- public static final byte FrameDataFirstFrame = 0x00;
- public static final byte FrameDataFinalConsecutiveFrame = 0x00;
-
- public ProtocolFrameHeader() {}
-
- public static ProtocolFrameHeader parseWiProHeader(byte[] header) {
- ProtocolFrameHeader msg = new ProtocolFrameHeader();
-
- byte version = (byte) (header[0] >>> 4);
- msg.setVersion(version);
-
- boolean compressed = 1 == ((header[0] & 0x08) >>> 3);
- msg.setCompressed(compressed);
-
- byte frameType = (byte) (header[0] & 0x07);
- msg.setFrameType(FrameType.valueOf(frameType));
-
- byte serviceType = header[1];
- msg.setSessionType(SessionType.valueOf(serviceType));
-
- byte frameData = header[2];
- msg.setFrameData(frameData);
-
- byte sessionID = header[3];
- msg.setSessionID(sessionID);
-
- int dataSize = BitConverter.intFromByteArray(header, 4);
- msg.setDataSize(dataSize);
-
- if (version > 1) {
- int messageID = BitConverter.intFromByteArray(header, 8);
- msg.setMessageID(messageID);
- } else msg.setMessageID(0);
-
- return msg;
- }
-
- protected byte[] assembleHeaderBytes() {
- int header = 0;
- header |= (version & 0x0F);
- header <<= 1;
- header |= (compressed ? 1 : 0);
- header <<= 3;
- header |= (frameType.value() & 0x07);
- header <<= 8;
- header |= (sessionType.value() & 0xFF);
- header <<= 8;
- header |= (frameData & 0xFF);
- header <<= 8;
- header |= (sessionID & 0xFF);
-
- if (version == 1) {
- byte[] ret = new byte[8];
- System.arraycopy(BitConverter.intToByteArray(header), 0, ret, 0, 4);
- System.arraycopy(BitConverter.intToByteArray(dataSize), 0, ret, 4, 4);
-
- return ret;
- } else if (version > 1) {
- byte[] ret = new byte[12];
- System.arraycopy(BitConverter.intToByteArray(header), 0, ret, 0, 4);
- System.arraycopy(BitConverter.intToByteArray(dataSize), 0, ret, 4, 4);
- System.arraycopy(BitConverter.intToByteArray(messageID), 0, ret, 8, 4);
-
- return ret;
- } else return null;
- }
-
- public String toString() {
- String ret = "";
- ret += "version " + version + ", " + (compressed ? "compressed" : "uncompressed") + "\n";
- ret += "frameType " + frameType + ", serviceType " + sessionType;
- ret += "\nframeData " + frameData;
- ret += ", sessionID " + sessionID;
- ret += ", dataSize " + dataSize;
- ret += ", messageID " + messageID;
- return ret;
- }
-
- public byte getVersion() {
- return version;
- }
-
- public void setVersion(byte version) {
- this.version = version;
- }
-
- public boolean isCompressed() {
- return compressed;
- }
-
- public void setCompressed(boolean compressed) {
- this.compressed = compressed;
- }
-
- public byte getFrameData() {
- return frameData;
- }
-
- public void setFrameData(byte frameData) {
- this.frameData = frameData;
- }
-
- public byte getSessionID() {
- return sessionID;
- }
-
- public void setSessionID(byte sessionID) {
- this.sessionID = sessionID;
- }
-
- public int getDataSize() {
- return dataSize;
- }
-
- public void setDataSize(int dataSize) {
- this.dataSize = dataSize;
- }
-
- public int getMessageID() {
- return messageID;
- }
-
- public void setMessageID(int messageID) {
- this.messageID = messageID;
- }
-
- public FrameType getFrameType() {
- return frameType;
- }
-
- public void setFrameType(FrameType frameType) {
- this.frameType = frameType;
- }
-
- public SessionType getSessionType() {
- return sessionType;
- }
-
- public void setSessionType(SessionType sessionType) {
- this.sessionType = sessionType;
- }
-}
diff --git a/sdl_android_lib/src/com/smartdevicelink/protocol/ProtocolFrameHeaderFactory.java b/sdl_android_lib/src/com/smartdevicelink/protocol/ProtocolFrameHeaderFactory.java
deleted file mode 100644
index 79b9bdda5..000000000
--- a/sdl_android_lib/src/com/smartdevicelink/protocol/ProtocolFrameHeaderFactory.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package com.smartdevicelink.protocol;
-
-import com.smartdevicelink.protocol.enums.*;
-
-public class ProtocolFrameHeaderFactory {
-
- public static ProtocolFrameHeader createStartSession(SessionType serviceType, int messageID, byte version, byte sessionID) {
- ProtocolFrameHeader msg = new ProtocolFrameHeader();
- msg.setVersion(version);
- msg.setFrameType(FrameType.Control);
- msg.setSessionType(serviceType);
- msg.setFrameData(FrameDataControlFrameType.StartSession.value());
- msg.setMessageID(messageID);
- msg.setSessionID(sessionID);
-
- return msg;
- }
-
- public static ProtocolFrameHeader createHeartbeat(SessionType serviceType, byte sessionID,
- byte version) {
- return createControlFrame(serviceType, sessionID, version,
- FrameDataControlFrameType.Heartbeat);
- }
-
- public static ProtocolFrameHeader createHeartbeatACK(
- SessionType serviceType, byte sessionID, byte version) {
- return createControlFrame(serviceType, sessionID, version,
- FrameDataControlFrameType.HeartbeatACK);
- }
-
- private static ProtocolFrameHeader createControlFrame(SessionType serviceType, byte sessionID, byte version,
- FrameDataControlFrameType frameData) {
- ProtocolFrameHeader msg = new ProtocolFrameHeader();
- msg.setVersion(version);
- msg.setFrameType(FrameType.Control);
- msg.setSessionType(serviceType);
- msg.setSessionID(sessionID);
- msg.setFrameData(frameData.value());
- return msg;
- }
-
- public static ProtocolFrameHeader createStartSessionACK(SessionType serviceType, byte sessionID, int messageID, byte version) {
- ProtocolFrameHeader msg = new ProtocolFrameHeader();
- msg.setVersion(version);
- msg.setFrameType(FrameType.Control);
- msg.setSessionType(serviceType);
- msg.setSessionID(sessionID);
- msg.setFrameData(FrameDataControlFrameType.StartSessionACK.value());
- msg.setMessageID(messageID);
-
- return msg;
- }
-
- public static ProtocolFrameHeader createStartSessionNACK(SessionType serviceType, byte sessionID, int messageID, byte version) {
- ProtocolFrameHeader msg = new ProtocolFrameHeader();
- msg.setVersion(version);
- msg.setFrameType(FrameType.Control);
- msg.setSessionType(serviceType);
- msg.setSessionID(sessionID);
- msg.setFrameData(FrameDataControlFrameType.StartSessionNACK.value());
- msg.setMessageID(messageID);
-
- return msg;
- }
-
- public static ProtocolFrameHeader createEndSession(SessionType serviceType, byte sessionID, int messageID, byte version, int dataSize) {
- ProtocolFrameHeader msg = new ProtocolFrameHeader();
- msg.setVersion(version);
- msg.setFrameType(FrameType.Control);
- msg.setSessionType(serviceType);
- msg.setSessionID(sessionID);
- msg.setDataSize(dataSize);
- msg.setFrameData(FrameDataControlFrameType.EndSession.value());
- msg.setMessageID(messageID);
-
- return msg;
- }
-
- public static ProtocolFrameHeader createSingleSendData(SessionType serviceType, byte sessionID,
- int dataLength, int messageID, byte version) {
- ProtocolFrameHeader msg = new ProtocolFrameHeader();
- msg.setVersion(version);
- msg.setFrameType(FrameType.Single);
- msg.setSessionType(serviceType);
- msg.setFrameData(ProtocolFrameHeader.FrameDataSingleFrame);
- msg.setSessionID(sessionID);
- msg.setDataSize(dataLength);
- msg.setMessageID(messageID);
-
- return msg;
- }
-
- public static ProtocolFrameHeader createMultiSendDataFirst(SessionType serviceType, byte sessionID,
- int messageID, byte version) {
- ProtocolFrameHeader msg = new ProtocolFrameHeader();
- msg.setVersion(version);
- msg.setFrameType(FrameType.First);
- msg.setSessionType(serviceType);
- msg.setFrameData(ProtocolFrameHeader.FrameDataFirstFrame);
- msg.setSessionID(sessionID);
- msg.setDataSize(8);
- msg.setMessageID(messageID);
-
- return msg;
- }
-
- public static ProtocolFrameHeader createMultiSendDataRest(SessionType serviceType, byte sessionID,
- int dataLength, byte frameSequenceNumber, int messageID, byte version) {
- ProtocolFrameHeader msg = new ProtocolFrameHeader();
- msg.setVersion(version);
- msg.setFrameType(FrameType.Consecutive);
- msg.setSessionType(serviceType);
- msg.setFrameData(frameSequenceNumber/*FrameData.ConsecutiveFrame.value()*/);
- msg.setSessionID(sessionID);
- msg.setDataSize(dataLength);
- msg.setMessageID(messageID);
-
- return msg;
- }
-
- public static ProtocolFrameHeader createMultiSendDataRest(SessionType serviceType, byte sessionID,
- int dataLength, int messageID, byte version) {
- ProtocolFrameHeader msg = new ProtocolFrameHeader();
- msg.setVersion(version);
- msg.setFrameType(FrameType.Consecutive);
- msg.setSessionType(serviceType);
- msg.setFrameData(FrameData.ConsecutiveFrame.value());
- msg.setSessionID(sessionID);
- msg.setDataSize(dataLength);
- msg.setMessageID(messageID);
-
- return msg;
- }
-
- public static BinaryFrameHeader createBinaryFrameHeader(byte rpcType, int functionID, int corrID, int jsonSize) {
- BinaryFrameHeader msg = new BinaryFrameHeader();
- msg.setRPCType(rpcType);
- msg.setFunctionID(functionID);
- msg.setCorrID(corrID);
- msg.setJsonSize(jsonSize);
-
- return msg;
- }
-}
diff --git a/sdl_android_lib/src/com/smartdevicelink/protocol/ProtocolMessage.java b/sdl_android_lib/src/com/smartdevicelink/protocol/ProtocolMessage.java
index c63f560d5..5d0e0fc5d 100644
--- a/sdl_android_lib/src/com/smartdevicelink/protocol/ProtocolMessage.java
+++ b/sdl_android_lib/src/com/smartdevicelink/protocol/ProtocolMessage.java
@@ -12,7 +12,8 @@ public class ProtocolMessage {
private int _functionID;
private int _correlationID;
private int _jsonSize;
-
+ int priorityCoefficient = 0;
+
private byte[] _data = null;
private byte[] _bulkData = null;
@@ -122,4 +123,14 @@ public class ProtocolMessage {
public void setJsonSize(int _jsonSize) {
this._jsonSize = _jsonSize;
}
+ /**
+ * Set the priority for this packet. The lower the number the higher the priority. <br>0 is the highest priority and the default.
+ * @param priority
+ */
+ public void setPriorityCoefficient(int priority){
+ this.priorityCoefficient = priority;
+ }
+ public int getPrioirtyCoefficient(){
+ return this.priorityCoefficient;
+ }
} // end-class \ No newline at end of file
diff --git a/sdl_android_lib/src/com/smartdevicelink/protocol/SdlPacket.aidl b/sdl_android_lib/src/com/smartdevicelink/protocol/SdlPacket.aidl
new file mode 100644
index 000000000..cec80021e
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/protocol/SdlPacket.aidl
@@ -0,0 +1,5 @@
+package com.smartdevicelink.protocol;
+//Seems silly, but....welcome to Android
+//We need to do this so the compiler knows that the SdlPacket.java class ia actually a parceable object and how to handle it
+// when dealing with other interface (aidl) files as well as being able to send them through intents
+parcelable SdlPacket; \ No newline at end of file
diff --git a/sdl_android_lib/src/com/smartdevicelink/protocol/SdlPacket.java b/sdl_android_lib/src/com/smartdevicelink/protocol/SdlPacket.java
new file mode 100644
index 000000000..f6a24d5e3
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/protocol/SdlPacket.java
@@ -0,0 +1,349 @@
+package com.smartdevicelink.protocol;
+
+import java.nio.ByteBuffer;
+
+import com.smartdevicelink.protocol.enums.FrameType;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class SdlPacket implements Parcelable{
+
+
+ public static final int HEADER_SIZE = 12;
+ public static final int HEADER_SIZE_V1 = 8;//Backwards
+
+ private static final int COMPRESSION_MASK = 0x08; //4th lowest bit
+
+ public static final int FRAME_TYPE_CONTROL = 0x00;
+ public static final int FRAME_TYPE_SINGLE = 0x01;
+ public static final int FRAME_TYPE_FIRST = 0x02;
+ public static final int FRAME_TYPE_CONSECUTIVE = 0x03;
+
+ /*
+ * Service Type
+ */
+ public static final int SERVICE_TYPE_CONTROL = 0x00;
+ //RESERVED 0x01 - 0x06
+ public static final int SERVICE_TYPE_RPC = 0x07;
+ //RESERVED 0x08 - 0x09
+ public static final int SERVICE_TYPE_PCM = 0x0A;
+ public static final int SERVICE_TYPE_VIDEO = 0x0B;
+ //RESERVED 0x0C - 0x0E
+ public static final int SERVICE_TYPE_BULK_DATA = 0x0F;
+ //RESERVED 0x10 - 0xFF
+
+
+ /*
+ * Frame Info
+ */
+ //Control Frame Info
+ public static final int FRAME_INFO_HEART_BEAT = 0x00;
+ public static final int FRAME_INFO_START_SERVICE = 0x01;
+ public static final int FRAME_INFO_START_SERVICE_ACK = 0x02;
+ public static final int FRAME_INFO_START_SERVICE_NAK = 0x03;
+ public static final int FRAME_INFO_END_SERVICE = 0x04;
+ public static final int FRAME_INFO_END_SERVICE_ACK = 0x05;
+ public static final int FRAME_INFO_END_SERVICE_NAK = 0x06;
+ //0x07-0xFD are reserved
+ public static final int FRAME_INFO_SERVICE_DATA_ACK = 0xFE;
+ public static final int FRAME_INFO_HEART_BEAT_ACK = 0xFF;
+
+ public static final int FRAME_INFO_FINAL_CONNESCUTIVE_FRAME = 0x00;
+
+ //Most others
+ public static final int FRAME_INFO_RESERVED = 0x00;
+
+
+ int version;
+ boolean compression;
+ int frameType;
+ int serviceType;
+ int frameInfo;
+ int sessionId;
+ int dataSize;
+ int messageId;
+ int priorityCoefficient;
+ byte[] payload = null;
+
+ public SdlPacket(int version, boolean compression, int frameType,
+ int serviceType, int frameInfo, int sessionId,
+ int dataSize, int messageId, byte[] payload) {
+ this.version = version;
+ this.compression = compression;
+ this.frameType = frameType;
+ this.serviceType = serviceType;
+ this.frameInfo = frameInfo;
+ this.sessionId = sessionId;
+ this.dataSize = dataSize;
+ this.messageId = messageId;
+ this.priorityCoefficient = 0;
+ if(payload!=null){
+ this.payload = new byte[payload.length];
+ System.arraycopy(payload, 0, this.payload, 0, payload.length);
+ }
+ }
+
+ public SdlPacket(int version, boolean compression, int frameType,
+ int serviceType, int frameInfo, int sessionId,
+ int dataSize, int messageId, byte[] payload, int offset,int bytesToWrite) {
+ this.version = version;
+ this.compression = compression;
+ this.frameType = frameType;
+ this.serviceType = serviceType;
+ this.frameInfo = frameInfo;
+ this.sessionId = sessionId;
+ this.dataSize = dataSize;
+ this.messageId = messageId;
+ this.priorityCoefficient = 0;
+ if(payload!=null){
+ this.payload = new byte[bytesToWrite];
+ System.arraycopy(payload, offset, this.payload, 0, bytesToWrite);
+ }
+ }
+ /**
+ * This constructor is available as a protected method. A few defaults have been set, however a few things <b>MUST</b> be set before use. The rest will "work"
+ * however, it won't be valid data.
+ *
+ * <p>Frame Type
+ * <p>Service Type
+ * <p>Frame Info
+ * <p>
+ */
+ protected SdlPacket(){
+ //Package only empty constructor
+ //TODO add defaults
+ this.version = 1;
+ this.compression = false;
+ this.frameType = -1; //This NEEDS to be set
+ this.serviceType = -1;
+ this.frameInfo = -1;
+ this.sessionId = 0;
+ this.dataSize = 0;
+ this.messageId = 0;
+
+ }
+
+ /**
+ * Creates a new packet based on previous packet definitions
+ * @param packet
+ */
+ protected SdlPacket(SdlPacket packet){
+ this.version = packet.version;
+ this.compression = packet.compression;
+ this.frameType = packet.frameType;
+ this.serviceType = packet.serviceType;
+ this.frameInfo = packet.frameInfo;
+ this.sessionId = packet.sessionId;
+ this.dataSize = 0;
+ this.messageId = 0;
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public boolean isCompression() {
+ return compression;
+ }
+
+ public FrameType getFrameType() {
+ switch(frameType){
+ case FRAME_TYPE_CONTROL:
+ return FrameType.Control;
+ case FRAME_TYPE_FIRST:
+ return FrameType.First;
+ case FRAME_TYPE_CONSECUTIVE:
+ return FrameType.Consecutive;
+ case FRAME_TYPE_SINGLE:
+ default:
+ return FrameType.Single;
+ }
+ }
+
+ public int getServiceType() {
+ return serviceType;
+ }
+
+ public int getFrameInfo() {
+ return frameInfo;
+ }
+
+ public int getSessionId() {
+ return sessionId;
+ }
+
+ public int getMessageId() {
+ return messageId;
+ }
+
+ public long getDataSize() {
+ return dataSize;
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+
+ public byte[] constructPacket(){
+ return constructPacket(version, compression, frameType,
+ serviceType, frameInfo, sessionId,
+ dataSize, messageId, payload);
+ }
+ public void setPayload(byte[] bytes){
+ this.payload = bytes;
+ }
+ /**
+ * Set the priority for this packet. The lower the number the higher the priority. <br>0 is the highest priority and the default.
+ * @param priority
+ */
+ public void setPriorityCoefficient(int priority){
+ this.priorityCoefficient = priority;
+ }
+ public int getPrioirtyCoefficient(){
+ return this.priorityCoefficient;
+ }
+ /**
+ * This method takes in the various components to the SDL packet structure and creates a new byte array that can be sent via the transport
+ * @param version
+ * @param compression
+ * @param frameType
+ * @param serviceType
+ * @param controlFrameInfo
+ * @param sessionId
+ * @param dataSize
+ * @param messageId
+ * @param payload
+ * @return
+ */
+ public static byte[] constructPacket(int version, boolean compression, int frameType,
+ int serviceType, int controlFrameInfo, int sessionId,
+ int dataSize, int messageId, byte[] payload){
+ ByteBuffer builder;
+ switch(version){
+ case 1:
+ builder = ByteBuffer.allocate(HEADER_SIZE_V1 + dataSize);
+ break;
+ default:
+ builder = ByteBuffer.allocate(HEADER_SIZE + dataSize);
+ break;
+ }
+
+ builder.put((byte)((version<<4) + getCompressionBit(compression) + frameType));
+ builder.put((byte)serviceType);
+ builder.put((byte)controlFrameInfo);
+ builder.put((byte)sessionId);
+
+ builder.put((byte)((dataSize&0xFF000000)>>24));
+ builder.put((byte)((dataSize&0x00FF0000)>>16));
+ builder.put((byte)((dataSize&0x0000FF00)>>8));
+ builder.put((byte)((dataSize&0x000000FF)));
+
+ if(version>1){ //Version 1 did not include this part of the header
+ builder.put((byte)((messageId&0xFF000000)>>24));
+ builder.put((byte)((messageId&0x00FF0000)>>16));
+ builder.put((byte)((messageId&0x0000FF00)>>8));
+ builder.put((byte)((messageId&0x000000FF)));
+ }
+
+ if(payload!=null && payload.length>0){
+ builder.put(payload);
+ }
+
+ return builder.array();
+ }
+
+
+ public static int getCompressionBit(boolean compression){
+ if(compression){
+ return COMPRESSION_MASK;
+ }else{
+ return 0;
+ }
+ }
+
+
+
+@Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("***** Sdl Packet ******");
+ builder.append( "\nVersion: " +version);
+ builder.append( "\nCompression: " +compression);
+ builder.append( "\nFrameType: " +frameType);
+ builder.append( "\nServiceType: " +serviceType);
+ builder.append( "\nFrameInfo: " +frameInfo);
+ builder.append( "\nSessionId: " +sessionId);
+ builder.append( "\nDataSize: " +dataSize);
+ if(version>1){
+ builder.append( "\nMessageId: " +messageId);
+ }
+ builder.append("\n***** Sdl Packet End******");
+
+
+ return builder.toString();
+ }
+
+
+
+ /* ***************************************************************************************************************************************************
+ * *********************************************************** Parceable Overrides *****************************************************************
+ *****************************************************************************************************************************************************/
+
+
+ //I think this is FIFO...right?
+ public SdlPacket(Parcel p) {
+ this.version = p.readInt();
+ this.compression = (p.readInt() == 0) ? false : true;
+ this.frameType = p.readInt();
+ this.serviceType = p.readInt();
+ this.frameInfo = p.readInt();
+ this.sessionId = p.readInt();
+ this.dataSize = p.readInt();
+ this.messageId = p.readInt();
+ if(p.readInt() == 1){ //We should have a payload attached
+ payload = new byte[dataSize];
+ p.readByteArray(payload);
+ }
+ this.priorityCoefficient = p.readInt();
+ }
+
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+
+ dest.writeInt(version);
+ dest.writeInt(compression? 1 : 0);
+ dest.writeInt(frameType);
+ dest.writeInt(serviceType);
+ dest.writeInt(frameInfo);
+ dest.writeInt(sessionId);
+ dest.writeInt(dataSize);
+ dest.writeInt(messageId);
+ dest.writeInt(payload!=null? 1 : 0);
+ if(payload!=null){
+ dest.writeByteArray(payload);
+ }
+ dest.writeInt(priorityCoefficient);
+
+ }
+
+ public static final Parcelable.Creator<SdlPacket> CREATOR = new Parcelable.Creator<SdlPacket>() {
+ public SdlPacket createFromParcel(Parcel in) {
+ return new SdlPacket(in);
+ }
+
+ @Override
+ public SdlPacket[] newArray(int size) {
+ return new SdlPacket[size];
+ }
+
+ };
+
+
+}
diff --git a/sdl_android_lib/src/com/smartdevicelink/protocol/SdlPacketFactory.java b/sdl_android_lib/src/com/smartdevicelink/protocol/SdlPacketFactory.java
new file mode 100644
index 000000000..1fff2b1a2
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/protocol/SdlPacketFactory.java
@@ -0,0 +1,92 @@
+package com.smartdevicelink.protocol;
+
+import com.smartdevicelink.protocol.enums.FrameDataControlFrameType;
+import com.smartdevicelink.protocol.enums.SessionType;
+
+public class SdlPacketFactory {
+
+ /*
+ * public SdlPacket(int version, boolean compression, int frameType,
+ int serviceType, int frameInfo, int sessionId,
+ int dataSize, int messageId, byte[] payload) {
+ */
+ public static SdlPacket createStartSession(SessionType serviceType, int messageID, byte version, byte sessionID) {
+ SdlPacket packet = new SdlPacket(version,false,SdlPacket.FRAME_TYPE_CONTROL,
+ serviceType.getValue(),SdlPacket.FRAME_INFO_START_SERVICE,sessionID,
+ 0,messageID,null);
+
+ return packet;
+ }
+
+ public static SdlPacket createHeartbeat(SessionType serviceType, byte sessionID, byte version) {
+
+ return new SdlPacket(version,false,SdlPacket.FRAME_TYPE_CONTROL,
+ serviceType.getValue(),FrameDataControlFrameType.Heartbeat.value(),sessionID,
+ 0,0,null);
+
+ }
+
+ public static SdlPacket createHeartbeatACK(SessionType serviceType, byte sessionID, byte version) {
+ return new SdlPacket(version,false,SdlPacket.FRAME_TYPE_CONTROL,
+ serviceType.getValue(),FrameDataControlFrameType.HeartbeatACK.value(),sessionID,
+ 0,0,null);
+ }
+
+ public static SdlPacket createStartSessionACK(SessionType serviceType, byte sessionID, int messageID, byte version) {
+
+ return new SdlPacket(version,false,SdlPacket.FRAME_TYPE_CONTROL,
+ serviceType.getValue(),FrameDataControlFrameType.StartSessionACK.value(),sessionID,
+ 0,messageID,null);
+
+ }
+
+ public static SdlPacket createStartSessionNACK(SessionType serviceType, byte sessionID, int messageID, byte version) {
+
+ return new SdlPacket(version,false,SdlPacket.FRAME_TYPE_CONTROL,
+ serviceType.getValue(),SdlPacket.FRAME_INFO_START_SERVICE_NAK,sessionID,
+ 0,messageID,null);
+ }
+
+ public static SdlPacket createEndSession(SessionType serviceType, byte sessionID, int messageID, byte version, byte[] payload) {
+ return new SdlPacket(version,false,SdlPacket.FRAME_TYPE_CONTROL,
+ serviceType.getValue(),SdlPacket.FRAME_INFO_END_SERVICE,sessionID,
+ payload.length,messageID,payload);
+ }
+
+ public static SdlPacket createSingleSendData(SessionType serviceType, byte sessionID,
+ int dataLength, int messageID, byte version, byte[] payload) {
+
+ return new SdlPacket(version,false,SdlPacket.FRAME_TYPE_SINGLE,
+ serviceType.getValue(),0,sessionID,
+ payload.length,messageID,payload);
+ }
+
+ public static SdlPacket createMultiSendDataFirst(SessionType serviceType, byte sessionID,
+ int messageID, byte version, byte[] payload) {
+
+ return new SdlPacket(version,false,SdlPacket.FRAME_TYPE_FIRST,
+ serviceType.getValue(),0,sessionID,
+ 8,messageID,payload);
+
+ }
+
+ public static SdlPacket createMultiSendDataRest(SessionType serviceType, byte sessionID,
+ int dataLength, byte frameSequenceNumber, int messageID, byte version, byte[] payload,int offset,int length) {
+
+ return new SdlPacket(version,false,SdlPacket.FRAME_TYPE_CONSECUTIVE,
+ serviceType.getValue(),frameSequenceNumber,sessionID,
+ length,messageID,payload,offset,length);
+ }
+
+
+ public static BinaryFrameHeader createBinaryFrameHeader(byte rpcType, int functionID, int corrID, int jsonSize) {
+ BinaryFrameHeader msg = new BinaryFrameHeader();
+ msg.setRPCType(rpcType);
+ msg.setFunctionID(functionID);
+ msg.setCorrID(corrID);
+ msg.setJsonSize(jsonSize);
+
+ return msg;
+ }
+
+}
diff --git a/sdl_android_lib/src/com/smartdevicelink/protocol/WiProProtocol.java b/sdl_android_lib/src/com/smartdevicelink/protocol/WiProProtocol.java
index a68d3f89e..284f04574 100644
--- a/sdl_android_lib/src/com/smartdevicelink/protocol/WiProProtocol.java
+++ b/sdl_android_lib/src/com/smartdevicelink/protocol/WiProProtocol.java
@@ -3,8 +3,6 @@ package com.smartdevicelink.protocol;
import java.io.ByteArrayOutputStream;
import java.util.Hashtable;
-import android.util.Log;
-
import com.smartdevicelink.exception.*;
import com.smartdevicelink.protocol.enums.*;
import com.smartdevicelink.util.BitConverter;
@@ -19,13 +17,7 @@ public class WiProProtocol extends AbstractProtocol {
private static int HEADER_SIZE = 8;
private static int MAX_DATA_SIZE = V1_V2_MTU_SIZE - HEADER_SIZE;
- boolean _haveHeader = false;
- byte[] _headerBuf = new byte[HEADER_SIZE];
- int _headerBufWritePos = 0;
- ProtocolFrameHeader _currentHeader = null;
- byte[] _dataBuf = null;
- int _dataBufWritePos = 0;
-
+ int hashID = 0;
int messageID = 0;
@SuppressWarnings("unused")
@@ -49,58 +41,45 @@ public class WiProProtocol extends AbstractProtocol {
public byte getVersion() {
return this._version;
}
-
+
public void setVersion(byte version) {
if (version > 4) {
this._version = 4; //protect for future, proxy only supports v4 or lower
HEADER_SIZE = 12;
MAX_DATA_SIZE = V1_V2_MTU_SIZE - HEADER_SIZE; //default to lowest size since capabilities of this version are unknown
- _headerBuf = new byte[HEADER_SIZE];
} else if (version == 4) {
this._version = version;
HEADER_SIZE = 12;
MAX_DATA_SIZE = V3_V4_MTU_SIZE; //versions 4 supports 128k MTU
- _headerBuf = new byte[HEADER_SIZE];
} else if (version == 3) {
this._version = version;
HEADER_SIZE = 12;
MAX_DATA_SIZE = V3_V4_MTU_SIZE; //versions 3 supports 128k MTU
- _headerBuf = new byte[HEADER_SIZE];
} else if (version == 2) {
this._version = version;
HEADER_SIZE = 12;
MAX_DATA_SIZE = V1_V2_MTU_SIZE - HEADER_SIZE;
- _headerBuf = new byte[HEADER_SIZE];
} else if (version == 1){
this._version = version;
HEADER_SIZE = 8;
MAX_DATA_SIZE = V1_V2_MTU_SIZE - HEADER_SIZE;
- _headerBuf = new byte[HEADER_SIZE];
}
}
public void StartProtocolSession(SessionType sessionType) {
- ProtocolFrameHeader header = ProtocolFrameHeaderFactory.createStartSession(sessionType, 0x00, _version, (byte) 0x00);
- sendFrameToTransport(header);
+ SdlPacket header = SdlPacketFactory.createStartSession(sessionType, 0x00, _version, (byte) 0x00);
+ handlePacketToSend(header);
} // end-method
private void sendStartProtocolSessionACK(SessionType sessionType, byte sessionID) {
- ProtocolFrameHeader header = ProtocolFrameHeaderFactory.createStartSessionACK(sessionType, sessionID, 0x00, _version);
- sendFrameToTransport(header);
+ SdlPacket header = SdlPacketFactory.createStartSessionACK(sessionType, sessionID, 0x00, _version);
+ handlePacketToSend(header);
} // end-method
- @Override
- public void EndProtocolService(SessionType serviceType, byte sessionID) {
- ProtocolFrameHeader header = ProtocolFrameHeaderFactory.createEndSession(serviceType, sessionID, 0x00, _version, 0x00);
- sendFrameToTransport(header);
- }
-
- @Override
public void EndProtocolSession(SessionType sessionType, byte sessionID, int hashId) {
- byte[] data = new byte[4];
- data = BitConverter.intToByteArray(hashId);
- ProtocolFrameHeader header = ProtocolFrameHeaderFactory.createEndSession(sessionType, sessionID, 0x00, _version, data.length);
- handleProtocolFrameToSend(header, data, 0, data.length);
+ SdlPacket header = SdlPacketFactory.createEndSession(sessionType, sessionID, hashID, _version, BitConverter.intToByteArray(hashId));
+ handlePacketToSend(header);
+
} // end-method
public void SendMessage(ProtocolMessage protocolMsg) {
@@ -111,11 +90,11 @@ public class WiProProtocol extends AbstractProtocol {
byte[] data = null;
if (_version > 1 && sessionType != SessionType.NAV && sessionType != SessionType.PCM) {
if (protocolMsg.getBulkData() != null) {
- data = new byte[12 + protocolMsg.getJsonSize() + protocolMsg.getBulkData().length];
+ data = new byte[12 + protocolMsg.getJsonSize() + protocolMsg.getBulkData().length];
sessionType = SessionType.BULK_DATA;
} else data = new byte[12 + protocolMsg.getJsonSize()];
BinaryFrameHeader binFrameHeader = new BinaryFrameHeader();
- binFrameHeader = ProtocolFrameHeaderFactory.createBinaryFrameHeader(protocolMsg.getRPCType(), protocolMsg.getFunctionID(), protocolMsg.getCorrID(), protocolMsg.getJsonSize());
+ binFrameHeader = SdlPacketFactory.createBinaryFrameHeader(protocolMsg.getRPCType(), protocolMsg.getFunctionID(), protocolMsg.getCorrID(), protocolMsg.getJsonSize());
System.arraycopy(binFrameHeader.assembleHeaderBytes(), 0, data, 0, 12);
System.arraycopy(protocolMsg.getData(), 0, data, 12, protocolMsg.getJsonSize());
if (protocolMsg.getBulkData() != null) {
@@ -137,7 +116,6 @@ public class WiProProtocol extends AbstractProtocol {
if (data.length > MAX_DATA_SIZE) {
messageID++;
- ProtocolFrameHeader firstHeader = ProtocolFrameHeaderFactory.createMultiSendDataFirst(sessionType, sessionID, messageID, _version);
// Assemble first frame.
int frameCount = data.length / MAX_DATA_SIZE;
@@ -150,187 +128,71 @@ public class WiProProtocol extends AbstractProtocol {
System.arraycopy(BitConverter.intToByteArray(data.length), 0, firstFrameData, 0, 4);
// Second four bytes are frame count.
System.arraycopy(BitConverter.intToByteArray(frameCount), 0, firstFrameData, 4, 4);
-
- handleProtocolFrameToSend(firstHeader, firstFrameData, 0, firstFrameData.length);
+ SdlPacket firstHeader = SdlPacketFactory.createMultiSendDataFirst(sessionType, sessionID, messageID, _version,firstFrameData);
+ firstHeader.setPriorityCoefficient(1+protocolMsg.priorityCoefficient);
+ //Send the first frame
+ handlePacketToSend(firstHeader);
int currentOffset = 0;
byte frameSequenceNumber = 0;
-
+
for (int i = 0; i < frameCount; i++) {
if (i < (frameCount - 1)) {
++frameSequenceNumber;
if (frameSequenceNumber ==
- ProtocolFrameHeader.FrameDataFinalConsecutiveFrame) {
+ SdlPacket.FRAME_INFO_FINAL_CONNESCUTIVE_FRAME) {
// we can't use 0x00 as frameSequenceNumber, because
// it's reserved for the last frame
++frameSequenceNumber;
}
} else {
- frameSequenceNumber = ProtocolFrameHeader.FrameDataFinalConsecutiveFrame;
+ frameSequenceNumber = SdlPacket.FRAME_INFO_FINAL_CONNESCUTIVE_FRAME;
} // end-if
int bytesToWrite = data.length - currentOffset;
if (bytesToWrite > MAX_DATA_SIZE) {
bytesToWrite = MAX_DATA_SIZE;
}
-
- ProtocolFrameHeader consecHeader = ProtocolFrameHeaderFactory.createMultiSendDataRest(sessionType, sessionID, bytesToWrite, frameSequenceNumber , messageID, _version);
- handleProtocolFrameToSend(consecHeader, data, currentOffset, bytesToWrite);
+ SdlPacket consecHeader = SdlPacketFactory.createMultiSendDataRest(sessionType, sessionID, bytesToWrite, frameSequenceNumber , messageID, _version,data, currentOffset, bytesToWrite);
+ consecHeader.setPriorityCoefficient(i+2+protocolMsg.priorityCoefficient);
+ handlePacketToSend(consecHeader);
currentOffset += bytesToWrite;
}
} else {
messageID++;
- ProtocolFrameHeader header = ProtocolFrameHeaderFactory.createSingleSendData(sessionType, sessionID, data.length, messageID, _version);
- handleProtocolFrameToSend(header, data, 0, data.length);
+ SdlPacket header = SdlPacketFactory.createSingleSendData(sessionType, sessionID, data.length, messageID, _version,data);
+ header.setPriorityCoefficient(protocolMsg.priorityCoefficient);
+ handlePacketToSend(header);
}
}
}
- private void sendFrameToTransport(ProtocolFrameHeader header) {
- handleProtocolFrameToSend(header, null, 0, 0);
- }
-
- public void HandleReceivedBytes(byte[] receivedBytes, int receivedBytesLength) {
-
- byte[] remainingBytes = processReceivedBytes(receivedBytes, receivedBytesLength);
- while (remainingBytes != null)
- {
- remainingBytes = processReceivedBytes(remainingBytes, remainingBytes.length);
- }
- }
-
- private byte[] processReceivedBytes(byte[] receivedBytes, int receivedBytesLength) {
- int receivedBytesReadPos = 0;
-
+ public void handlePacketReceived(SdlPacket packet){
//Check for a version difference
if (_version == 1) {
- //Nothing has been read into the buffer and version is 2
- if (_headerBufWritePos == 0 && (byte) (receivedBytes[0] >>> 4) > 1) {
- setVersion((byte) (receivedBytes[0] >>> 4));
- //Buffer has something in it and version is 2
- } else if ((byte) (_headerBuf[0] >>> 4) > 1) {
- //safe current state of the buffer and also set the new version
- byte[] tempHeader = new byte[_headerBufWritePos];
- tempHeader = _headerBuf;
- setVersion((byte) (_headerBuf[0] >>> 4));
- _headerBuf = tempHeader;
- } else if ( (_version == 1) && ( HEADER_SIZE == 12) ){
- setVersion((byte) (1));
- }
-
- }
-
- // If I don't yet know the message size, grab those bytes.
- if (!_haveHeader) {
- // If I can't get the size, just get the bytes that are there.
- int headerBytesNeeded = _headerBuf.length - _headerBufWritePos;
- if (receivedBytesLength < headerBytesNeeded) {
- System.arraycopy(receivedBytes, receivedBytesReadPos,
- _headerBuf, _headerBufWritePos, receivedBytesLength);
- _headerBufWritePos += receivedBytesLength;
- return null;
- } else {
- // If I got the size, allocate the buffer
- System.arraycopy(receivedBytes, receivedBytesReadPos,
- _headerBuf, _headerBufWritePos, headerBytesNeeded);
- _headerBufWritePos += headerBytesNeeded;
- receivedBytesReadPos += headerBytesNeeded;
- _haveHeader = true;
- _currentHeader = ProtocolFrameHeader.parseWiProHeader(_headerBuf);
-
-
- int iDataSize = _currentHeader.getDataSize();
- if (iDataSize <= MAX_DATA_SIZE) {
- _dataBuf = new byte[iDataSize];
- }
- else {
- //something is wrong with the header
- Log.e("HandleReceivedBytes", "Corrupt header found, request to allocate a byte array of size: " + iDataSize);
- Log.e("HandleReceivedBytes", "_headerBuf: " + _headerBuf.toString());
- Log.e("HandleReceivedBytes", "_currentHeader: " + _currentHeader.toString());
- Log.e("HandleReceivedBytes", "receivedBytes: " + receivedBytes.toString());
- Log.e("HandleReceivedBytes", "receivedBytesReadPos: " + receivedBytesReadPos);
- Log.e("HandleReceivedBytes", "_headerBufWritePos: " + _headerBufWritePos);
- Log.e("HandleReceivedBytes", "headerBytesNeeded: " + headerBytesNeeded);
- handleProtocolError("Error handling protocol message from sdl, header invalid.",
- new SdlException("Error handling protocol message from sdl, header invalid.", SdlExceptionCause.INVALID_HEADER));
- return null;
- }
- _dataBufWritePos = 0;
- }
- }
-
- if (_dataBuf == null)
- {
- Log.e("HandleReceivedBytes", "Error: Databuffer is null, logging debug info.");
- try
- {
- Log.e("HandleReceivedBytes", "_headerBuf: " + _headerBuf.toString());
- Log.e("HandleReceivedBytes", "_currentHeader: " + _currentHeader.toString());
- Log.e("HandleReceivedBytes", "receivedBytes: " + receivedBytes.toString());
- Log.e("HandleReceivedBytes", "receivedBytesReadPos: " + receivedBytesReadPos);
- Log.e("HandleReceivedBytes", "receivedBytesLength: " + receivedBytesLength);
- Log.e("HandleReceivedBytes", "_headerBufWritePos: " + _headerBufWritePos);
- }
- catch(NullPointerException e)
- {
- Log.e("HandleReceivedBytes", "Null Pointer Encountered: " + e);
- }
-
- handleProtocolError("Error handling protocol message from sdl, header invalid.",
- new SdlException("Error handling protocol message from sdl, data buffer is null.", SdlExceptionCause.DATA_BUFFER_NULL));
- return null;
+ setVersion((byte)packet.version);
}
-
- onResetIncomingHeartbeat(_currentHeader.getSessionType(), _currentHeader.getSessionID());
-
- int bytesLeft = receivedBytesLength - receivedBytesReadPos;
- int bytesNeeded = _dataBuf.length - _dataBufWritePos;
- // If I don't have enough bytes for the message, just grab what's there.
- if (bytesLeft < bytesNeeded) {
- System.arraycopy(receivedBytes, receivedBytesReadPos, _dataBuf,
- _dataBufWritePos, bytesLeft);
- _dataBufWritePos += bytesLeft;
- return null;
- } else {
- // Fill the buffer and call the handler!
- System.arraycopy(receivedBytes, receivedBytesReadPos, _dataBuf, _dataBufWritePos, bytesNeeded);
- receivedBytesReadPos += bytesNeeded;
+
+ MessageFrameAssembler assembler = getFrameAssemblerForFrame(packet);
+ assembler.handleFrame(packet);
- MessageFrameAssembler assembler = getFrameAssemblerForFrame(_currentHeader);
- handleProtocolFrameReceived(_currentHeader, _dataBuf, assembler);
+ onResetIncomingHeartbeat(SessionType.valueOf((byte)packet.getServiceType()), (byte)packet.getSessionId());
- // Reset all class member variables for next frame
- _dataBuf = null;
- _dataBufWritePos = 0;
- _haveHeader = false;
- _headerBuf = new byte[HEADER_SIZE];
- _currentHeader = null;
- _headerBufWritePos = 0;
-
- // If there are any bytes left, recurse.
- int moreBytesLeft = receivedBytesLength - receivedBytesReadPos;
- if (moreBytesLeft > 0) {
- byte[] moreBytes = new byte[moreBytesLeft];
- System.arraycopy(receivedBytes, receivedBytesReadPos,
- moreBytes, 0, moreBytesLeft);
- return moreBytes;
- }
- }
- return null;
}
+
+
- protected MessageFrameAssembler getFrameAssemblerForFrame(ProtocolFrameHeader header) {
- Hashtable<Integer, MessageFrameAssembler> hashSessionID = _assemblerForSessionID.get(Byte.valueOf(header.getSessionID()));
+ protected MessageFrameAssembler getFrameAssemblerForFrame(SdlPacket packet) {
+ Hashtable<Integer, MessageFrameAssembler> hashSessionID = _assemblerForSessionID.get(packet.getSessionId());
if (hashSessionID == null) {
hashSessionID = new Hashtable<Integer, MessageFrameAssembler>();
- _assemblerForSessionID.put(Byte.valueOf(header.getSessionID()), hashSessionID);
+ _assemblerForSessionID.put((byte)packet.getSessionId(), hashSessionID);
} // end-if
- MessageFrameAssembler ret = (MessageFrameAssembler) _assemblerForMessageID.get(Integer.valueOf(header.getMessageID()));
+ MessageFrameAssembler ret = (MessageFrameAssembler) _assemblerForMessageID.get(Integer.valueOf(packet.getMessageId()));
if (ret == null) {
ret = new MessageFrameAssembler();
- _assemblerForMessageID.put(Integer.valueOf(header.getMessageID()), ret);
+ _assemblerForMessageID.put(Integer.valueOf(packet.getMessageId()), ret);
} // end-if
return ret;
@@ -338,35 +200,30 @@ public class WiProProtocol extends AbstractProtocol {
protected class MessageFrameAssembler {
protected boolean hasFirstFrame = false;
- protected boolean hasSecondFrame = false;
protected ByteArrayOutputStream accumulator = null;
protected int totalSize = 0;
protected int framesRemaining = 0;
- protected void handleFirstDataFrame(ProtocolFrameHeader header, byte[] data) {
+ protected void handleFirstDataFrame(SdlPacket packet) {
//The message is new, so let's figure out how big it is.
hasFirstFrame = true;
- totalSize = BitConverter.intFromByteArray(data, 0) - HEADER_SIZE;
- framesRemaining = BitConverter.intFromByteArray(data, 4);
+ totalSize = BitConverter.intFromByteArray(packet.payload, 0) - HEADER_SIZE;
+ framesRemaining = BitConverter.intFromByteArray(packet.payload, 4);
accumulator = new ByteArrayOutputStream(totalSize);
}
- protected void handleSecondFrame(ProtocolFrameHeader header, byte[] data) {
- handleRemainingFrame(header, data);
- }
-
- protected void handleRemainingFrame(ProtocolFrameHeader header, byte[] data) {
- accumulator.write(data, 0, header.getDataSize());
- notifyIfFinished(header);
+ protected void handleRemainingFrame(SdlPacket packet) {
+ accumulator.write(packet.payload, 0, (int)packet.getDataSize());
+ notifyIfFinished(packet);
}
- protected void notifyIfFinished(ProtocolFrameHeader header) {
+ protected void notifyIfFinished(SdlPacket packet) {
//if (framesRemaining == 0) {
- if (header.getFrameType() == FrameType.Consecutive && header.getFrameData() == 0x0)
+ if (packet.getFrameType() == FrameType.Consecutive && packet.getFrameInfo() == 0x0)
{
ProtocolMessage message = new ProtocolMessage();
- message.setSessionType(header.getSessionType());
- message.setSessionID(header.getSessionID());
+ message.setSessionType(SessionType.valueOf((byte)packet.getServiceType()));
+ message.setSessionID((byte)packet.getSessionId());
//If it is WiPro 2.0 it must have binary header
if (_version > 1) {
BinaryFrameHeader binFrameHeader = BinaryFrameHeader.
@@ -377,9 +234,11 @@ public class WiProProtocol extends AbstractProtocol {
message.setCorrID(binFrameHeader.getCorrID());
if (binFrameHeader.getJsonSize() > 0) message.setData(binFrameHeader.getJsonData());
if (binFrameHeader.getBulkData() != null) message.setBulkData(binFrameHeader.getBulkData());
- } else message.setData(accumulator.toByteArray());
+ } else{
+ message.setData(accumulator.toByteArray());
+ }
- _assemblerForMessageID.remove(header.getMessageID());
+ _assemblerForMessageID.remove(packet.getMessageId());
try {
handleProtocolMessageReceived(message);
@@ -388,121 +247,119 @@ public class WiProProtocol extends AbstractProtocol {
} // end-catch
hasFirstFrame = false;
- hasSecondFrame = false;
accumulator = null;
} // end-if
} // end-method
- protected void handleMultiFrameMessageFrame(ProtocolFrameHeader header, byte[] data) {
- //if (!hasFirstFrame) {
- // hasFirstFrame = true;
- if (header.getFrameType() == FrameType.First)
- {
- handleFirstDataFrame(header, data);
+ protected void handleMultiFrameMessageFrame(SdlPacket packet) {
+ if (packet.getFrameType() == FrameType.First){
+ handleFirstDataFrame(packet);
}
-
- //} else if (!hasSecondFrame) {
- // hasSecondFrame = true;
- // framesRemaining--;
- // handleSecondFrame(header, data);
- //} else {
- // framesRemaining--;
- else
- {
- handleRemainingFrame(header, data);
+ else{
+ handleRemainingFrame(packet);
}
- //}
} // end-method
- protected void handleFrame(ProtocolFrameHeader header, byte[] data) {
- if (header.getFrameType().equals(FrameType.Control)) {
- handleControlFrame(header, data);
+ protected void handleFrame(SdlPacket packet) {
+ if (packet.getFrameType().equals(FrameType.Control)) {
+ handleControlFrame(packet);
} else {
// Must be a form of data frame (single, first, consecutive, etc.)
- if ( header.getFrameType() == FrameType.First
- || header.getFrameType() == FrameType.Consecutive
+ if ( packet.getFrameType() == FrameType.First
+ || packet.getFrameType() == FrameType.Consecutive
) {
- handleMultiFrameMessageFrame(header, data);
+ handleMultiFrameMessageFrame(packet);
} else {
- handleSingleFrameMessageFrame(header, data);
+ handleSingleFrameMessageFrame(packet);
}
} // end-if
} // end-method
- private void handleProtocolHeartbeat(ProtocolFrameHeader header,
- byte[] data) {
- WiProProtocol.this.handleProtocolHeartbeat(header.getSessionType(),header.getSessionID());
+ private void handleProtocolHeartbeatACK(SdlPacket packet) {
+ WiProProtocol.this.handleProtocolHeartbeatACK(SessionType.valueOf((byte)packet.getServiceType()),(byte)packet.getSessionId());
} // end-method
-
- private void handleProtocolHeartbeatACK(ProtocolFrameHeader header,
- byte[] data) {
- WiProProtocol.this.handleProtocolHeartbeatACK(header.getSessionType(),header.getSessionID());
+ private void handleProtocolHeartbeat(SdlPacket packet) {
+ WiProProtocol.this.handleProtocolHeartbeat(SessionType.valueOf((byte)packet.getServiceType()),(byte)packet.getSessionId());
} // end-method
-
- private void handleControlFrame(ProtocolFrameHeader header, byte[] data) {
- if (header.getFrameData() == FrameDataControlFrameType.Heartbeat.getValue()) {
- handleProtocolHeartbeat(header, data);
- } else if (header.getFrameData() == FrameDataControlFrameType.HeartbeatACK.getValue()) {
- handleProtocolHeartbeatACK(header, data);
- } else if (header.getFrameData() == FrameDataControlFrameType.StartSession.getValue()) {
- sendStartProtocolSessionACK(header.getSessionType(), header.getSessionID());
- } else if (header.getFrameData() == FrameDataControlFrameType.StartSessionACK.getValue()) {
+
+ private void handleControlFrame(SdlPacket packet) {
+ int frameInfo = packet.getFrameInfo();
+ SessionType serviceType = SessionType.valueOf((byte)packet.getServiceType());
+
+ if (frameInfo == FrameDataControlFrameType.Heartbeat.getValue()) {
+ handleProtocolHeartbeat(packet);
+ }
+ if (frameInfo == FrameDataControlFrameType.HeartbeatACK.getValue()) {
+ handleProtocolHeartbeatACK(packet);
+ }
+ else if (frameInfo == FrameDataControlFrameType.StartSession.getValue()) {
+ sendStartProtocolSessionACK(serviceType, (byte)packet.getSessionId());
+ } else if (frameInfo == FrameDataControlFrameType.StartSessionACK.getValue()) {
// Use this sessionID to create a message lock
- Object messageLock = _messageLocks.get(header.getSessionID());
+ Object messageLock = _messageLocks.get(packet.getSessionId());
if (messageLock == null) {
messageLock = new Object();
- _messageLocks.put(header.getSessionID(), messageLock);
+ _messageLocks.put((byte)packet.getSessionId(), messageLock);
}
int hashID = 0;
if (_version > 1){
- if (data != null && data.length == 4){ //hashid will be 4 bytes in length
- hashID = BitConverter.intFromByteArray(data, 0);
+ if (packet.payload!= null && packet.dataSize == 4){ //hashid will be 4 bytes in length
+ hashID = BitConverter.intFromByteArray(packet.payload, 0);
}
- }
- handleProtocolSessionStarted(header.getSessionType(), header.getSessionID(), _version, "", hashID);
- } else if (header.getFrameData() == FrameDataControlFrameType.StartSessionNACK.getValue()) {
- if (header.getSessionType().eq(SessionType.NAV) || header.getSessionType().eq(SessionType.PCM)) {
- handleProtocolSessionNACKed(header.getSessionType(), header.getSessionID(), _version, "");
+ }
+ handleProtocolSessionStarted(serviceType,(byte) packet.getSessionId(), _version, "", hashID);
+ } else if (frameInfo == FrameDataControlFrameType.StartSessionNACK.getValue()) {
+ if (serviceType.eq(SessionType.NAV) || serviceType.eq(SessionType.PCM)) {
+ handleProtocolSessionNACKed(serviceType, (byte)packet.getSessionId(), _version, "");
} else {
- handleProtocolError("Got StartSessionNACK for protocol sessionID=" + header.getSessionID(), null);
+ handleProtocolError("Got StartSessionNACK for protocol sessionID=" + packet.getSessionId(), null);
}
- } else if (header.getFrameData() == FrameDataControlFrameType.EndSession.getValue()) {
+ } else if (frameInfo == FrameDataControlFrameType.EndSession.getValue()) {
if (_version > 1) {
- handleProtocolSessionEnded(header.getSessionType(), header.getSessionID(), "");
- } else handleProtocolSessionEnded(header.getSessionType(), header.getSessionID(), "");
- } else if (header.getFrameData() == FrameDataControlFrameType.EndSessionACK.getValue()) {
- handleProtocolSessionEnded(header.getSessionType(), header.getSessionID(), "");
- } else if (header.getFrameData() == FrameDataControlFrameType.EndSessionNACK.getValue()) {
- handleProtocolSessionEndedNACK(header.getSessionType(), header.getSessionID(), "");
- } else if (header.getFrameData() == FrameDataControlFrameType.ServiceDataACK.getValue()) {
- handleProtocolServiceDataACK(header.getSessionType(), header.getSessionID());
+ handleProtocolSessionEnded(serviceType, (byte)packet.getSessionId(), "");
+ } else {
+ handleProtocolSessionEnded(serviceType, (byte)packet.getSessionId(), "");
+ }
+ } else if (frameInfo == FrameDataControlFrameType.EndSessionACK.getValue()) {
+ handleProtocolSessionEnded(serviceType, (byte)packet.getSessionId(), "");
+ } else if (frameInfo == FrameDataControlFrameType.EndSessionNACK.getValue()) {
+ handleProtocolSessionEndedNACK(serviceType, (byte)packet.getSessionId(), "");
+ } else if (frameInfo == FrameDataControlFrameType.ServiceDataACK.getValue()) {
+ handleProtocolServiceDataACK(serviceType, (byte)packet.getSessionId ());
}
} // end-method
- private void handleSingleFrameMessageFrame(ProtocolFrameHeader header, byte[] data) {
+ private void handleSingleFrameMessageFrame(SdlPacket packet) {
ProtocolMessage message = new ProtocolMessage();
- if (header.getSessionType() == SessionType.RPC) {
- message.setMessageType(MessageType.RPC);
- } else if (header.getSessionType() == SessionType.BULK_DATA) {
+ SessionType serviceType = SessionType.valueOf((byte)packet.getServiceType());
+ if (serviceType == SessionType.RPC) {
+ message.setMessageType(MessageType.RPC);
+ } else if (serviceType == SessionType.BULK_DATA) {
message.setMessageType(MessageType.BULK);
} // end-if
- message.setSessionType(header.getSessionType());
- message.setSessionID(header.getSessionID());
+ message.setSessionType(serviceType);
+ message.setSessionID((byte)packet.getSessionId());
//If it is WiPro 2.0 it must have binary header
if (_version > 1) {
BinaryFrameHeader binFrameHeader = BinaryFrameHeader.
- parseBinaryHeader(data);
+ parseBinaryHeader(packet.payload);
message.setVersion(_version);
message.setRPCType(binFrameHeader.getRPCType());
message.setFunctionID(binFrameHeader.getFunctionID());
message.setCorrID(binFrameHeader.getCorrID());
- if (binFrameHeader.getJsonSize() > 0) message.setData(binFrameHeader.getJsonData());
- if (binFrameHeader.getBulkData() != null) message.setBulkData(binFrameHeader.getBulkData());
- } else message.setData(data);
+ if (binFrameHeader.getJsonSize() > 0){
+ message.setData(binFrameHeader.getJsonData());
+ }
+ if (binFrameHeader.getBulkData() != null){
+ message.setBulkData(binFrameHeader.getBulkData());
+ }
+ } else {
+ message.setData(packet.payload);
+ }
- _assemblerForMessageID.remove(header.getMessageID());
+ _assemblerForMessageID.remove(packet.getMessageId());
try {
handleProtocolMessageReceived(message);
@@ -515,8 +372,8 @@ public class WiProProtocol extends AbstractProtocol {
@Override
public void StartProtocolService(SessionType sessionType, byte sessionID) {
- ProtocolFrameHeader header = ProtocolFrameHeaderFactory.createStartSession(sessionType, 0x00, _version, sessionID);
- sendFrameToTransport(header);
+ SdlPacket header = SdlPacketFactory.createStartSession(sessionType, 0x00, _version, sessionID);
+ handlePacketToSend(header);
}
@@ -534,13 +391,20 @@ public class WiProProtocol extends AbstractProtocol {
@Override
public void SendHeartBeat(byte sessionID) {
- final ProtocolFrameHeader heartbeat = ProtocolFrameHeaderFactory.createHeartbeat(SessionType.CONTROL, sessionID, _version);
- sendFrameToTransport(heartbeat);
+ final SdlPacket heartbeat = SdlPacketFactory.createHeartbeat(SessionType.CONTROL, sessionID, _version);
+ handlePacketToSend(heartbeat);
}
@Override
public void SendHeartBeatACK(byte sessionID) {
- final ProtocolFrameHeader heartbeat = ProtocolFrameHeaderFactory.createHeartbeatACK(SessionType.CONTROL, sessionID, _version);
- sendFrameToTransport(heartbeat);
+ final SdlPacket heartbeat = SdlPacketFactory.createHeartbeatACK(SessionType.CONTROL, sessionID, _version);
+ handlePacketToSend(heartbeat);
+ }
+
+ @Override
+ public void EndProtocolService(SessionType serviceType, byte sessionID) {
+ SdlPacket header = SdlPacketFactory.createEndSession(serviceType, sessionID, hashID, _version, new byte[4]);
+ handlePacketToSend(header);
+
}
} // end-class \ No newline at end of file
diff --git a/sdl_android_lib/src/com/smartdevicelink/proxy/RPCMessage.java b/sdl_android_lib/src/com/smartdevicelink/proxy/RPCMessage.java
index 8d3794075..dc2d44ee1 100644
--- a/sdl_android_lib/src/com/smartdevicelink/proxy/RPCMessage.java
+++ b/sdl_android_lib/src/com/smartdevicelink/proxy/RPCMessage.java
@@ -2,8 +2,6 @@ package com.smartdevicelink.proxy;
import java.util.Hashtable;
-import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
-
public class RPCMessage extends RPCStruct {
public static final String KEY_REQUEST = "request";
diff --git a/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxy.java b/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxy.java
deleted file mode 100644
index 605fb245b..000000000
--- a/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxy.java
+++ /dev/null
@@ -1,404 +0,0 @@
-package com.smartdevicelink.proxy;
-
-import java.util.Vector;
-
-import com.smartdevicelink.exception.SdlException;
-import com.smartdevicelink.exception.SdlExceptionCause;
-import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
-import com.smartdevicelink.proxy.rpc.enums.Language;
-import com.smartdevicelink.trace.SdlTrace;
-import com.smartdevicelink.transport.BTTransportConfig;
-import com.smartdevicelink.transport.BaseTransportConfig;
-
-@Deprecated
-public class SdlProxy extends SdlProxyBase<IProxyListener> {
-
- private static final String SDL_LIB_TRACE_KEY = "42baba60-eb57-11df-98cf-0800200c9a66";
- @SuppressWarnings("unused")
- private static final String SDL_LIB_PRIVATE_TOKEN = "{DAE1A88C-6C16-4768-ACA5-6F1247EA01C2}";
-
- /**
- * Constructor for the SdlProxy object, the proxy for communicating between the App and SDL.
- *
- * @param listener - Reference to the object in the App listening to callbacks from SDL.
- * @throws SdlException
- */
- @Deprecated
- public SdlProxy(IProxyListener listener) throws SdlException {
- super( listener,
- /*application context*/null,
- /*enable advanced lifecycle management*/false,
- /*app name*/ null,
- /*TTS Name*/null,
- /*ngn media screen app name*/null,
- /*vr synonyms*/null,
- /*is media app*/ null,
- /*sdlMsgVersion*/null,
- /*language desired*/null,
- /*HMI Display Language Desired*/null,
- /*App Type*/null,
- /*App ID*/null,
- /*autoActivateID*/null,
- /*callbackToUIThread*/ true,
- new BTTransportConfig());
-
- SdlTrace.logProxyEvent("Application constructed SdlProxy instance passing in: IProxyListener.", SDL_LIB_TRACE_KEY);
- }
-
- /**
- * Constructor for the SdlProxy object, the proxy for communicating between the App and SDL.
- *
- * @param listener - Reference to the object in the App listening to callbacks from SDL.
- * @param sAppName
- * @param sAppID
- * @throws SdlException
- */
- @Deprecated
- public SdlProxy(IProxyListener listener, String sAppName, String sAppID) throws SdlException {
- super( listener,
- /*application context*/null,
- /*enable advanced lifecycle management*/false,
- /*app name*/ sAppName,
- /*TTS Name*/null,
- /*ngn media screen app name*/null,
- /*vr synonyms*/null,
- /*is media app*/ null,
- /*sdlMsgVersion*/null,
- /*language desired*/null,
- /*HMI Display Language Desired*/null,
- /*App Type*/null,
- /*App ID*/sAppID,
- /*autoActivateID*/null,
- /*callbackToUIThread*/ true,
- new BTTransportConfig());
-
- SdlTrace.logProxyEvent("Application constructed SdlProxy instance passing in: IProxyListener.", SDL_LIB_TRACE_KEY);
- }
-
- /**
- * Constructor for the SdlProxy object, the proxy for communicating between the App and SDL.
- *
- * @param listener - Reference to the object in the App listening to callbacks from SDL.
- * @param sdlProxyConfigurationResources
- * @throws SdlException
- */
- @Deprecated
- public SdlProxy(IProxyListener listener, SdlProxyConfigurationResources sdlProxyConfigurationResources)
- throws SdlException {
- super( listener,
- sdlProxyConfigurationResources,
- /*enable advanced lifecycle management*/false,
- /*app name*/ null,
- /*TTS Name*/null,
- /*ngn media screen app name*/null,
- /*vr synonyms*/null,
- /*is media app*/ null,
- /*sdlMsgVersion*/null,
- /*language desired*/null,
- /*HMI Display Language Desired*/null,
- /*App Type*/null,
- /*App ID*/null,
- /*autoActivateID*/null,
- /*callbackToUIThread*/ true,
- new BTTransportConfig());
-
- SdlTrace.logProxyEvent("Application constructed SdlProxy instance passing in: IProxyListener, SdlProxyConfigurationResources.", SDL_LIB_TRACE_KEY);
- }
-
- /**
- * Constructor for the SdlProxy object, the proxy for communicating between the App and SDL.
- *
- * @param listener - Reference to the object in the App listening to callbacks from SDL.
- * @param callbackToUIThread - If true, all callbacks will occur on the UI thread.
- * @throws SdlException
- */
- @Deprecated
- public SdlProxy(IProxyListener listener, boolean callbackToUIThread) throws SdlException {
- super( listener,
- /*sdl proxy configuration resources*/null,
- /*enable advanced lifecycle management*/false,
- /*app name*/ null,
- /*TTS Name*/null,
- /*ngn media screen app name*/null,
- /*vr synonyms*/null,
- /*is media app*/ null,
- /*sdlMsgVersion*/null,
- /*language desired*/null,
- /*HMI Display Language Desired*/null,
- /*App Type*/null,
- /*App ID*/null,
- /*autoActivateID*/null,
- callbackToUIThread,
- new BTTransportConfig());
-
- SdlTrace.logProxyEvent("Application constructed SdlProxy instance passing in: IProxyListener, callBackToUIThread.", SDL_LIB_TRACE_KEY);
- }
-
- /**
- * Constructor for the SdlProxy object, the proxy for communicating between the App and SDL.
- *
- * @param listener - Reference to the object in the App listening to callbacks from SDL.
- * @param sdlProxyConfigurationResources
- * @param callbackToUIThread - If true, all callbacks will occur on the UI thread.
- * @throws SdlException
- */
- @Deprecated
- public SdlProxy(IProxyListener listener, SdlProxyConfigurationResources sdlProxyConfigurationResources,
- boolean callbackToUIThread) throws SdlException {
- super( listener,
- sdlProxyConfigurationResources,
- /*enable advanced lifecycle management*/false,
- /*app name*/ null,
- /*TTS Name*/null,
- /*ngn media screen app name*/null,
- /*vr synonyms*/null,
- /*is media app*/ null,
- /*sdlMsgVersion*/null,
- /*language desired*/null,
- /*HMI Display Language Desired*/null,
- /*App Type*/null,
- /*App ID*/null,
- /*autoActivateID*/null,
- callbackToUIThread,
- new BTTransportConfig());
-
- SdlTrace.logProxyEvent("Application constructed SdlProxy instance passing in: IProxyListener, callBackToUIThread.", SDL_LIB_TRACE_KEY);
- }
-
- /********************************************** TRANSPORT SWITCHING SUPPORT *****************************************/
-
- /**
- * Constructor for the SdlProxy object, the proxy for communicating between the App and SDL.
- *
- * @param listener - Reference to the object in the App listening to callbacks from SDL.
- * @param transportConfig Initial configuration for transport.
- * @throws SdlException
- */
- @Deprecated
- public SdlProxy(IProxyListener listener, BaseTransportConfig transportConfig) throws SdlException {
- super( listener,
- /*application context*/null,
- /*enable advanced lifecycle management*/false,
- /*app name*/ null,
- /*TTS Name*/null,
- /*ngn media screen app name*/null,
- /*vr synonyms*/null,
- /*is media app*/ null,
- /*sdlMsgVersion*/null,
- /*language desired*/null,
- /*HMI Display Language Desired*/null,
- /*App Type*/null,
- /*App ID*/null,
- /*autoActivateID*/null,
- /*callbackToUIThread*/ true,
- transportConfig);
-
- SdlTrace.logProxyEvent("Application constructed SdlProxy instance passing in: IProxyListener.", SDL_LIB_TRACE_KEY);
- }
-
- /**
- * Constructor for the SdlProxy object, the proxy for communicating between the App and SDL.
- *
- * @param listener - Reference to the object in the App listening to callbacks from SDL.
- * @param sdlProxyConfigurationResources
- * @param transportConfig Initial configuration for transport.
- * @throws SdlException
- */
- @Deprecated
- public SdlProxy(IProxyListener listener, SdlProxyConfigurationResources sdlProxyConfigurationResources,
- BaseTransportConfig transportConfig)
- throws SdlException {
- super( listener,
- sdlProxyConfigurationResources,
- /*enable advanced lifecycle management*/false,
- /*app name*/ null,
- /*TTS Name*/null,
- /*ngn media screen app name*/null,
- /*vr synonyms*/null,
- /*is media app*/ null,
- /*sdlMsgVersion*/null,
- /*language desired*/null,
- /*HMI Display Language Desired*/null,
- /*App Type*/null,
- /*App ID*/null,
- /*autoActivateID*/null,
- /*callbackToUIThread*/ true,
- transportConfig);
-
- SdlTrace.logProxyEvent("Application constructed SdlProxy instance passing in: IProxyListener, SdlProxyConfigurationResources.", SDL_LIB_TRACE_KEY);
- }
-
- /**
- * Constructor for the SdlProxy object, the proxy for communicating between the App and SDL.
- *
- * @param listener - Reference to the object in the App listening to callbacks from SDL.
- * @param callbackToUIThread - If true, all callbacks will occur on the UI thread.
- * @param transportConfig Initial configuration for transport.
- * @throws SdlException
- */
- @Deprecated
- public SdlProxy(IProxyListener listener, boolean callbackToUIThread, BaseTransportConfig transportConfig) throws SdlException {
- super( listener,
- /*sdl proxy configuration resources*/null,
- /*enable advanced lifecycle management*/false,
- /*app name*/ null,
- /*TTS Name*/null,
- /*ngn media screen app name*/null,
- /*vr synonyms*/null,
- /*is media app*/ null,
- /*sdlMsgVersion*/null,
- /*language desired*/null,
- /*HMI Display Language Desired*/null,
- /*App Type*/null,
- /*App ID*/null,
- /*autoActivateID*/null,
- callbackToUIThread,
- transportConfig);
-
- SdlTrace.logProxyEvent("Application constructed SdlProxy instance passing in: IProxyListener, callBackToUIThread.", SDL_LIB_TRACE_KEY);
- }
-
- /**
- * Constructor for the SdlProxy object, the proxy for communicating between the App and SDL.
- *
- * @param listener - Reference to the object in the App listening to callbacks from SDL.
- * @param sdlProxyConfigurationResources
- * @param callbackToUIThread - If true, all callbacks will occur on the UI thread.
- * @param transportConfig Initial configuration for transport.
- * @throws SdlException
- */
- @Deprecated
- public SdlProxy(IProxyListener listener, SdlProxyConfigurationResources sdlProxyConfigurationResources,
- boolean callbackToUIThread, BaseTransportConfig transportConfig) throws SdlException {
- super( listener,
- sdlProxyConfigurationResources,
- /*enable advanced lifecycle management*/false,
- /*app name*/ null,
- /*TTS Name*/null,
- /*ngn media screen app name*/null,
- /*vr synonyms*/null,
- /*is media app*/ null,
- /*sdlMsgVersion*/null,
- /*language desired*/null,
- /*HMI Display Language Desired*/null,
- /*App Type*/null,
- /*App ID*/null,
- /*autoActivateID*/null,
- callbackToUIThread,
- transportConfig);
-
- SdlTrace.logProxyEvent("Application constructed SdlProxy instance passing in: IProxyListener, callBackToUIThread.", SDL_LIB_TRACE_KEY);
- }
-
- /******************** Public Helper Methods *************************/
-
-
- /**
- * Sends a RegisterAppInterface RPCRequest to SDL. Responses are captured through callback on IProxyListener.
- *
- * @param sdlMsgVersion
- * @param appName
- * @param ngnMediaScreenAppName
- * @param vrSynonyms
- * @param isMediaApp
- * @param languageDesired
- * @param autoActivateID
- * @param correlationID
- *
- * @throws SdlException
- */
- @Deprecated
- public void registerAppInterface(
- SdlMsgVersion sdlMsgVersion, String appName, String ngnMediaScreenAppName,
- Vector<String> vrSynonyms, Boolean isMediaApp, Language languageDesired, Language hmiDisplayLanguageDesired,
- String appID, String autoActivateID, Integer correlationID)
- throws SdlException {
-
- // Test if proxy has been disposed
- if (_proxyDisposed) {
- throw new SdlException("This SdlProxy object has been disposed, it is no long capable of sending requests.", SdlExceptionCause.SDL_PROXY_DISPOSED);
- }
-
- registerAppInterfacePrivate(
- sdlMsgVersion,
- appName,
- null,
- ngnMediaScreenAppName,
- vrSynonyms,
- isMediaApp,
- languageDesired,
- hmiDisplayLanguageDesired,
- null,
- appID,
- autoActivateID,
- correlationID);
- }
-
- /**
- * Sends a RegisterAppInterface RPCRequest to SDL. Responses are captured through callback on IProxyListener.
- *
- * @param appName
- * @param isMediaApp
- * @param autoActivateID
- * @throws SdlException
- */
- @Deprecated
- public void registerAppInterface(
- String appName, Boolean isMediaApp, String appID, String autoActivateID, Integer correlationID)
- throws SdlException {
-
- registerAppInterface(
- /*sdlMsgVersion*/null,
- appName,
- /*ngnMediaScreenAppName*/null,
- /*vrSynonyms*/null,
- isMediaApp,
- /*languageDesired*/null,
- /*hmiDisplayLanguageDesired*/null,
- appID,
- autoActivateID,
- correlationID);
- }
-
- /**
- * Sends a RegisterAppInterface RPCRequest to SDL. Responses are captured through callback on IProxyListener.
- *
- * @param appName
- * @throws SdlException
- */
- @Deprecated
- public void registerAppInterface(String appName, String appID, Integer correlationID)
- throws SdlException {
-
- registerAppInterface(appName, false, appID, "", correlationID);
- }
-
- /**
- * Sends an UnregisterAppInterface RPCRequest to SDL. Responses are captured through callback on IProxyListener.
- *
- * @param correlationID
- * @throws SdlException
- */
- @Deprecated
- public void unregisterAppInterface(Integer correlationID)
- throws SdlException {
- // Test if proxy has been disposed
- if (_proxyDisposed) {
- throw new SdlException("This SdlProxy object has been disposed, it is no long capable of executing methods.",
- SdlExceptionCause.SDL_PROXY_DISPOSED);
- }
-
- unregisterAppInterfacePrivate(correlationID);
- }
-
- /**
- * Returns is isConnected state of the SDL transport.
- *
- * @return Boolean isConnected
- */
- @Deprecated
- public Boolean getIsConnected() {
- return super.getIsConnected();
- }
-
-}
diff --git a/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyALM.java b/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyALM.java
index 093b919ac..8b9174a19 100644
--- a/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyALM.java
+++ b/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyALM.java
@@ -4,10 +4,10 @@ import java.util.List;
import java.util.Vector;
import android.app.Service;
+import android.content.Context;
import com.smartdevicelink.exception.SdlException;
import com.smartdevicelink.exception.SdlExceptionCause;
-import com.smartdevicelink.proxy.Version;
import com.smartdevicelink.proxy.interfaces.IProxyListenerALM;
import com.smartdevicelink.proxy.rpc.AudioPassThruCapabilities;
import com.smartdevicelink.proxy.rpc.ButtonCapabilities;
@@ -27,8 +27,10 @@ import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
import com.smartdevicelink.proxy.rpc.enums.VrCapabilities;
import com.smartdevicelink.trace.SdlTrace;
import com.smartdevicelink.transport.BTTransportConfig;
-import com.smartdevicelink.transport.BaseTransportConfig;
+import com.smartdevicelink.transport.BaseTransportConfig;
+import com.smartdevicelink.transport.MultiplexTransportConfig;
import com.smartdevicelink.transport.enums.TransportType;
+
public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
@@ -44,7 +46,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
* @param appName - Name of the application displayed on SDL.
* @param isMediaApp - Indicates if the app is a media application.
*/
- public SdlProxyALM(IProxyListenerALM listener, String appName, Boolean isMediaApp,
+ public SdlProxyALM(Context context,IProxyListenerALM listener, String appName, Boolean isMediaApp,
Language languageDesired, Language hmiDisplayLanguageDesired, String appID) throws SdlException {
super( listener,
/*sdl proxy configuration resources*/null,
@@ -61,7 +63,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
/*App ID*/appID,
/*autoActivateID*/null,
/*callbackToUIThread*/ false,
- new BTTransportConfig());
+ new MultiplexTransportConfig(context,appID));
SdlTrace.logProxyEvent("Application constructed SdlProxyALM (using legacy constructor for BT transport) instance passing in: IProxyListener, appName, and isMediaApp.", SDL_LIB_TRACE_KEY);
}
@@ -83,7 +85,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
* @param autoActivateID - ID used to re-register previously registered application.
* @throws SdlException
*/
- public SdlProxyALM(IProxyListenerALM listener, String appName, String ngnMediaScreenAppName,
+ public SdlProxyALM(Context context,IProxyListenerALM listener, String appName, String ngnMediaScreenAppName,
Vector<String> vrSynonyms, Boolean isMediaApp, SdlMsgVersion sdlMsgVersion,
Language languageDesired, Language hmiDisplayLanguageDesired, String appID,
String autoActivateID) throws SdlException {
@@ -102,7 +104,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
/*App ID*/appID,
autoActivateID,
/*callbackToUIThread*/ false,
- new BTTransportConfig());
+ new MultiplexTransportConfig(context,appID));
SdlTrace.logProxyEvent("Application constructed SdlProxyALM (using legacy constructor for BT transport) instance passing in: IProxyListener, appName, ngnMediaScreenAppName, " +
"vrSynonyms, isMediaApp, sdlMsgVersion, languageDesired, and autoActivateID.", SDL_LIB_TRACE_KEY);
@@ -125,7 +127,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
* @param autoActivateID - ID used to re-register previously registered application.
* @throws SdlException
*/
- public SdlProxyALM(IProxyListenerALM listener, SdlProxyConfigurationResources sdlProxyConfigurationResources,
+ public SdlProxyALM(Context context,IProxyListenerALM listener, SdlProxyConfigurationResources sdlProxyConfigurationResources,
String appName, String ngnMediaScreenAppName, Vector<String> vrSynonyms,
Boolean isMediaApp, SdlMsgVersion sdlMsgVersion, Language languageDesired,
Language hmiDisplayLanguageDesired, String appID, String autoActivateID) throws SdlException {
@@ -144,7 +146,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
/*App ID*/appID,
autoActivateID,
/*callbackToUIThread*/ false,
- new BTTransportConfig());
+ new MultiplexTransportConfig(context,appID));
SdlTrace.logProxyEvent("Application constructed SdlProxyALM (using legacy constructor for BT transport) instance passing in: IProxyListener, sdlProxyConfigurationResources, " +
"appName, ngnMediaScreenAppName, vrSynonyms, isMediaApp, sdlMsgVersion, languageDesired, and autoActivateID.", SDL_LIB_TRACE_KEY);
@@ -168,7 +170,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
* @param callbackToUIThread - If true, all callbacks will occur on the UI thread.
* @throws SdlException
*/
- public SdlProxyALM(IProxyListenerALM listener, String appName, String ngnMediaScreenAppName,
+ public SdlProxyALM(Context context,IProxyListenerALM listener, String appName, String ngnMediaScreenAppName,
Vector<String> vrSynonyms, Boolean isMediaApp, SdlMsgVersion sdlMsgVersion,
Language languageDesired, Language hmiDisplayLanguageDesired, String appID,
String autoActivateID, boolean callbackToUIThread) throws SdlException {
@@ -187,7 +189,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
/*App ID*/appID,
autoActivateID,
callbackToUIThread,
- new BTTransportConfig());
+ new MultiplexTransportConfig(context,appID));
SdlTrace.logProxyEvent("Application constructed SdlProxyALM (using legacy constructor for BT transport) instance passing in: IProxyListener, " +
"appName, ngnMediaScreenAppName, vrSynonyms, isMediaApp, sdlMsgVersion, languageDesired, autoActivateID, " +
@@ -212,7 +214,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
* @param callbackToUIThread - If true, all callbacks will occur on the UI thread.
* @throws SdlException
*/
- public SdlProxyALM(IProxyListenerALM listener, SdlProxyConfigurationResources sdlProxyConfigurationResources,
+ public SdlProxyALM(Context context,IProxyListenerALM listener, SdlProxyConfigurationResources sdlProxyConfigurationResources,
String appName, String ngnMediaScreenAppName, Vector<String> vrSynonyms, Boolean isMediaApp,
SdlMsgVersion sdlMsgVersion, Language languageDesired, Language hmiDisplayLanguageDesired,
String appID, String autoActivateID,
@@ -232,14 +234,14 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
/*App ID*/appID,
autoActivateID,
callbackToUIThread,
- new BTTransportConfig());
+ new MultiplexTransportConfig(context,appID));
SdlTrace.logProxyEvent("Application constructed SdlProxyALM (using legacy constructor for BT transport) instance passing in: IProxyListener, sdlProxyConfigurationResources, " +
"appName, ngnMediaScreenAppName, vrSynonyms, isMediaApp, sdlMsgVersion, languageDesired, autoActivateID, " +
"and callbackToUIThread", SDL_LIB_TRACE_KEY);
}
- public SdlProxyALM(IProxyListenerALM listener, SdlProxyConfigurationResources sdlProxyConfigurationResources,
+ public SdlProxyALM(Context context,IProxyListenerALM listener, SdlProxyConfigurationResources sdlProxyConfigurationResources,
String appName, String ngnMediaScreenAppName, Vector<String> vrSynonyms, Boolean isMediaApp,
SdlMsgVersion sdlMsgVersion, Language languageDesired, Language hmiDisplayLanguageDesired,
String appID, String autoActivateID, boolean callbackToUIThread, boolean preRegister) throws SdlException {
@@ -259,7 +261,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
autoActivateID,
callbackToUIThread,
preRegister,
- new BTTransportConfig());
+ new MultiplexTransportConfig(context,appID));
SdlTrace.logProxyEvent("Application constructed SdlProxyALM (using legacy constructor for BT transport) instance passing in: IProxyListener, sdlProxyConfigurationResources, " +
"appName, ngnMediaScreenAppName, vrSynonyms, isMediaApp, sdlMsgVersion, languageDesired, autoActivateID, " +
@@ -547,7 +549,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
* @param preRegister Flag that indicates that client should be pre-registred or not
* @throws SdlException
*/
- public SdlProxyALM(IProxyListenerALM listener, String appName, Boolean isMediaApp,Language languageDesired, Language hmiDisplayLanguageDesired,
+ public SdlProxyALM(Context context,IProxyListenerALM listener, String appName, Boolean isMediaApp,Language languageDesired, Language hmiDisplayLanguageDesired,
String appID, boolean callbackToUIThread, boolean preRegister) throws SdlException
{
super( listener,
@@ -566,7 +568,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
/*autoActivateID*/null,
callbackToUIThread,
preRegister,
- new BTTransportConfig());
+ new MultiplexTransportConfig(context,appID));
SdlTrace.logProxyEvent("Application constructed SdlProxyALM (using legacy constructor for BT transport) instance passing in: IProxyListener, " +
"appName, isMediaApp, languageDesired, hmiDisplayLanguageDesired" + "callbackToUIThread and version", SDL_LIB_TRACE_KEY);
@@ -583,7 +585,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
* @param appID Identifier of the client application.
* @throws SdlException
*/
- public SdlProxyALM(IProxyListenerALM listener, String appName, Boolean isMediaApp,String appID) throws SdlException {
+ public SdlProxyALM(Context context,IProxyListenerALM listener, String appName, Boolean isMediaApp,String appID) throws SdlException {
super( listener,
/*sdlProxyConfigurationResources*/null,
/*enable advanced lifecycle management*/true,
@@ -600,7 +602,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
/*autoActivateID*/null,
false,
false,
- new BTTransportConfig());
+ new MultiplexTransportConfig(context,appID));
SdlTrace.logProxyEvent("Application constructed SdlProxyALM (using legacy constructor for BT transport) instance passing in: IProxyListener, " +
"appName, isMediaApp, appID", SDL_LIB_TRACE_KEY);
@@ -678,7 +680,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
* @param preRegister Flag that indicates that client should be pre-registred or not
* @throws SdlException
*/
- public SdlProxyALM(IProxyListenerALM listener, String appName, Boolean isMediaApp,String appID,
+ public SdlProxyALM(Context context,IProxyListenerALM listener, String appName, Boolean isMediaApp,String appID,
boolean callbackToUIThread, boolean preRegister) throws SdlException {
super( listener,
/*sdlProxyConfigurationResources*/null,
@@ -696,7 +698,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
/*autoActivateID*/null,
callbackToUIThread,
preRegister,
- new BTTransportConfig());
+ new MultiplexTransportConfig(context,appID));
SdlTrace.logProxyEvent("Application constructed SdlProxyALM (using legacy constructor for BT transport) instance passing in: IProxyListener, " +
"appName, isMediaApp, " + "callbackToUIThread and version", SDL_LIB_TRACE_KEY);
@@ -745,7 +747,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
autoActivateID,
callbackToUIThread,
preRegister,
- new BTTransportConfig());
+ new MultiplexTransportConfig(appService.getBaseContext(),appID));
this.setAppService(appService);
this.sendTransportBroadcast();
@@ -812,7 +814,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
* @param preRegister Flag that indicates that client should be pre-registred or not
* @throws SdlException
*/
- public SdlProxyALM(IProxyListenerALM listener, SdlProxyConfigurationResources sdlProxyConfigurationResources,
+ public SdlProxyALM(Context context,IProxyListenerALM listener, SdlProxyConfigurationResources sdlProxyConfigurationResources,
String appName, Vector<TTSChunk> ttsName, String ngnMediaScreenAppName, Vector<String> vrSynonyms, Boolean isMediaApp,
SdlMsgVersion sdlMsgVersion, Language languageDesired, Language hmiDisplayLanguageDesired,
String appID, String autoActivateID, boolean callbackToUIThread, boolean preRegister) throws SdlException {
@@ -832,7 +834,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
autoActivateID,
callbackToUIThread,
preRegister,
- new BTTransportConfig());
+ new MultiplexTransportConfig(context,appID));
SdlTrace.logProxyEvent("Application constructed SdlProxyALM (using legacy constructor for BT transport) instance passing in: IProxyListener, sdlProxyConfigurationResources, " +
"appName, ngnMediaScreenAppName, vrSynonyms, isMediaApp, sdlMsgVersion, languageDesired, autoActivateID, " +
@@ -916,7 +918,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
* @param preRegister Flag that indicates that client should be pre-registred or not
* @throws SdlException
*/
- public SdlProxyALM(IProxyListenerALM listener, SdlProxyConfigurationResources sdlProxyConfigurationResources,
+ public SdlProxyALM(Context context,IProxyListenerALM listener, SdlProxyConfigurationResources sdlProxyConfigurationResources,
String appName, Vector<TTSChunk> ttsName, String ngnMediaScreenAppName, Vector<String> vrSynonyms, Boolean isMediaApp,
SdlMsgVersion sdlMsgVersion, Language languageDesired, Language hmiDisplayLanguageDesired,
Vector<AppHMIType> appType, String appID, String autoActivateID, boolean callbackToUIThread, boolean preRegister) throws SdlException {
@@ -936,7 +938,7 @@ public class SdlProxyALM extends SdlProxyBase<IProxyListenerALM> {
autoActivateID,
callbackToUIThread,
preRegister,
- new BTTransportConfig());
+ new MultiplexTransportConfig(context,appID));
SdlTrace.logProxyEvent("Application constructed SdlProxyALM (using legacy constructor for BT transport) instance passing in: IProxyListener, sdlProxyConfigurationResources, " +
"appName, ngnMediaScreenAppName, vrSynonyms, isMediaApp, sdlMsgVersion, languageDesired, appType, appID, autoActivateID, " +
diff --git a/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyBase.java b/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyBase.java
index 76648e2d2..470cb6a14 100644
--- a/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyBase.java
+++ b/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyBase.java
@@ -271,7 +271,12 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
if (_advancedLifecycleManagementEnabled) {
// Cycle the proxy
- cycleProxy(SdlDisconnectedReason.TRANSPORT_ERROR);
+ if(SdlConnection.isLegacyModeEnabled()){
+ cycleProxy(SdlDisconnectedReason.LEGACY_BLUETOOTH_MODE_ENABLED);
+
+ }else{
+ cycleProxy(SdlDisconnectedReason.TRANSPORT_ERROR);
+ }
} else {
notifyProxyClosed(info, e, SdlDisconnectedReason.TRANSPORT_ERROR);
}
@@ -1147,6 +1152,25 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
sendTransportBroadcast();
}
}
+ /**
+ * This method will fake the multiplex connection event
+ * @param action
+ */
+ public void forceOnConnected(){
+ synchronized(CONNECTION_REFERENCE_LOCK) {
+ if (sdlSession != null) {
+ if(sdlSession.getSdlConnection()==null){ //There is an issue when switching from v1 to v2+ where the connection is closed. So we restart the session during this call.
+ try {
+ sdlSession.startSession();
+ } catch (SdlException e) {
+ e.printStackTrace();
+ }
+ }
+ sdlSession.getSdlConnection().forceHardwareConnectEvent(TransportType.BLUETOOTH);
+
+ }
+ }
+ }
public void sendTransportBroadcast()
{
@@ -1334,7 +1358,9 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
_cycling = true;
cleanProxy(disconnectedReason);
initializeProxy();
- notifyProxyClosed("Sdl Proxy Cycled", new SdlException("Sdl Proxy Cycled", SdlExceptionCause.SDL_PROXY_CYCLED), disconnectedReason);
+ if(!SdlDisconnectedReason.LEGACY_BLUETOOTH_MODE_ENABLED.equals(disconnectedReason)){//We don't want to alert higher if we are just cycling for legacy bluetooth
+ notifyProxyClosed("Sdl Proxy Cycled", new SdlException("Sdl Proxy Cycled", SdlExceptionCause.SDL_PROXY_CYCLED), disconnectedReason);
+ }
}
catch (SdlException e) {
Intent sendIntent = createBroadcastIntent();
@@ -1589,8 +1615,12 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
throw new SdlException("CorrelationID cannot be null. RPC: " + request.getFunctionName(), SdlExceptionCause.INVALID_ARGUMENT);
}
pm.setCorrID(request.getCorrelationID());
- if (request.getBulkData() != null)
+ if (request.getBulkData() != null){
pm.setBulkData(request.getBulkData());
+ }
+ if(request.getFunctionName().equalsIgnoreCase(FunctionID.PUT_FILE.name())){
+ pm.setPriorityCoefficient(1);
+ }
// Queue this outgoing message
synchronized(OUTGOING_MESSAGE_QUEUE_THREAD_LOCK) {
diff --git a/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyFactory.java b/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyFactory.java
deleted file mode 100644
index 9134fc5d4..000000000
--- a/sdl_android_lib/src/com/smartdevicelink/proxy/SdlProxyFactory.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.smartdevicelink.proxy;
-
-import com.smartdevicelink.exception.SdlException;
-@Deprecated
-public class SdlProxyFactory {
-
- @Deprecated
- public static SdlProxy buildSdlProxy(IProxyListener listener) {
- SdlProxy ret = null;
- try {
- ret = new SdlProxy(listener);
- } catch (SdlException e) {
- e.printStackTrace();
- }
- return ret;
- }
- @Deprecated
- public static SdlProxy buildSdlProxy(IProxyListener listener, String sAppName, String sAppID) {
- SdlProxy ret = null;
- try {
- ret = new SdlProxy(listener,sAppName,sAppID);
- } catch (SdlException e) {
- e.printStackTrace();
- }
- return ret;
- }
-} \ No newline at end of file
diff --git a/sdl_android_lib/src/com/smartdevicelink/proxy/rpc/enums/SdlDisconnectedReason.java b/sdl_android_lib/src/com/smartdevicelink/proxy/rpc/enums/SdlDisconnectedReason.java
index ae1a08894..0d1902e3a 100644
--- a/sdl_android_lib/src/com/smartdevicelink/proxy/rpc/enums/SdlDisconnectedReason.java
+++ b/sdl_android_lib/src/com/smartdevicelink/proxy/rpc/enums/SdlDisconnectedReason.java
@@ -21,7 +21,13 @@ public enum SdlDisconnectedReason {
SDL_REGISTRATION_ERROR,
APP_INTERFACE_UNREG,
GENERIC_ERROR,
- RPC_SESSION_ENDED;
+ /**
+ * This only occurs when multiplexing is running and it is found to be on an old gen 1 system.
+ */
+ LEGACY_BLUETOOTH_MODE_ENABLED,
+ RPC_SESSION_ENDED
+ ;
+
public static SdlDisconnectedReason valueForString(String value) {
try{
diff --git a/sdl_android_lib/src/com/smartdevicelink/streaming/StreamRPCPacketizer.java b/sdl_android_lib/src/com/smartdevicelink/streaming/StreamRPCPacketizer.java
index 68a7a02ca..ac9ebb379 100644
--- a/sdl_android_lib/src/com/smartdevicelink/streaming/StreamRPCPacketizer.java
+++ b/sdl_android_lib/src/com/smartdevicelink/streaming/StreamRPCPacketizer.java
@@ -151,7 +151,8 @@ public class StreamRPCPacketizer extends AbstractPacketizer implements IPutFileR
PutFile msg = (PutFile) _request;
long iOffsetCounter = msg.getOffset();
sFileName = msg.getSdlFileName();
-
+ int priorityCoefficient = 1;
+
if (lFileSize != 0)
{
Long iFileSize = (long) lFileSize;
@@ -211,6 +212,8 @@ public class StreamRPCPacketizer extends AbstractPacketizer implements IPutFileR
pm.setBulkDataNoCopy(buffer);
pm.setCorrID(msg.getCorrelationID());
+ priorityCoefficient++;
+ pm.setPriorityCoefficient(priorityCoefficient);
notification = new OnStreamRPC();
notification.setFileName(msg.getSdlFileName());
diff --git a/sdl_android_lib/src/com/smartdevicelink/trace/SdlTrace.java b/sdl_android_lib/src/com/smartdevicelink/trace/SdlTrace.java
index 51c3ab9bf..93f8ca246 100644
--- a/sdl_android_lib/src/com/smartdevicelink/trace/SdlTrace.java
+++ b/sdl_android_lib/src/com/smartdevicelink/trace/SdlTrace.java
@@ -1,12 +1,14 @@
package com.smartdevicelink.trace;
import java.sql.Timestamp;
+
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice;
import android.os.Build;
import android.os.Debug;
import android.os.Process;
-import com.smartdevicelink.protocol.ProtocolFrameHeader;
+
+import com.smartdevicelink.protocol.SdlPacket;
import com.smartdevicelink.protocol.enums.FrameDataControlFrameType;
import com.smartdevicelink.protocol.enums.FrameType;
import com.smartdevicelink.protocol.enums.SessionType;
@@ -218,7 +220,7 @@ public class SdlTrace {
return writeXmlTraceMessage(xml);
}
- public static boolean logProtocolEvent(InterfaceActivityDirection frameDirection, ProtocolFrameHeader frameHeader, byte[] frameData, int frameDataOffset, int frameDataLength, String token) {
+ public static boolean logProtocolEvent(InterfaceActivityDirection frameDirection, SdlPacket packet, int frameDataOffset, int frameDataLength, String token) {
DetailLevel dl = DiagLevel.getLevel(Mod.proto);
if (dl == DetailLevel.OFF || !token.equals(SDL_LIB_TRACE_KEY)) {
return false;
@@ -226,12 +228,12 @@ public class SdlTrace {
StringBuffer protoMsg = new StringBuffer();
protoMsg.append("<frame>");
- protoMsg.append(SdlTrace.getProtocolFrameHeaderInfo(frameHeader, frameData));
+ protoMsg.append(SdlTrace.getProtocolFrameHeaderInfo(packet));
if (dl == DetailLevel.VERBOSE) {
- if (frameData != null && frameDataLength > 0) {
+ if (packet.getPayload() != null && frameDataLength > 0) {
protoMsg.append("<d>");
String bytesInfo = "";
- bytesInfo = Mime.base64Encode(frameData, frameDataOffset, frameDataLength);
+ bytesInfo = Mime.base64Encode(packet.getPayload(), frameDataOffset, frameDataLength);
// Base64 only available in 2.2, when SmartDeviceLink base is 2.2 use: bytesInfo = Base64.encodeToString(frameData, frameDataOffset, frameDataLength, Base64.DEFAULT);
protoMsg.append(bytesInfo);
protoMsg.append("</d>");
@@ -266,24 +268,24 @@ public class SdlTrace {
return s;
} // end-method
- private static String getProtocolFrameHeaderInfo(ProtocolFrameHeader hdr, byte[] buf) {
+ private static String getProtocolFrameHeaderInfo(SdlPacket hdr) {
StringBuilder sb = new StringBuilder();
sb.append("<hdr>");
sb.append("<ver>");
sb.append(hdr.getVersion());
sb.append("</ver><cmp>");
- sb.append(hdr.isCompressed());
+ sb.append(hdr.isCompression());
sb.append("</cmp><ft>");
sb.append(getProtocolFrameType(hdr.getFrameType()));
sb.append("</ft><st>");
- sb.append(getProtocolSessionType(hdr.getSessionType()));
+ sb.append(getProtocolSessionType(SessionType.valueOf((byte)hdr.getServiceType())));
sb.append("</st><sid>");
- sb.append(hdr.getSessionID());
+ sb.append(hdr.getSessionId());
sb.append("</sid><sz>");
sb.append(hdr.getDataSize());
sb.append("</sz>");
- int frameData = hdr.getFrameData();
+ int frameData = hdr.getFrameInfo();
if (hdr.getFrameType() == FrameType.Control) {
sb.append("<ca>");
if (frameData == FrameDataControlFrameType.StartSession.getValue())
@@ -303,8 +305,8 @@ public class SdlTrace {
sb.append(String.format("%02X",frameData));
sb.append("</fsn>");
} else if (hdr.getFrameType() == FrameType.First ) {
- int totalSize = BitConverter.intFromByteArray(buf, 0);
- int numFrames = BitConverter.intFromByteArray(buf, 4);
+ int totalSize = BitConverter.intFromByteArray(hdr.getPayload(), 0);
+ int numFrames = BitConverter.intFromByteArray(hdr.getPayload(), 4);
sb.append("<total>" + totalSize + "</total><numframes>" + numFrames + "</numframes>");
} else if (hdr.getFrameType() == FrameType.Single ) {
sb.append("<single/>");
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/BTTransport.java b/sdl_android_lib/src/com/smartdevicelink/transport/BTTransport.java
index e45c2967b..e41c501e5 100644
--- a/sdl_android_lib/src/com/smartdevicelink/transport/BTTransport.java
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/BTTransport.java
@@ -12,8 +12,10 @@ import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Build.VERSION;
+import com.smartdevicelink.SdlConnection.SdlConnection;
import com.smartdevicelink.exception.SdlException;
import com.smartdevicelink.exception.SdlExceptionCause;
+import com.smartdevicelink.protocol.SdlPacket;
import com.smartdevicelink.trace.SdlTrace;
import com.smartdevicelink.trace.enums.InterfaceActivityDirection;
import com.smartdevicelink.transport.enums.TransportType;
@@ -31,7 +33,6 @@ public class BTTransport extends SdlTransport {
private BluetoothAdapter _adapter = null;
private BluetoothSocket _activeSocket = null;
- private InputStream _input = null;
private UUID _listeningServiceUUID = SDL_V4_MOBILE_APPLICATION_SVC_CLASS;
private BluetoothAdapterMonitor _bluetoothAdapterMonitor = null;
private TransportReaderThread _transportReader = null;
@@ -133,8 +134,7 @@ public class BTTransport extends SdlTransport {
}*/
- public void openConnection () throws SdlException {
-
+ public void openConnection () throws SdlException {
if (_serverSocket != null) {
return;
}
@@ -145,15 +145,18 @@ public class BTTransport extends SdlTransport {
// Test if Adapter exists
if (_adapter == null) {
+ SdlConnection.enableLegacyMode(false, null);
throw new SdlException("No Bluetooth adapter found. Bluetooth adapter must exist to communicate with SDL.", SdlExceptionCause.BLUETOOTH_ADAPTER_NULL);
}
// Test if Bluetooth is enabled
try {
if (!_adapter.isEnabled()) {
+ SdlConnection.enableLegacyMode(false, null);
throw new SdlException("Bluetooth adapter must be enabled to instantiate a SdlProxy object.", SdlExceptionCause.BLUETOOTH_DISABLED);
}
} catch (SecurityException e) {
+ SdlConnection.enableLegacyMode(false, null);
throw new SdlException("Insufficient permissions to interact with the Bluetooth Adapter.", SdlExceptionCause.PERMISSION_DENIED);
}
@@ -166,32 +169,36 @@ public class BTTransport extends SdlTransport {
int iSocket = getChannel(mySock);
sComment = "Accepting Connections on SDP Server Port Number: " + iSocket + "\r\n";
- sComment += "Keep Server Socket Open: " + bKeepSocketActive;
+ sComment += "Keep Server Socket Open: " + bKeepSocketActive;
if (iSocket < 0)
{
+ SdlConnection.enableLegacyMode(false, null);
throw new SdlException("Could not open connection to SDL.", SdlExceptionCause.BLUETOOTH_SOCKET_UNAVAILABLE);
}
} catch (IOException e) {
-
+ SdlConnection.enableLegacyMode(false, null);
throw new SdlException("Could not open connection to SDL.", SdlExceptionCause.BLUETOOTH_SOCKET_UNAVAILABLE);
} catch (Exception ex) {
// Test to determine if the bluetooth has been disabled since last check
if (!_adapter.isEnabled()) {
+ SdlConnection.enableLegacyMode(false, null);
throw new SdlException("Bluetooth adapter must be on to instantiate a SdlProxy object.", SdlExceptionCause.BLUETOOTH_DISABLED);
}
if(((SdlException) ex).getSdlExceptionCause() == SdlExceptionCause.BLUETOOTH_SOCKET_UNAVAILABLE) {
-
+ SdlConnection.enableLegacyMode(false, null);
throw new SdlException("Could not open connection to SDL.", SdlExceptionCause.BLUETOOTH_SOCKET_UNAVAILABLE);
}
+ SdlConnection.enableLegacyMode(false, null);
throw new SdlException("Could not open connection to SDL.", ex, SdlExceptionCause.SDL_CONNECTION_FAILED);
}
// Test to ensure serverSocket is not null
if (_serverSocket == null) {
+ SdlConnection.enableLegacyMode(false, null);
throw new SdlException("Could not open connection to SDL.", SdlExceptionCause.SDL_CONNECTION_FAILED);
}
@@ -271,14 +278,7 @@ public class BTTransport extends SdlTransport {
DebugTool.logError("Failed to close activeSocket", e);
} // end-catch
- try {
- if (_input != null) {
- _input.close();
- _input = null;
- }
- } catch (Exception e) {
- DebugTool.logError("Failed to close input stream", e);
- } // end-catch
+
try {
if (_output != null) {
@@ -306,10 +306,11 @@ public class BTTransport extends SdlTransport {
* Sends data over the transport. Takes a byte array and transmits data provided starting at the
* offset and of the provided length to fragment transmission.
*/
- public boolean sendBytesOverTransport(byte[] msgBytes, int offset, int length) {
+ public boolean sendBytesOverTransport(SdlPacket packet) {
boolean sendResult = false;
try {
- _output.write(msgBytes, offset, length);
+ byte[] msgBytes = packet.constructPacket();
+ _output.write(msgBytes, 0, msgBytes.length);
sendResult = true;
} catch (Exception ex) {
DebugTool.logError("Error writing to Bluetooth socket: " + ex.toString(), ex);
@@ -322,17 +323,24 @@ public class BTTransport extends SdlTransport {
private class TransportReaderThread extends Thread {
-
- byte[] buf = new byte[4096];
private Boolean isHalted = false;
-
+ SdlPsm psm;
+ byte byteRead = -1;
+ boolean stateProgress = false;
+
+ private InputStream _input = null;
+
+
+ public TransportReaderThread(){
+ psm = new SdlPsm();
+ }
public void halt() {
isHalted = true;
}
private void acceptConnection() {
SdlTrace.logTransportEvent("BTTransport: Waiting for incoming RFCOMM connect", "", InterfaceActivityDirection.Receive, null, 0, SDL_LIB_TRACE_KEY);
-
+
try {
// Blocks thread until connection established.
_activeSocket = _serverSocket.accept();
@@ -353,10 +361,9 @@ public class BTTransport extends SdlTransport {
handleTransportConnected();
} catch (Exception e) {
-
if (!isHalted) {
// Only call disconnect if the thread has not been halted
-
+ clearInputStream();
// Check to see if Bluetooth was disabled
if (_adapter != null && !_adapter.isEnabled()) {
disconnect("Bluetooth Adapater has been disabled.", new SdlException("Bluetooth adapter must be enabled to instantiate a SdlProxy object.", e, SdlExceptionCause.BLUETOOTH_DISABLED));
@@ -365,6 +372,7 @@ public class BTTransport extends SdlTransport {
}
}
} finally {
+
if (!bKeepSocketActive && _serverSocket != null && !isHalted && (VERSION.SDK_INT > 0x00000010 /*VERSION_CODES.JELLY_BEAN*/) ) {
try {
_serverSocket.close();
@@ -378,13 +386,12 @@ public class BTTransport extends SdlTransport {
private void readFromTransport() {
try {
- int bytesRead = -1;
try {
- bytesRead = _input.read(buf);
+ byteRead = (byte)_input.read();
} catch (Exception e) {
if (!isHalted) {
// Only call disconnect if the thread has not been halted
-
+ clearInputStream();
// Check to see if Bluetooth was disabled
if (_adapter != null && !_adapter.isEnabled()) {
disconnect("Bluetooth Adapater has been disabled.", new SdlException("Bluetooth adapter must be enabled to instantiate a SdlProxy object.", e, SdlExceptionCause.BLUETOOTH_DISABLED));
@@ -394,32 +401,53 @@ public class BTTransport extends SdlTransport {
}
return;
} // end-catch
-
- if (bytesRead != -1) {
- handleReceivedBytes(buf, bytesRead);
- } else {
- // When bytesRead == -1, it indicates end of stream
- if (!isHalted) {
- // Only call disconnect if the thread has not been halted
- DebugTool.logError("End of stream reached!");
- disconnect("End of stream reached.", null);
+
+ stateProgress = psm.handleByte(byteRead);
+ if(!stateProgress){//We are trying to weed through the bad packet info until we get something
+ //Log.w(TAG, "Packet State Machine did not move forward from state - "+ psm.getState()+". PSM being Reset.");
+ psm.reset();
+ if(byteRead == -1){ //If we read a -1 and the psm didn't move forward, then there is a problem
+ if (!isHalted) {
+ // Only call disconnect if the thread has not been halted
+ DebugTool.logError("End of stream reached!");
+ disconnect("End of stream reached.", null);
+ }
}
}
- } catch (Exception excp) {
- if (!isHalted) {
- // Only call disconnect if the thread has not been halted
- String errString = "Failure in BTTransport reader thread: " + excp.toString();
- DebugTool.logError(errString, excp);
- disconnect(errString, excp);
+ if(psm.getState() == SdlPsm.FINISHED_STATE){
+ //Log.d(TAG, "Packet formed, sending off");
+ handleReceivedPacket((SdlPacket)psm.getFormedPacket());
+ //We put a trace statement in the message read so we can avoid all the extra bytes
+ psm.reset();
}
- return;
+
+ } catch (Exception excp) {
+ if (!isHalted) {
+ // Only call disconnect if the thread has not been halted
+ clearInputStream();
+ String errString = "Failure in BTTransport reader thread: " + excp.toString();
+ DebugTool.logError(errString, excp);
+ disconnect(errString, excp);
+ }
+ return;
} // end-catch
} // end-method
+ private void clearInputStream(){
+ try {
+ if (_input != null) {
+ _input.close();
+ _input = null;
+ }
+ } catch (Exception e) {
+ DebugTool.logError("Failed to close input stream", e);
+ } // end-catch
+ }
+
public void run() {
// acceptConnection blocks until the connection has been accepted
acceptConnection();
-
+ psm.reset();
while (!isHalted) {
readFromTransport();
}
@@ -488,5 +516,17 @@ public class BTTransport extends SdlTransport {
public String getBroadcastComment() {
return sComment;
}
+
+ @Override
+ protected void handleTransportDisconnected(String info) {
+ SdlConnection.enableLegacyMode(false, null);
+ super.handleTransportDisconnected(info);
+ }
+
+ @Override
+ protected void handleTransportError(String message, Exception ex) {
+ SdlConnection.enableLegacyMode(false, null);
+ super.handleTransportError(message, ex);
+ }
} // end-class
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/ITransportListener.java b/sdl_android_lib/src/com/smartdevicelink/transport/ITransportListener.java
index 7f4f0b7de..b4c4a9b33 100644
--- a/sdl_android_lib/src/com/smartdevicelink/transport/ITransportListener.java
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/ITransportListener.java
@@ -1,8 +1,10 @@
package com.smartdevicelink.transport;
+import com.smartdevicelink.protocol.SdlPacket;
+
public interface ITransportListener {
- // Called to indicate and deliver bytes received from transport
- void onTransportBytesReceived(byte[] receivedBytes, int receivedBytesLength);
+ // Called to indicate and deliver a packet received from transport
+ void onTransportPacketReceived(SdlPacket packet);
// Called to indicate that transport connection was established
void onTransportConnected();
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/MultiplexBluetoothTransport.java b/sdl_android_lib/src/com/smartdevicelink/transport/MultiplexBluetoothTransport.java
new file mode 100644
index 000000000..08fe98586
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/MultiplexBluetoothTransport.java
@@ -0,0 +1,888 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.smartdevicelink.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.UUID;
+
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothServerSocket;
+import android.bluetooth.BluetoothSocket;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * This class does all the work for setting up and managing Bluetooth
+ * connections with other devices. It has a thread that listens for
+ * incoming connections, a thread for connecting with a device, and a
+ * thread for performing data transmissions when connected.
+ *
+ * @author Joey Grover
+ *
+ */
+public class MultiplexBluetoothTransport {
+ //finals
+ private static final String TAG = "Bluetooth Transport";
+ private static final UUID SERVER_UUID= new UUID(0x936DA01F9ABD4D9DL, 0x80C702AF85C822A8L);
+ // Name for the SDP record when creating server socket
+ private static final String NAME_SECURE =" SdlProxy";// = "LIVIO_CONNECT";
+
+
+ protected static final String SHARED_PREFS = "sdl.bluetoothprefs";
+
+
+ // Constants that indicate the current connection state
+ public static final int STATE_NONE = 0; // we're doing nothing
+ public static final int STATE_LISTEN = 1; // now listening for incoming connections
+ public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
+ public static final int STATE_CONNECTED = 3; // now connected to a remote device
+ public static final int STATE_ERROR = 4; // Something bad happend, we wil not try to restart the thread
+
+
+ // Member fields
+ private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+ private final Handler mHandler;
+ private AcceptThread mSecureAcceptThread;
+ private static Object threadLock = null;
+ private ConnectThread mConnectThread;
+ private static ConnectedThread mConnectedThread; //I HATE ALL THIS STATIC CRAP, But it seems like the only way right now
+ private static ConnectedWriteThread mConnectedWriteThread; //I HATE ALL THIS STATIC CRAP, But it seems like the only way right now
+
+ private static int mState;
+
+ // Key names received from the BluetoothSerialServer Handler
+ public static final String DEVICE_NAME = "device_name";
+ public static final String TOAST = "toast";
+
+
+ private int mBluetoothLevel = 0;
+ Handler timeOutHandler;
+ Runnable socketRunable;
+ private static final long msTillTimeout = 2500;
+
+ public static String currentlyConnectedDevice = null;
+ public static String currentlyConnectedDeviceAddress = null;
+ private static MultiplexBluetoothTransport serverInstance = null;
+ //private BluetoothServerSocket serverSocket= null;
+
+
+ static boolean listening = false;
+
+
+ /**
+ * Constructor. Prepares a new BluetoothChat session.
+ * @param context The UI Activity Context
+ * @param handler A Handler to send messages back to the UI Activity
+ */
+ private MultiplexBluetoothTransport(Handler handler) {
+ //Log.w(TAG, "Creating Bluetooth Serial Adapter");
+ // mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mState = STATE_NONE;
+ mHandler = handler;
+
+ //This will keep track of which method worked last night
+ mBluetoothLevel = SdlRouterService.getBluetoothPrefs(SHARED_PREFS);
+ Object object = new Object();
+ threadLock = object;
+
+ }
+
+
+
+ /*
+ * Let's use this method from now on to get bluetooth service
+ */
+ public synchronized static MultiplexBluetoothTransport getBluetoothSerialServerInstance(Handler handler){
+
+ if(serverInstance==null){
+ serverInstance = new MultiplexBluetoothTransport(handler);
+ }
+
+ return serverInstance;
+ }
+ public synchronized static MultiplexBluetoothTransport getBluetoothSerialServerInstance(){
+ return serverInstance;
+ }
+
+ //These methods are used so we can have a semi-static reference to the Accept Thread (Static reference inherited by housing class)
+ private synchronized AcceptThread getAcceptThread(){
+ return mSecureAcceptThread;
+ }
+ private synchronized void setAcceptThread(AcceptThread aThread){
+ mSecureAcceptThread = aThread;
+ }
+ protected synchronized void setStateManually(int state){
+ //Log.d(TAG, "Setting state from: " +mState + " to: " +state);
+ mState = state;
+ }
+ /**
+ * Set the current state of the chat connection
+ * @param state An integer defining the current connection state
+ */
+ private synchronized void setState(int state) {
+ //Log.d(TAG, "Setting state from: " +mState + " to: " +state);
+ int previousState = mState;
+ mState = state;
+
+ // Give the new state to the Handler so the UI Activity can update
+ //Also sending the previous state so we know if we lost a connection
+ mHandler.obtainMessage(SdlRouterService.MESSAGE_STATE_CHANGE, state, previousState).sendToTarget();
+ }
+
+ /**
+ * Return the current connection state. */
+ public synchronized int getState() {
+ return mState;
+ }
+
+ /**
+ * Start the chat service. Specifically start AcceptThread to begin a
+ * session in listening (server) mode. Called by the Activity onResume() */
+ public synchronized void start() {
+ //Log.d(TAG, "Starting up Bluetooth Server to Listen");
+ // Cancel any thread attempting to make a connection
+ if (serverInstance.mConnectThread != null) {serverInstance.mConnectThread.cancel(); serverInstance.mConnectThread = null;}
+
+ // Cancel any thread currently running a connection
+ if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
+ if (mConnectedWriteThread != null) {mConnectedWriteThread.cancel(); mConnectedWriteThread = null;}
+
+
+
+ // Start the thread to listen on a BluetoothServerSocket
+ if (getBluetoothSerialServerInstance().getAcceptThread() == null
+ //&& !listening
+ && serverInstance.mAdapter != null
+ && serverInstance.mAdapter.isEnabled()) {
+ //Log.d(TAG, "Secure thread was null, attempting to create new");
+ getBluetoothSerialServerInstance().setAcceptThread(new AcceptThread(true));
+ if(getBluetoothSerialServerInstance().getAcceptThread()!=null){
+ getBluetoothSerialServerInstance().setState(STATE_LISTEN);
+ getBluetoothSerialServerInstance().getAcceptThread().start();
+ }
+ }
+ }
+
+ /**
+ * Start the ConnectThread to initiate a connection to a remote device.
+ * @param device The BluetoothDevice to connect
+ */
+ public synchronized void connect(BluetoothDevice device) {
+ // Cancel any thread attempting to make a connection
+ if (mState == STATE_CONNECTING) {
+ if (serverInstance.mConnectThread != null) {serverInstance.mConnectThread.cancel(); serverInstance.mConnectThread = null;}
+ }
+
+ // Cancel any thread currently running a connection
+ if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
+ if (mConnectedWriteThread != null) {mConnectedWriteThread.cancel(); mConnectedWriteThread = null;}
+
+
+ // Cancel the accept thread because we only want to connect to one device
+ if (mSecureAcceptThread != null) {
+ mSecureAcceptThread.cancel();
+ mSecureAcceptThread = null;
+ }
+
+ // Start the thread to connect with the given device
+ serverInstance.mConnectThread = new ConnectThread(device);
+ serverInstance.mConnectThread.start();
+ serverInstance.setState(STATE_CONNECTING);
+ }
+
+ /**
+ * Start the ConnectedThread to begin managing a Bluetooth connection
+ * @param socket The BluetoothSocket on which the connection was made
+ * @param device The BluetoothDevice that has been connected
+ */
+ public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {
+ // Cancel the thread that completed the connection
+ if (getBluetoothSerialServerInstance().mConnectThread != null) {
+ getBluetoothSerialServerInstance().mConnectThread.cancel();
+ getBluetoothSerialServerInstance().mConnectThread = null;
+ }
+
+ // Cancel any thread currently running a connection
+ if (mConnectedThread != null) {
+ mConnectedThread.cancel();
+ mConnectedThread = null;
+ }
+ if (mConnectedWriteThread != null) {
+ mConnectedWriteThread.cancel();
+ mConnectedWriteThread = null;
+ }
+ // Cancel the accept thread because we only want to connect to one device
+ if (getBluetoothSerialServerInstance().mSecureAcceptThread != null) {
+ getBluetoothSerialServerInstance().mSecureAcceptThread.cancel();
+ getBluetoothSerialServerInstance().mSecureAcceptThread = null;
+ }
+
+ // Start the thread to manage the connection and perform transmissions
+ mConnectedThread = new ConnectedThread(socket);
+ mConnectedThread.start();
+ // Start the thread to manage the connection and perform transmissions
+ mConnectedWriteThread = new ConnectedWriteThread(socket);
+ mConnectedWriteThread.start();
+
+ //Store a static name of the device that is connected. We can do this since the only time
+ //we will access it will be when we receive a CONNECT packet from a device
+ if(device!=null && device.getName()!=null && device.getName()!=""){
+ currentlyConnectedDevice = device.getName();
+ }
+
+ // Send the name of the connected device back to the UI Activity
+ Message msg = mHandler.obtainMessage(SdlRouterService.MESSAGE_DEVICE_NAME);
+ Bundle bundle = new Bundle();
+ bundle.putString(DEVICE_NAME, device.getName());
+ msg.setData(bundle);
+ getBluetoothSerialServerInstance().mHandler.sendMessage(msg);
+ getBluetoothSerialServerInstance().setState(STATE_CONNECTED);
+ }
+
+ /**
+ * Stop all threads
+ */
+ public synchronized void stop() {
+ getBluetoothSerialServerInstance().stop(STATE_NONE);
+ }
+ protected synchronized void stop(int stateToTransitionTo) {
+ //Log.d(TAG, "Attempting to close the bluetooth serial server");
+ if (getBluetoothSerialServerInstance().mConnectThread != null) {
+ getBluetoothSerialServerInstance().mConnectThread.cancel();
+ getBluetoothSerialServerInstance().mConnectThread = null;
+ }
+
+ if (mConnectedThread != null) {
+ mConnectedThread.cancel();
+ mConnectedThread = null;
+ }
+ if (mConnectedWriteThread != null) {mConnectedWriteThread.cancel(); mConnectedWriteThread = null;}
+
+ if (mSecureAcceptThread != null) {
+ mSecureAcceptThread.cancel();
+ mSecureAcceptThread = null;
+ }
+
+ getBluetoothSerialServerInstance().setState(stateToTransitionTo);
+ }
+
+
+ /**
+ * Write to the ConnectedThread in an unsynchronized manner
+ * @param out The bytes to write
+ * @see ConnectedThread#write(byte[])
+ */
+ public void write(byte[] out, int offset, int count) {
+ // Create temporary object
+ ConnectedWriteThread r;
+ // Synchronize a copy of the ConnectedThread
+ synchronized (this) {
+ if (mState != STATE_CONNECTED) return;
+ r = mConnectedWriteThread;
+ //r.write(out,offset,count);
+ }
+ // Perform the write unsynchronized
+ r.write(out,offset,count);
+ }
+
+ /**
+ * Indicate that the connection attempt failed and notify the UI Activity.
+ */
+ private void connectionFailed() {
+ // Send a failure message back to the Activity
+ Message msg = mHandler.obtainMessage(SdlRouterService.MESSAGE_TOAST);
+ Bundle bundle = new Bundle();
+ bundle.putString(TOAST, "Unable to connect device");
+ msg.setData(bundle);
+ getBluetoothSerialServerInstance().mHandler.sendMessage(msg);
+
+ // Start the service over to restart listening mode
+ // BluetoothSerialServer.this.start();
+ }
+
+ /**
+ * Indicate that the connection was lost and notify the UI Activity.
+ */
+ private void connectionLost() {
+ listening = false;
+ // Send a failure message back to the Activity
+ Message msg = mHandler.obtainMessage(SdlRouterService.MESSAGE_TOAST);
+ Bundle bundle = new Bundle();
+ bundle.putString(TOAST, "Device connection was lost");
+ msg.setData(bundle);
+ getBluetoothSerialServerInstance().mHandler.sendMessage(msg);
+ getBluetoothSerialServerInstance().stop();
+
+ }
+
+ private void timerDelayRemoveDialog(final BluetoothSocket sock){
+ getBluetoothSerialServerInstance().timeOutHandler = new Handler();
+ getBluetoothSerialServerInstance().socketRunable = new Runnable() {
+ public void run() {
+ //Log.e(TAG, "BLUETOOTH SOCKET CONNECT TIMEOUT - ATTEMPT TO CLOSE SOCKET");
+ try {
+ sock.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ };
+ getBluetoothSerialServerInstance().timeOutHandler.postDelayed(socketRunable, msTillTimeout);
+ }
+
+
+
+ /**
+ * This thread runs while listening for incoming connections. It behaves
+ * like a server-side client. It runs until a connection is accepted
+ * (or until cancelled).
+ */
+ private class AcceptThread extends Thread {
+ // The local server socket
+ private String mSocketType;
+ final BluetoothServerSocket mmServerSocket;
+
+ @SuppressLint("NewApi")
+ public AcceptThread(boolean secure) {
+ synchronized(threadLock){
+ listening = false;
+ //Log.d(TAG, "Creating an Accept Thread");
+ BluetoothServerSocket tmp = null;
+ mSocketType = secure ? "Secure":"Insecure";
+ // Create a new listening server socket
+ try {
+ if (secure) {
+ tmp = getBluetoothSerialServerInstance().mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, SERVER_UUID);
+ listening = true;
+ }
+ } catch (IOException e) {
+ listening = false;
+ //Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);
+ //Let's try to shut down this thead
+ }catch(SecurityException e2){
+ //Log.e(TAG, "<LIVIO> Security Exception in Accept Thread - "+e2.toString());
+ listening = false;
+ interrupt();
+ }
+ mmServerSocket = tmp;
+ //Should only log on debug
+ //BluetoothSocket mySock = getBTSocket(mmServerSocket);
+ //Log.d(TAG, "Accepting Connections on SDP Server Port Number: " + getChannel(mySock) + "\r\n");
+ }
+ }
+
+ public void run() {
+ synchronized(threadLock){
+ Log.d(TAG, "Socket Type: " + mSocketType +
+ " BEGIN mAcceptThread" + this);
+ setName("AcceptThread" + mSocketType);
+
+ BluetoothSocket socket = null;
+ int listenAttempts = 0;
+
+ // Listen to the server socket if we're not connected
+ while (mState != STATE_CONNECTED) {
+ try {
+ if(listenAttempts>=5){
+ Log.e(TAG, "Complete failure in attempting to listen for Bluetooth connection, erroring out.");
+ getBluetoothSerialServerInstance().stop(STATE_ERROR);
+ return;
+ }
+ listenAttempts++;
+ Log.d(TAG, "SDL Bluetooth Accept thread is running.");
+
+ // This is a blocking call and will only return on a
+ // successful connection or an exception
+ if(mmServerSocket!=null){
+
+ socket = mmServerSocket.accept();
+
+ }
+ else{
+ Log.e(TAG, "Listening Socket was null, stopping the bluetooth serial server.");
+ getBluetoothSerialServerInstance().stop(STATE_ERROR);
+ return;
+ }
+ } catch (IOException e) {
+ listening = false;
+ Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);
+ interrupt();
+ return;
+ }
+
+ // If a connection was accepted
+ if (socket != null) {
+ synchronized (MultiplexBluetoothTransport.this) {
+ switch (mState) {
+ case STATE_LISTEN:
+ case STATE_CONNECTING:
+ // Situation normal. Start the connected thread.
+ getBluetoothSerialServerInstance().connected(socket, socket.getRemoteDevice());
+
+ break;
+ case STATE_NONE:
+ case STATE_CONNECTED:
+ // Either not ready or already connected. Terminate new socket.
+ try {
+ Log.d(TAG, "Close unwanted socket");
+ if(socket!=null){
+ socket.close();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Could not close unwanted socket", e);
+ }
+ break;
+ }
+ }
+ }
+ }
+ Log.d(TAG, mState + " END mAcceptThread, socket Type: " + mSocketType);
+ }
+ }
+
+ public synchronized void cancel() {
+ listening = false;
+ Log.d(TAG, mState + " Socket Type " + mSocketType + " cancel ");
+ try {
+ if(mmServerSocket != null){
+ mmServerSocket.close();
+ }
+
+ } catch (IOException e) {
+ Log.e(TAG, mState + " Socket Type " + mSocketType + " close() of server failed "+ e.getStackTrace());
+ }
+ }
+ }
+
+
+ /**
+ * This thread runs while attempting to make an outgoing connection
+ * with a device. It runs straight through; the connection either
+ * succeeds or fails.
+ */
+ private class ConnectThread extends Thread {
+ private BluetoothSocket mmSocket;
+ private final BluetoothDevice mmDevice;
+ public ConnectThread(BluetoothDevice device) {
+ mmDevice = device;
+ //Log.d(TAG, "Attempting to conenct to " + device.getName());
+ //Log.d(TAG, "UUID to conenct to " + SERVER_UUID.toString());
+
+ }
+
+ public void attemptCancelDiscovery(){
+ try{
+ mAdapter.cancelDiscovery();
+ }catch(SecurityException e2){
+ Log.e(TAG, "Don't have required permision to cancel discovery. Moving on");
+ }
+ }
+
+ public void run() {
+ setName("ConnectThread");
+ // Always cancel discovery because it will slow down a connection
+ attemptCancelDiscovery();
+ // Make a connection to the BluetoothSocket
+ int attemptCount = 0;
+ boolean success = false;
+ Looper.prepare();
+
+ while(attemptCount < 5)
+ {
+ //Looper.loop()
+ attemptCount++;
+ try {
+ // This is a blocking call and will only return on a
+ // successful connection or an exception
+ mBluetoothLevel = SdlRouterService.getBluetoothPrefs(SHARED_PREFS);
+ long waitTime = 3000;
+ try {
+ Thread.sleep(waitTime);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ //This sequence tries to use reflection first to connect with a certain number of phones that require this
+ //Next is the most common methods for phones
+ //Finally if both have failed an insecure connection is attempted, though this is not available on lower SDK's
+ boolean tryInsecure = false;
+ boolean trySecure = false;
+ //Log.i(TAG,mmDevice.getName() + " socket connecting...");
+
+ if(mBluetoothLevel<=1){
+ try {
+ SdlRouterService.setBluetoothPrefs(2,SHARED_PREFS);
+ Method m = mmDevice.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
+ //Log.i(TAG,"connecting using createRfcommSocket");
+ mmSocket = (BluetoothSocket) m.invoke(mmDevice, Integer.valueOf(1));
+ if(mmSocket!=null){
+ //Looper.prepare();
+ timerDelayRemoveDialog(mmSocket);
+ //Looper.loop();
+ mmSocket.connect();
+ timeOutHandler.removeCallbacks(socketRunable);
+ Looper.myLooper().quit();
+ success=true;
+ SdlRouterService.setBluetoothPrefs(1,SHARED_PREFS);
+ break;
+ } else{trySecure = true;}
+
+ } catch (Exception e) {
+ //Log.e(TAG,"createRfcommSocket exception - " + e.toString());
+ SdlRouterService.setBluetoothPrefs(0,SHARED_PREFS);
+
+ trySecure = true;
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e2) {
+ e2.printStackTrace();
+ }
+ }
+ }else{trySecure = true;}
+ if(trySecure && mBluetoothLevel<=2){
+ try {
+ SdlRouterService.setBluetoothPrefs(3,SHARED_PREFS);
+ //Log.i(TAG, "connecting using createRfcommSocketToServiceRecord ");
+ mmSocket = mmDevice.createRfcommSocketToServiceRecord(SERVER_UUID);
+ if(mmSocket!=null){
+ //Looper.prepare();
+ timerDelayRemoveDialog(mmSocket);
+ //Looper.loop();
+ mmSocket.connect();
+ timeOutHandler.removeCallbacks(socketRunable);
+ Looper.myLooper().quit();
+ success=true;
+ SdlRouterService.setBluetoothPrefs(2,SHARED_PREFS);
+ break;
+ }else{tryInsecure = true;}
+ } catch (IOException io) {
+ tryInsecure = true;
+ Log.e(TAG,"createRfcommSocketToServiceRecord exception - " + io.toString());
+ SdlRouterService.setBluetoothPrefs(0,SHARED_PREFS);
+
+ } catch (Exception e){
+ Log.e(TAG,"createRfcommSocketToServiceRecord exception - " + e.toString());
+ SdlRouterService.setBluetoothPrefs(0,SHARED_PREFS);
+
+ }
+ }else{tryInsecure = true;}
+
+ if (tryInsecure && mBluetoothLevel<=3) {
+ // try again using insecure comm if available
+ try {
+ SdlRouterService.setBluetoothPrefs(4,SHARED_PREFS);
+ //Log.i(TAG,"connecting using createInsecureRfcommSocketToServiceRecord");
+ Method m = mmDevice.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] {UUID.class});
+ mmSocket = (BluetoothSocket) m.invoke(mmDevice, new Object[] {SERVER_UUID});
+ //Looper.prepare();
+ timerDelayRemoveDialog(mmSocket);
+ //Looper.loop();
+ mmSocket.connect();
+ timeOutHandler.removeCallbacks(socketRunable);
+ Looper.myLooper().quit();
+ success=true;
+ tryInsecure = false;
+ SdlRouterService.setBluetoothPrefs(3,SHARED_PREFS);
+ break;
+ } catch (NoSuchMethodException ie) {
+ SdlRouterService.setBluetoothPrefs(0,SHARED_PREFS);
+ } catch (IllegalAccessException ie) {
+ SdlRouterService.setBluetoothPrefs(0,SHARED_PREFS);
+ } catch (InvocationTargetException ie) {
+ SdlRouterService.setBluetoothPrefs(0,SHARED_PREFS);
+ }
+ }
+ if (tryInsecure && mBluetoothLevel<=4) {
+ // try again using insecure comm if available
+ try {
+ SdlRouterService.setBluetoothPrefs(0,SHARED_PREFS);
+ //Log.i(TAG,"connecting using createInsecureRfcommSocket()");
+ Method m = mmDevice.getClass().getMethod("createInsecureRfcommSocket()", new Class[] {UUID.class});
+ mmSocket = (BluetoothSocket) m.invoke(mmDevice, new Object[] {SERVER_UUID});
+ //Looper.prepare();
+ timerDelayRemoveDialog(mmSocket);
+ //Looper.loop();
+ mmSocket.connect();
+ timeOutHandler.removeCallbacks(socketRunable);
+ Looper.myLooper().quit();
+ success=true;
+ SdlRouterService.setBluetoothPrefs(4,SHARED_PREFS);
+ break;
+ } catch (NoSuchMethodException ie) {
+ SdlRouterService.setBluetoothPrefs(0,SHARED_PREFS);
+ } catch (IllegalAccessException ie) {
+ SdlRouterService.setBluetoothPrefs(0,SHARED_PREFS);
+ } catch (InvocationTargetException ie) {
+ SdlRouterService.setBluetoothPrefs(0,SHARED_PREFS);
+ }
+ }
+ } catch (IOException e) {
+ connectionFailed();
+ Log.e(TAG,e.getClass().getSimpleName()
+ + " caught connecting to the bluetooth socket: "
+ + e.toString());
+ try {
+ mmSocket.close();
+ } catch (IOException e2) {
+ Log.e(TAG,"unable to close() socket during connection failure" + e2);
+ }
+ return;
+ }
+ }
+ // Reset the ConnectThread because we're done
+ if(success)
+ {
+ synchronized (MultiplexBluetoothTransport.this) {
+ mConnectThread = null;
+ }
+
+
+ // Start the connected thread
+
+ connected(mmSocket, mmDevice);
+ }
+ else
+ {
+ Log.e(TAG, "There was a problem opening up RFCOMM");
+ }
+ }
+
+ public void cancel() {
+ try {
+ Log.d(TAG, "Calling Cancel in the connect thread");
+ mmSocket.close();
+ } catch (IOException e) {
+ // close() of connect socket failed
+ }
+ catch(NullPointerException e){
+ //mSocket was pry never initialized
+ }
+ }
+ }
+
+ /**
+ * This thread runs during a connection with a remote device.
+ * It handles all incoming and outgoing transmissions.
+ */
+ private class ConnectedWriteThread extends Thread {
+ private final BluetoothSocket mmSocket;
+ private final OutputStream mmOutStream;
+
+
+ public ConnectedWriteThread(BluetoothSocket socket) {
+ //Log.d(TAG, "Creating a Connected - Write Thread");
+ mmSocket = socket;
+ OutputStream tmpOut = null;
+ setName(" Livio Bluetooth Write Thread");
+ // Get the BluetoothSocket input and output streams
+ try {
+ tmpOut = socket.getOutputStream();
+ } catch (IOException e) {
+ // temp sockets not created
+ Log.e(TAG, "Connected Write Thread: " + e.getMessage());
+ }
+ mmOutStream = tmpOut;
+
+
+ }
+ /**
+ * Write to the connected OutStream.
+ * @param buffer The bytes to write
+ */
+ public void write(byte[] buffer, int offset, int count) {
+ try {
+ if(buffer==null){
+ Log.w(TAG, "Can't write to device, nothing to send");
+ return;
+ }
+ //This would be a good spot to log out all bytes received
+ mmOutStream.write(buffer, offset, count);
+ //Log.w(TAG, "Wrote out to device: bytes = "+ count);
+ } catch (IOException e) {
+ // Exception during write
+ //OMG! WE MUST NOT BE CONNECTED ANYMORE! LET THE USER KNOW
+ Log.e(TAG, "Error sending bytes to connected device!");
+ getBluetoothSerialServerInstance().connectionLost();
+ }
+ }
+
+ public synchronized void cancel() {
+ try {
+ Log.d(TAG, "Calling Cancel in the write thread");
+ if(mmOutStream!=null){
+ mmOutStream.flush();
+ mmOutStream.close();
+
+ }
+ if(mmSocket!=null){
+ mmSocket.close();
+ }
+ } catch (IOException e) {
+ // close() of connect socket failed
+ Log.d(TAG, "Write Thread: " + e.getMessage());
+ }
+ }
+ }
+
+ private class ConnectedThread extends Thread {
+ private final BluetoothSocket mmSocket;
+ private final InputStream mmInStream;
+ SdlPsm psm;
+ public ConnectedThread(BluetoothSocket socket) {
+ this.psm = new SdlPsm();
+ //Log.d(TAG, "Creating a Connected - Read Thread");
+ mmSocket = socket;
+ InputStream tmpIn = null;
+ setName(" Livio Bluetooth Read Thread");
+ // Get the BluetoothSocket input and output streams
+ try {
+ tmpIn = socket.getInputStream();
+ } catch (IOException e) {
+ // temp sockets not created
+ Log.e(TAG, "Connected Read Thread: "+e.getMessage());
+ }
+ mmInStream = tmpIn;
+
+ }
+
+ @SuppressLint("NewApi")
+ public void run() {
+ Log.d(TAG, "Running the Connected Thread");
+ byte input = 0;
+ MultiplexBluetoothTransport.currentlyConnectedDevice = mmSocket.getRemoteDevice().getName();
+ MultiplexBluetoothTransport.currentlyConnectedDeviceAddress = mmSocket.getRemoteDevice().getAddress();
+ // Keep listening to the InputStream while connected
+ boolean stateProgress;
+
+ psm.reset();
+
+ while (true) {
+ try {
+ input = (byte)mmInStream.read();
+ // Send the response of what we received
+ stateProgress = psm.handleByte(input);
+ if(!stateProgress){//We are trying to weed through the bad packet info until we get something
+ //Log.w(TAG, "Packet State Machine did not move forward from state - "+ psm.getState()+". PSM being Reset.");
+ psm.reset();
+ continue;
+ }
+
+ if(psm.getState() == SdlPsm.FINISHED_STATE){
+ //Log.d(TAG, "Packet formed, sending off");
+ mHandler.obtainMessage(SdlRouterService.MESSAGE_READ, psm.getFormedPacket()).sendToTarget();
+ psm.reset();
+
+ }
+ }catch (IOException e){
+ Log.e(TAG, "Lost connection in the Connected Thread");
+ e.printStackTrace();
+ connectionLost();
+ break;
+ }
+ }
+ }
+
+
+ public synchronized void cancel() {
+ try {
+ //Log.d(TAG, "Calling Cancel in the Read thread");
+ if(mmInStream!=null){
+ mmInStream.close();
+ }
+ if(mmSocket!=null){
+ mmSocket.close();
+ }
+
+ } catch (IOException e) {
+ //Log.trace(TAG, "Read Thread: " + e.getMessage());
+
+ }
+ }
+ }
+
+ public boolean isConnected()
+ {
+ return !(mState == STATE_NONE);
+ }
+
+
+ public BluetoothSocket getBTSocket(BluetoothServerSocket bsSocket){
+ if(bsSocket == null){
+ return null;
+ }
+ Field[] f = bsSocket.getClass().getDeclaredFields();
+
+ //int channel = -1;
+ BluetoothSocket mySocket = null;
+ for (Field field : f) {
+ if(field.getName().equals("mSocket")){
+ field.setAccessible(true);
+ try {
+
+ mySocket = (BluetoothSocket) field.get(bsSocket);
+ return mySocket;
+ //channel = field.getInt(bsSocket);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ field.setAccessible(false);
+ }
+ }
+
+ return null;
+ }
+
+ public int getChannel(BluetoothSocket bsSocket){
+
+ int channel = -1;
+ if (bsSocket == null){
+ return channel;
+ }
+
+ Field[] f = bsSocket.getClass().getDeclaredFields();
+
+ for (Field field : f) {
+ if(field.getName().equals("mPort")){
+ field.setAccessible(true);
+ try {
+
+
+ channel = field.getInt(bsSocket);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ field.setAccessible(false);
+ }
+ }
+
+ return channel;
+ }
+
+}
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/MultiplexTransport.java b/sdl_android_lib/src/com/smartdevicelink/transport/MultiplexTransport.java
new file mode 100644
index 000000000..5556d9af1
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/MultiplexTransport.java
@@ -0,0 +1,314 @@
+package com.smartdevicelink.transport;
+
+import android.annotation.SuppressLint;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Build;
+import android.os.Looper;
+import android.os.Parcelable;
+import android.util.Log;
+
+import com.smartdevicelink.SdlConnection.SdlConnection;
+import com.smartdevicelink.exception.SdlException;
+import com.smartdevicelink.protocol.SdlPacket;
+import com.smartdevicelink.transport.enums.TransportType;
+
+public class MultiplexTransport extends SdlTransport{
+ private final static String TAG = "Multiplex Transport";
+ private String sComment = "I'm_a_little_teapot";
+
+ TransportBrokerThread brokerThread;
+ protected boolean isDisconnecting = false;
+ MultiplexTransportConfig transportConfig;
+ public MultiplexTransport(MultiplexTransportConfig transportConfig, final ITransportListener transportListener){
+ super(transportListener);
+ if(transportConfig == null){
+ this.handleTransportError("Transport config was null", null);
+ }
+ this.transportConfig = transportConfig;
+ brokerThread = new TransportBrokerThread(transportConfig.context, transportConfig.appId, transportConfig.service);
+ brokerThread.start();
+ isDisconnecting = false;
+ //brokerThread.initTransportBroker();
+ //brokerThread.start();
+
+ }
+
+ public boolean forceHardwareConnectEvent(TransportType type){
+ if(brokerThread!=null){
+ brokerThread.onHardwareConnected(type);
+ return true;
+ }
+ Log.w(TAG, "Transport broker thread was null, nothing to force connect. Are we disconnecting? " + isDisconnecting);
+ return false;
+
+ }
+
+ public boolean isDisconnecting(){
+ return this.isDisconnecting;
+ }
+ /**
+ * Returns the config that was used to create this transport
+ * @return
+ */
+ public MultiplexTransportConfig getConfig(){
+ return this.transportConfig;
+ }
+
+ public boolean requestNewSession(){
+ if(brokerThread!=null){
+ brokerThread.requestNewSession();
+ return true;
+ }
+ return false;
+ }
+
+ public void removeSession(long sessionId){
+ if(brokerThread!=null){
+ brokerThread.removeSession(sessionId);
+ }
+ }
+
+ /**
+ * Overridden abstract method which returns specific type of this transport.
+ *
+ * @return Constant value - TransportType.BLUETOOTH.
+ * @see TransportType
+ */
+ public TransportType getTransportType() {
+ return TransportType.MULTIPLEX;
+ }
+
+ @Override
+ public String getBroadcastComment() {
+ return sComment;
+ }
+
+ @Override
+ protected boolean sendBytesOverTransport(SdlPacket packet) {
+ if(brokerThread!=null){
+ brokerThread.sendPacket(packet);
+ return true;
+ }
+ return false; //Sure why not.
+ }
+
+ @Override
+ public void openConnection() throws SdlException {
+ Log.d(TAG, "Open connection");
+ if(brokerThread!=null){
+ brokerThread.startConnection();
+ }//else should log out
+
+ }
+
+ @Override
+ public void disconnect() {
+ if(isDisconnecting){
+ return;
+ }
+ Log.d(TAG, "Close connection");
+ this.isDisconnecting= true;
+ if(brokerThread!= null){
+ brokerThread.cancel();
+ brokerThread = null;
+ }
+ handleTransportDisconnected(TransportType.MULTIPLEX.name());
+ isDisconnecting = false;
+
+ }
+
+
+
+ @Override
+ protected void handleTransportError(String message, Exception ex) {
+ if(brokerThread!=null){
+ brokerThread.cancel();
+ //brokerThread.interrupt();
+ brokerThread = null;
+ }
+ super.handleTransportError(message, ex);
+ }
+
+
+ public boolean isPendingConnected(){
+ if(brokerThread!=null){
+ return brokerThread.queueStart;
+ }
+ return false;
+ }
+ /**
+ * This thread will handle the broker transaction with the router service.
+ *
+ */
+ protected class TransportBrokerThread extends Thread{
+ boolean connected = false; //This helps clear up double on hardware connects
+ TransportBroker broker;
+ boolean queueStart = false;
+ final Context context;
+ final String appId;
+ final ComponentName service;
+ Looper threadLooper = null;
+ /**
+ * Thread will automatically start to prepare its looper.
+ * @param context
+ * @param appId
+ */
+ public TransportBrokerThread(Context context, String appId, ComponentName service){
+ //this.start();
+ super();
+ this.context = context;
+ this.appId = appId;
+ this.service = service;
+ //initTransportBroker(context, appId);
+ }
+
+ public void startConnection(){
+ synchronized(this){
+ connected = false;
+ if(broker!=null){
+ try{
+ broker.start();
+ }catch(Exception e){
+ handleTransportError("Error starting transport", e);
+ }
+ }else{
+ queueStart = true;
+ }
+ }
+ }
+
+ @SuppressLint("NewApi")
+ public void cancel(){
+ if(broker!=null){
+ broker.stop();
+ broker = null;
+ }
+ connected = false;
+ if(threadLooper !=null){
+ if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.JELLY_BEAN_MR2){
+ threadLooper.quitSafely();
+ }else{
+ threadLooper.quit();
+ }
+ threadLooper = null;
+ }
+ //this.interrupt();
+
+ }
+
+ public void onHardwareConnected(TransportType type){
+ if(broker!=null){
+ broker.onHardwareConnected(type);
+ }else{
+ queueStart = true;
+ }
+ }
+
+ public void sendPacket(SdlPacket packet){
+ broker.sendPacketToRouterService(packet);
+ }
+
+ public void requestNewSession(){
+ if(broker!=null){
+ broker.requestNewSession();
+ }
+ }
+ public void removeSession(long sessionId){
+ if(broker!=null){
+ broker.removeSession(sessionId);
+ }
+ }
+ @Override
+ public void run() {
+ Looper.prepare();
+
+ if(broker==null){Log.d("JOEY", "Starting broker");
+ synchronized(this){
+ initTransportBroker();
+ if(queueStart){
+ try{
+ broker.start();
+ }catch(Exception e){
+ handleTransportError("Error starting transport", e);
+ }
+ }
+ this.notify();
+ }
+ }
+ threadLooper = Looper.myLooper();
+ Looper.loop();
+ Log.i(TAG, "Looper has finished. Thread should be sutting down");
+
+ }
+
+ public void initTransportBroker(){
+
+ broker = new TransportBroker(context, appId, service){
+
+ @Override
+ public boolean onHardwareConnected(TransportType type) {
+ if(super.onHardwareConnected(type)){
+ Log.d(TAG, "On transport connected...");
+ if(!connected){
+ connected = true;
+ Log.d(TAG, "Handling transport connected");
+ handleTransportConnected();
+ }else{Log.d(TAG, "Already connected");}
+ return true;
+ }else{
+ try{
+ this.start();
+ }catch(Exception e){
+ handleTransportError("Error starting transport", e);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onHardwareDisconnected(TransportType type) {
+ super.onHardwareDisconnected(type);
+ if(connected){
+ Log.d(TAG, "Handling disconnect");
+ connected = false;
+ SdlConnection.enableLegacyMode(isLegacyModeEnabled(), TransportType.BLUETOOTH);
+ if(isLegacyModeEnabled()){
+ Log.d(TAG, "Handle transport disconnect, legacy mode enabled");
+ this.stop();
+ isDisconnecting = true;
+ //handleTransportDisconnected("");
+ handleTransportError("",null); //This seems wrong, but it works
+ }else{
+ Log.d(TAG, "Handle transport Error");
+ isDisconnecting = true;
+ handleTransportError("",null); //This seems wrong, but it works
+ }
+ }
+ }
+
+ @Override
+ public void onLegacyModeEnabled() {
+ super.onLegacyModeEnabled();
+ SdlConnection.enableLegacyMode(isLegacyModeEnabled(), TransportType.BLUETOOTH);
+ if(isLegacyModeEnabled()){
+ Log.d(TAG, "Handle on legacy mode enabled");
+ this.stop();
+ isDisconnecting = true;
+ //handleTransportDisconnected("");
+ handleTransportError("",null); //This seems wrong, but it works
+ }
+ }
+
+ @Override
+ public void onPacketReceived(Parcelable packet) {
+ if(packet!=null){
+ SdlPacket sdlPacket = (SdlPacket)packet;
+ handleReceivedPacket(sdlPacket);
+ }
+ }
+ };
+ }
+
+ }
+}
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/MultiplexTransportConfig.java b/sdl_android_lib/src/com/smartdevicelink/transport/MultiplexTransportConfig.java
new file mode 100644
index 000000000..b15a6cbb9
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/MultiplexTransportConfig.java
@@ -0,0 +1,51 @@
+package com.smartdevicelink.transport;
+
+import com.smartdevicelink.transport.enums.TransportType;
+
+import android.content.ComponentName;
+import android.content.Context;
+
+public class MultiplexTransportConfig extends BaseTransportConfig{
+
+ Context context;
+ String appId;
+ ComponentName service;
+
+
+
+
+ public MultiplexTransportConfig(Context context, String appId) {
+ this.context = context;
+ this.appId = appId;
+ }
+
+
+ /**
+ * Overridden abstract method which returns specific type of this transport configuration.
+ *
+ * @return Constant value TransportType.MULTIPLEX.
+ *
+ * @see TransportType
+ */
+ public TransportType getTransportType() {
+ return TransportType.MULTIPLEX;
+ }
+
+ public Context getContext(){
+ return this.context;
+ }
+
+
+ public ComponentName getService() {
+ return service;
+ }
+
+
+ public void setService(ComponentName service) {
+ this.service = service;
+ }
+
+
+
+
+}
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/RouterServiceValidator.java b/sdl_android_lib/src/com/smartdevicelink/transport/RouterServiceValidator.java
new file mode 100644
index 000000000..5809a19aa
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/RouterServiceValidator.java
@@ -0,0 +1,575 @@
+package com.smartdevicelink.transport;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningServiceInfo;
+import android.bluetooth.BluetoothAdapter;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.util.Log;
+
+import com.smartdevicelink.util.HttpRequestTask;
+import com.smartdevicelink.util.HttpRequestTask.HttpRequestTaskCallback;
+
+/**
+ * This class will tell us if the currently running router service is valid or not.
+ * To use this class simply create a new instance of RouterServiceValidator with a supplied context.
+ * After that, you have the option to set if you want to test in a production setting. If not, it will default to a debug setting.
+ * Once you are ready to check if the router service is trusted you simply call routerServiceValidator.validate();
+ * <br><br> This validator should be passed into the multiplexing transport construction as well.
+ * @author Joey Grover
+ *
+ */
+public class RouterServiceValidator {
+ private static final String TAG = "PackageCheckUtl";
+ public static final String ROUTER_SERVICE_PACKAGE = "com.sdl.router";
+
+ private static final String REQUEST_PREFIX = "https://woprjr.smartdevicelink.com/api/1/applications/queryTrustedRouters";
+
+ private static final String DEFAULT_APP_LIST = "{\"response\": {\"com.livio.sdl\" : { \"versionBlacklist\":[] }, \"com.lexus.tcapp\" : { \"versionBlacklist\":[] }, \"com.toyota.tcapp\" : { \"versionBlacklist\": [] } , \"com.sdl.router\":{\"versionBlacklist\": [] } }}";
+
+
+ private static final String JSON_RESPONSE_OBJECT_TAG = "response";
+ private static final String JSON_RESONSE_APP_VERSIONS_TAG = "versionBlacklist";
+
+ private static final String JSON_PUT_ARRAY_TAG = "installedApps";
+ private static final String JSON_APP_PACKAGE_TAG = "packageName";
+ private static final String JSON_APP_VERSION_TAG = "version";
+
+
+ private static final long REFRESH_TRUSTED_APP_LIST_TIME = 3600000 * 24; // 24 hours in ms
+
+ private static final String SDL = "sdl";
+ private static final String SDL_PACKAGE_LIST = "sdl_package_list";
+ private static final String SDL_PACKAGE_LIST_TIMESTAMP = "sdl_package_list_timestamp";
+
+ //Flags to aid in debugging and production checks
+ public static final int FLAG_DEBUG_NONE = 0x00;
+ public static final int FLAG_DEBUG_PACKAGE_CHECK = 0x01;
+ /**
+ * This will flag the validator to check for app version during debugging.
+ * <br><br><b>NOTE: This flag will include a package check as well.
+ */
+ public static final int FLAG_DEBUG_VERSION_CHECK = 0x03; //We use 3 becuase version check will be 2, but since a version check implies a package check we do 2+1=3;
+ public static final int FLAG_DEBUG_INSTALLED_FROM_CHECK = 0x04;
+ public static final int FLAG_DEBUG_USE_TIMESTAMP_CHECK = 0x05;
+
+ public static final int FLAG_DEBUG_PERFORM_ALL_CHECKS = 0xFF;
+
+
+ private int flags = FLAG_DEBUG_NONE;
+
+ private Context context= null;
+ private boolean inDebugMode = false;
+ @SuppressWarnings("unused")
+ private static boolean pendingListRefresh = false;
+
+ private ComponentName service;//This is how we can save different routers over another in a waterfall method if we choose to.
+
+
+ public RouterServiceValidator(Context context){
+ this.context = context;
+ inDebugMode = inDebugMode();
+ }
+
+ public RouterServiceValidator(Context context, ComponentName service){
+ this.context = context;
+ inDebugMode = inDebugMode();
+ this.service = service;
+ }
+ /**
+ * Main function to call to ensure we are connecting to a validated router service
+ * @return whether or not the currently running router service can be trusted.
+ */
+ public boolean validate(){
+ PackageManager pm = context.getPackageManager();
+ //Grab the package for the currently running router service. We need this call regardless of if we are in debug mode or not.
+ String packageName = null;
+
+ if(this.service != null){
+ Log.d(TAG, "Supplied service name of " + this.service.getClassName());
+ if(!isServiceRunning(context,this.service)){
+ //This means our service isn't actually running, so set to null. Hopefully we can find a real router service after this.
+ service = null;
+ Log.w(TAG, "Supplied service is not actually running.");
+ }
+ }
+ if(this.service == null){
+ this.service= componentNameForServiceRunning(pm); //Change this to an array if multiple services are started?
+ if(this.service == null){ //if this is still null we know there is no service running so we can return false
+ wakeUpRouterServices();
+ return false;
+ }
+ }
+
+ Log.d(TAG, "Checking app package: " + service.getClassName());
+ packageName = this.appPackageForComponentName(service, pm);
+
+
+ if(packageName!=null){//Make sure there is a service running
+ if(wasInstalledByAppStore(packageName)){ //Was this package installed from a trusted app store
+ if( isTrustedPackage(packageName, pm)){//Is this package on the list of trusted apps.
+ return true;
+ }
+ }
+ }//No running service found. Might need to attempt to start one
+ //TODO spin up a known good router service
+
+ if(context.getPackageName().equalsIgnoreCase(packageName)){
+ Log.d(TAG, "It's our router service running, so time to shut it down");
+ Intent intent = new Intent();
+ intent.setComponent(service);
+ try{context.stopService(intent);}catch(Exception e){}
+ }
+ wakeUpRouterServices();
+ return false;
+ }
+
+ /**
+ * This will ensure that all router services are aware that there are no valid router services running and should start up
+ */
+ private void wakeUpRouterServices(){
+ if(BluetoothAdapter.getDefaultAdapter()!=null && BluetoothAdapter.getDefaultAdapter().isEnabled()){
+ Intent intent = new Intent(TransportConstants.START_ROUTER_SERVICE_ACTION);
+ intent.putExtra(TransportConstants.PING_ROUTER_SERVICE_EXTRA, true);
+ context.sendBroadcast(intent);
+ }
+ }
+ public ComponentName getService(){
+ return this.service;
+ }
+
+ private boolean shouldOverrideVersionCheck(){
+ return (this.inDebugMode && ((this.flags & FLAG_DEBUG_VERSION_CHECK) != FLAG_DEBUG_VERSION_CHECK));
+ }
+
+ private boolean shouldOverridePackageName(){
+ return (this.inDebugMode && ((this.flags & FLAG_DEBUG_PACKAGE_CHECK) != FLAG_DEBUG_PACKAGE_CHECK));
+ }
+
+ private boolean shouldOverrideInstalledFrom(){
+ return (this.inDebugMode && ((this.flags & FLAG_DEBUG_INSTALLED_FROM_CHECK) != FLAG_DEBUG_INSTALLED_FROM_CHECK));
+ }
+
+ @SuppressWarnings("unused")
+ private boolean shouldOverrideTimeCheck(){
+ return (this.inDebugMode && ((this.flags & FLAG_DEBUG_USE_TIMESTAMP_CHECK) != FLAG_DEBUG_USE_TIMESTAMP_CHECK));
+ }
+
+
+ /**
+ * Use this method if you would like to test your app in a production setting rather than defaulting to a
+ * debug mode where you connect to whatever router service is running.
+ * <br><br><b>These flags are only used in debugging mode. During production they will be ignored.</b>
+ * @param flags
+ */
+ public void setFlags(int flags){
+ this.flags = flags;
+ }
+
+ /**
+ * This method will find which router service is running. Use that info to find out more about that app and service.
+ * It will store the found service for later use and return the package name if found.
+ * @param context
+ * @return
+ */
+ public ComponentName componentNameForServiceRunning(PackageManager pm){
+ if(context==null){
+ return null;
+ }
+ ActivityManager manager = (ActivityManager) context.getSystemService("activity");
+ //PackageManager pm = context.getPackageManager();
+
+
+ for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
+ //Log.d(TAG, service.service.getClassName());
+ //We will check to see if it contains this name, should be pretty specific
+ if ((service.service.getClassName()).toLowerCase(Locale.US).contains(SdlBroadcastReceiver.SDL_ROUTER_SERVICE_CLASS_NAME)){
+ //this.service = service.service; //This is great
+ if(service.started && service.restarting==0){ //If this service has been started and is not crashed
+ return service.service; //appPackageForComponenetName(service.service,pm);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the package name for the component name
+ * @param cn
+ * @param pm
+ * @return
+ */
+ private String appPackageForComponentName(ComponentName cn,PackageManager pm ){
+ if(cn!=null && pm!=null){
+ ServiceInfo info;
+ try {
+ info = pm.getServiceInfo(cn, 0);
+ return info.applicationInfo.packageName;
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ return null;
+
+ }
+
+ /**
+ * Check to see if the app was installed from a trusted app store.
+ * @param packageName the package name of the app to be tested
+ * @return whether or not the app was installed from a trusted app store
+ */
+ public boolean wasInstalledByAppStore(String packageName){
+ if(shouldOverrideInstalledFrom()){
+ return true;
+ }
+ PackageManager packageManager = context.getPackageManager();
+ try {
+ final ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
+ if(TrustedAppStore.isTrustedStore(packageManager.getInstallerPackageName(applicationInfo.packageName))){
+ // App was installed by trusted app store
+ return true;
+ }
+ } catch (final NameNotFoundException e) {
+ e.printStackTrace();
+ return false;
+ }
+ return false;
+ }
+
+ /**
+ * This method will check to see if this app is a debug build. If it is, we will attempt to connect to any router service.
+ * If false, it will only connect to approved apps with router services.
+ * @return
+ */
+ public boolean inDebugMode(){
+ return (0 != (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE));
+ }
+
+
+ private boolean isTrustedPackage(String packageName, PackageManager pm){
+ if(packageName == null){
+ return false;
+ }
+
+ if(shouldOverridePackageName()){ //If we don't care about package names, just return true;
+ return true;
+ }
+
+ int version = -1;
+ try {version = pm.getPackageInfo(packageName,0).versionCode;} catch (NameNotFoundException e1) {e1.printStackTrace(); return false;}
+
+ JSONObject trustedApps = stringToJson(getTrustedList(context));
+ JSONArray versions;
+ JSONObject app = null;
+
+ try {
+ app = trustedApps.getJSONObject(packageName);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ return false;
+ }
+
+ if(app!=null){
+ //At this point, an app object was found in the JSON list that matches the package name
+ if(shouldOverrideVersionCheck()){ //If we don't care about versions, just return true
+ return true;
+ }
+ try { versions = app.getJSONArray(JSON_RESONSE_APP_VERSIONS_TAG); } catch (JSONException e) { e.printStackTrace();return false;}
+ return verifyVersion(version, versions);
+ }
+
+ return false;
+ }
+
+ protected boolean verifyVersion(int version, JSONArray versions){
+ if(version<0){
+ return false;
+ }
+ if(versions == null || versions.length()==0){
+ return true;
+ }
+ for(int i=0;i<versions.length();i++){
+ try {
+ if(version == versions.getInt(i)){
+ return false;
+ }
+ } catch (JSONException e) {
+ continue;
+ }
+ }//We didn't find our version in the black list.
+ return true;
+ }
+
+ /**
+ * Using the knowledge that all SDL enabled apps have an SDL Broadcast Receiver that has an intent filter that includes a specific
+ * intent.
+ * @return
+ */
+ private static List<SdlApp> findAllSdlApps(Context context){
+ List<SdlApp> apps = new ArrayList<SdlApp>();
+ PackageManager packageManager = context.getPackageManager();
+ Intent intent = new Intent();
+ intent.setAction("sdl.router.startservice");
+ List<ResolveInfo> infoList = packageManager.queryBroadcastReceivers(intent, 0);
+ if(infoList!=null){
+ Log.i(TAG, "Number of SDL apps: " + infoList.size());
+ String packageName;
+ for(ResolveInfo info : infoList){
+ Log.i(TAG, "SDL apps: " + info.activityInfo.packageName);
+ packageName = info.activityInfo.packageName;
+ try {
+ apps.add(new SdlApp(packageName,packageManager.getPackageInfo(packageName,0).versionCode));
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return apps;
+ }else{
+ Log.i(TAG, "No SDL apps, list was null");
+ return null;
+ }
+ }
+
+ /**
+ * Performs a look up against installed SDL apps that support the router service.
+ * When it receives a list back from the server it will store it for later use.
+ * @param context
+ */
+ public static boolean createTrustedListRequest(final Context context, boolean forceRefresh){
+ return createTrustedListRequest(context,forceRefresh,null);
+ }
+
+ protected static boolean createTrustedListRequest(final Context context, boolean forceRefresh,HttpRequestTask.HttpRequestTaskCallback cb ){
+ if(context == null){
+ return false;
+ }
+
+ if(!forceRefresh && (System.currentTimeMillis()-getTrustedAppListTimeStamp(context))<REFRESH_TRUSTED_APP_LIST_TIME){
+ Log.d(TAG, "Don't need to get new list");
+ //Our list should still be ok for now so we will skip the request
+ pendingListRefresh = false;
+ return false;
+ }
+
+ pendingListRefresh = true;
+ //Might want to store a flag letting this class know a request is currently pending
+ StringBuilder builder = new StringBuilder();
+ builder.append(REQUEST_PREFIX);
+
+ List<SdlApp> apps = findAllSdlApps(context);
+
+ JSONObject object = new JSONObject();
+ JSONArray array = new JSONArray();
+ JSONObject jsonApp;
+
+ for(SdlApp app: apps){ //Format all the apps into a JSON object and add it to the JSON array
+ try{
+ jsonApp = new JSONObject();
+ jsonApp.put(JSON_APP_PACKAGE_TAG, app.packageName);
+ jsonApp.put(JSON_APP_VERSION_TAG, app.versionCode);
+ array.put(jsonApp);
+ }catch(JSONException e){
+ e.printStackTrace();
+ continue;
+ }
+ }
+
+ try {object.put(JSON_PUT_ARRAY_TAG, array);} catch (JSONException e) {e.printStackTrace();}
+
+ Log.d(TAG, "Request of apps: " + object.toString());
+
+ if (cb == null) {
+ cb = new HttpRequestTaskCallback() {
+
+ @Override
+ public void httpCallComplete(String response) {
+ // Might want to check if this list is ok
+ Log.d(TAG, "APPS! " + response);
+ setTrustedList(context, response);
+ pendingListRefresh = false;
+ }
+
+ @Override
+ public void httpFailure(int statusCode) {
+ Log.e(TAG, "Error while requesting trusted app list: "
+ + statusCode);
+ pendingListRefresh = false;
+ }
+ };
+ }
+
+ new HttpRequestTask(cb).execute(REQUEST_PREFIX,HttpRequestTask.REQUEST_TYPE_POST,object.toString(),"application/json","application/json");
+
+ return true;
+ }
+
+ /**
+ * This method will determine if our supplied component name is really running.
+ * @param context
+ * @param service
+ * @return
+ */
+ protected boolean isServiceRunning(Context context, ComponentName service){
+ ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ for (RunningServiceInfo serviceInfo : manager.getRunningServices(Integer.MAX_VALUE)) {
+ if (serviceInfo.service.equals(service)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Parses a string into a JSON array
+ * @param json
+ * @return
+ */
+ protected JSONObject stringToJson(String json){
+ if(json==null){
+ return stringToJson(DEFAULT_APP_LIST);
+ }
+ try {
+ JSONObject object = new JSONObject(json);
+ JSONObject trustedApps = object.getJSONObject(JSON_RESPONSE_OBJECT_TAG);
+ return trustedApps;
+
+ } catch (JSONException e) {
+ e.printStackTrace();
+ if(!json.equalsIgnoreCase(DEFAULT_APP_LIST)){ //Since we were unable to parse, let's fall back to at least our last known good list. If this list is somehow messed up, just quit.
+ return stringToJson(DEFAULT_APP_LIST);
+ }else{
+ return null;
+ }
+ }
+ }
+
+ public static boolean invalidateList(Context context){
+ if(context == null){
+ return false;
+ }
+ SharedPreferences pref = context.getSharedPreferences(SDL, Context.MODE_PRIVATE);
+ // Write the new prefs
+ SharedPreferences.Editor prefAdd = pref.edit();
+ prefAdd.putLong(SDL_PACKAGE_LIST_TIMESTAMP, 0); //This will be the last time we updated
+ return prefAdd.commit();
+ }
+ /******************************************************************
+ *
+ * Saving the list for later!!!
+ *
+ ******************************************************************/
+
+ /**
+ * Saves the list of available applications into user's shared prefs.
+ * @param context The application's environment
+ * @param jsonString The JSON string to save.
+ */
+ protected static boolean setTrustedList(Context context, String jsonString){
+ if(jsonString!=null && context!=null){
+ SharedPreferences pref = context.getSharedPreferences(SDL, Context.MODE_PRIVATE);
+ // Write the new prefs
+ SharedPreferences.Editor prefAdd = pref.edit();
+ prefAdd.putString(SDL_PACKAGE_LIST, jsonString);
+ prefAdd.putLong(SDL_PACKAGE_LIST_TIMESTAMP, System.currentTimeMillis()); //This will be the last time we updated
+ return prefAdd.commit();
+ }
+ return false;
+ }
+
+ /**
+ * Retrieves the list of available applications from user's shared prefs.
+ * @param context The application's environment.
+ * @return The JSON string that was retrieved.
+ */
+ protected static String getTrustedList(Context context){
+ if(context!=null){
+ SharedPreferences pref = context.getSharedPreferences(SDL, Context.MODE_PRIVATE);
+ return pref.getString(SDL_PACKAGE_LIST, DEFAULT_APP_LIST);
+ }
+ return null;
+ }
+
+ /**
+ * Retrieves the time stamp from the user's shared prefs.
+ * @param context The application's environment.
+ * @return The time stamp that was retrieved.
+ */
+ protected static Long getTrustedAppListTimeStamp(Context context){
+ if(context!=null){
+ SharedPreferences pref = context.getSharedPreferences(SDL, Context.MODE_PRIVATE);
+ return pref.getLong(SDL_PACKAGE_LIST_TIMESTAMP, 0);
+ }
+ return -1L;
+ }
+
+
+
+ /**
+ * Class that holds all the info we want to send/receive from the validation server
+ */
+ public static class SdlApp{
+ String packageName;
+ int versionCode;
+
+ SdlApp(String packageName, int versionCode){
+ this.packageName = packageName;
+ this.versionCode = versionCode;
+ }
+ }
+
+ public static enum TrustedAppStore{
+ PLAY_STORE("com.android.vending"),
+ AMAZON("com.amazon.venezia"),
+ XIAOMI("com.xiaomi.market"),
+ SAMSUNG("com.sec.android.app.samsungapps"),
+ WANDOUJIA("com.wandoujia.phoenix2"),
+ BAIDU_APP_SEARCH("com.baidu.appsearch"),
+ HIAPK("com.hiapk.marketpho"),
+ ;
+
+ String packageString;
+ private TrustedAppStore(String packageString){
+ this.packageString = packageString;
+ }
+
+ /**
+ * Test if the supplied store package is one of the trusted app stores
+ * @param packageString
+ * @return
+ */
+ public static boolean isTrustedStore(String packageString){
+ if(packageString == null){
+ return false;
+ }
+ TrustedAppStore[] stores = TrustedAppStore.values();
+ for(int i =0; i<stores.length; i++){
+ if(packageString.equalsIgnoreCase(stores[i].packageString)){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ }
+
+
+
+}
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/SdlBroadcastReceiver.java b/sdl_android_lib/src/com/smartdevicelink/transport/SdlBroadcastReceiver.java
new file mode 100644
index 000000000..70589bc4a
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/SdlBroadcastReceiver.java
@@ -0,0 +1,272 @@
+package com.smartdevicelink.transport;
+
+import java.util.Locale;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningServiceInfo;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.util.Log;
+
+public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
+
+ private static final String TAG = "Sdl Broadcast Receiver";
+
+ private static final String BOOT_COMPLETE = "android.intent.action.BOOT_COMPLETED";
+ private static final String ACL_CONNECTED = "android.bluetooth.device.action.ACL_CONNECTED";
+ private static final String STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED" ;
+
+ protected static final String SDL_ROUTER_SERVICE_CLASS_NAME = "sdlrouterservice";
+
+ public static final String LOCAL_ROUTER_SERVICE_EXTRA = "router_service";
+ public static final String LOCAL_ROUTER_SERVICE_DID_START_OWN = "did_start";
+
+ public static final String TRANSPORT_GLOBAL_PREFS = "SdlTransportPrefs";
+ public static final String IS_TRANSPORT_CONNECTED = "isTransportConnected";
+
+ public static ComponentName runningBluetoothServicePackage = null;
+
+ @SuppressWarnings("rawtypes")
+ private static Class localRouterClass;
+
+ private static final Object QUEUED_SERVICE_LOCK = new Object();
+ private static ComponentName queuedService = null;
+
+ public int getRouterServiceVersion(){
+ return SdlRouterService.ROUTER_SERVICE_VERSION_NUMBER;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ //Log.i(TAG, "Sdl Receiver Activated");
+ String action = intent.getAction();
+
+ if(action.equalsIgnoreCase(Intent.ACTION_PACKAGE_ADDED)
+ || action.equalsIgnoreCase(Intent.ACTION_PACKAGE_REPLACED)){
+ //The package manager has sent out a new broadcast.
+ RouterServiceValidator.invalidateList(context);
+ return;
+ }
+
+ if(!(action.equalsIgnoreCase(BOOT_COMPLETE)
+ || action.equalsIgnoreCase(ACL_CONNECTED)
+ || action.equalsIgnoreCase(STATE_CHANGED)
+ || action.equalsIgnoreCase(TransportConstants.START_ROUTER_SERVICE_ACTION))){
+ //We don't want anything else here if the child class called super and has different intent filters
+ //Log.i(TAG, "Unwanted intent from child class");
+ return;
+ }
+
+ boolean didStart = false;
+ localRouterClass = defineLocalSdlRouterClass();
+
+ //This will only be true if we are being told to reopen our SDL service because SDL is enabled
+ if(action.equalsIgnoreCase(TransportConstants.START_ROUTER_SERVICE_ACTION)){
+ if(intent.hasExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_EXTRA)){
+ if(intent.getBooleanExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_EXTRA, false)){
+ String packageName = intent.getStringExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_APP_PACKAGE);
+ ComponentName componentName = intent.getParcelableExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_CMP_NAME);
+ if(componentName!=null){
+ Log.v(TAG, "SDL enabled by router service from " + packageName + " compnent package " + componentName.getPackageName() + " - " + componentName.getClassName());
+ RouterServiceValidator vlad = new RouterServiceValidator(context,componentName);
+ if(vlad.validate()){
+ Log.d(TAG, "Router service trusted!");
+ queuedService = componentName;
+ intent.setAction("com.sdl.noaction"); //Replace what's there so we do go into some unintended loop
+ onSdlEnabled(context, intent);
+ }else{
+ Log.e(TAG, "RouterService was not trusted. Ignoring intent from : "+ componentName.getClassName());
+ }
+
+ }
+
+
+ }else{
+ //This was previously not hooked up, so let's leave it commented out
+ //onSdlDisabled(context);
+ }
+ return;
+ }else if(intent.getBooleanExtra(TransportConstants.PING_ROUTER_SERVICE_EXTRA, false)){
+ //We were told to wake up our router services
+ Log.d(TAG, "Starting router service off ping");
+ boolean altServiceWake = intent.getBooleanExtra(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT, false);
+ didStart = wakeUpRouterService(context, false,altServiceWake );
+
+ }
+
+ }
+
+ if (intent.getAction().contains("android.bluetooth.adapter.action.STATE_CHANGED")){
+
+ int state = intent.getExtras().getInt("android.bluetooth.adapter.extra.STATE");
+ if (state == BluetoothAdapter.STATE_OFF ||
+ state == BluetoothAdapter.STATE_TURNING_OFF ){
+ //onProtocolDisabled(context);
+ //Let's let the service that is running manage what to do for this
+ //If we were to do it here, for every instance of this BR it would send
+ //an intent to stop service, where it's only one that is needed.
+ return;
+ }else if(state == BluetoothAdapter.STATE_TURNING_ON){
+ //We started bluetooth, we should check for a new valid router list
+ Log.d(TAG, "Attempting to get list of approved router services");
+ RouterServiceValidator.createTrustedListRequest(context,true);
+ }
+ }
+
+ if(localRouterClass!=null){ //If there is a supplied router service lets run some logic regarding starting one
+
+ if(!didStart){
+ Log.d(TAG, "Waking up router service");
+ didStart = wakeUpRouterService(context, true,false);
+ }
+
+ //So even though we started our own version, on some older phones we find that two services are started up so we want to make sure we send our version that we are working with
+ //We will send it an intent with the version number of the local instance and an intent to start this instance
+
+ Intent serviceIntent = new Intent(context, localRouterClass);
+ SdlRouterService.LocalRouterService self = SdlRouterService.getLocalRouterService(serviceIntent, serviceIntent.getComponent());
+ Intent restart = new Intent(SdlRouterService.REGISTER_NEWER_SERVER_INSTANCE_ACTION);
+ restart.putExtra(LOCAL_ROUTER_SERVICE_EXTRA, self);
+ restart.putExtra(LOCAL_ROUTER_SERVICE_DID_START_OWN, didStart);
+ context.sendBroadcast(restart);
+ }
+ }
+
+ private boolean wakeUpRouterService(Context context, boolean ping, boolean altTransportWake){
+ Log.d(TAG, "Waking up router service");
+ if(!isRouterServiceRunning(context, ping)){
+ //If there isn't a service running we should try to start one
+ Log.i(TAG, "Attempting to start an instance of the Router Service");
+ //The under class should have implemented this....
+
+ //So let's start up our service since no copy is running
+ Intent serviceIntent = new Intent(context, localRouterClass);
+ if(altTransportWake){
+ serviceIntent.setAction(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT);
+ }
+ context.startService(serviceIntent);
+ return true;
+ }else{
+ Log.i(TAG, "An instance of the Router Service is already running");
+ if(altTransportWake){
+ Intent serviceIntent = new Intent();
+ serviceIntent.setComponent(runningBluetoothServicePackage);
+ serviceIntent.setAction(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT);
+ context.startService(serviceIntent);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Determines if an instance of the Router Service is currently running on the device.
+ * @param context A context to access Android system services through.
+ * @param pingService Set this to true if you want to make sure the service is up and listening to bluetooth
+ * @return True if a SDL Router Service is currently running, false otherwise.
+ */
+ private static boolean isRouterServiceRunning(Context context, boolean pingService){
+ if(context == null){
+ Log.e(TAG, "Can't look for router service, context supplied was null");
+ return false;
+ }
+ Log.d(TAG, "Looking for Service: "+ SDL_ROUTER_SERVICE_CLASS_NAME);
+ ActivityManager manager = (ActivityManager) context.getSystemService("activity");
+ for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
+ //We will check to see if it contains this name, should be pretty specific
+ //Log.d(TAG, "Found Service: "+ service.service.getClassName());
+ if ((service.service.getClassName()).toLowerCase(Locale.US).contains(SDL_ROUTER_SERVICE_CLASS_NAME)) {
+ runningBluetoothServicePackage = service.service; //Store which instance is running
+ if(pingService){
+ Intent intent = new Intent();
+ intent.setClassName(service.service.getPackageName(), service.service.getClassName());
+ intent.putExtra(TransportConstants.PING_ROUTER_SERVICE_EXTRA, pingService);
+ context.startService(intent);
+ }
+ return true;
+ }
+ }
+
+ return false;
+
+ }
+
+ /**
+ * If a Router Service is running, this method determines if that service is connected to a device over some form of transport.
+ * @param context A context to access Android system services through.
+ * @return True if a transport connection is established, false otherwise.
+ */
+ public static boolean isTransportConnected(Context context){
+ Log.d(TAG, "Checking to see if router service is transport connected");
+ if(isRouterServiceRunning(context,false)){ //So there is a service up, let's see if it's connected
+ Context con;
+ try {
+ con = context.createPackageContext(runningBluetoothServicePackage.getPackageName(), 0);
+ if(con==null ){
+ Log.w(TAG, "Unable to check for service connection. Returning false. "+runningBluetoothServicePackage);
+ return false; // =( well that sucks.
+ }
+ SharedPreferences pref = con.getSharedPreferences(
+ con.getPackageName()+TRANSPORT_GLOBAL_PREFS , 4);
+ boolean connected = pref.getBoolean(IS_TRANSPORT_CONNECTED, false);
+ // Log.w(TAG, "Is Connected? Returning " + connected);
+ return connected;
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ return false;
+ }
+
+ }else{
+ Log.w(TAG, "Router service isn't running, returning false.");
+ if(BluetoothAdapter.getDefaultAdapter()!=null && BluetoothAdapter.getDefaultAdapter().isEnabled()){
+ Intent serviceIntent = new Intent();
+ serviceIntent.setAction(TransportConstants.START_ROUTER_SERVICE_ACTION);
+ serviceIntent.putExtra(TransportConstants.PING_ROUTER_SERVICE_EXTRA, true);
+ context.sendBroadcast(serviceIntent);
+ }
+ }
+
+ return false;
+ }
+
+
+ public static ComponentName consumeQueuedRouterService(){
+ synchronized(QUEUED_SERVICE_LOCK){
+ ComponentName retVal = queuedService;
+ queuedService = null;
+ return retVal;
+ }
+ }
+
+ /**
+ * We need to define this for local copy of the Sdl Bluetooth Service class.
+ * It will be the main point of connection for Sdl Connected apps
+ * @return Return the local copy of SdlRouterService.class
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("rawtypes")
+ public abstract Class defineLocalSdlRouterClass();
+
+
+
+ /**
+ *
+ * The developer will need to define exactly what should happen when Sdl is enabled.
+ * This method will only get called when a Sdl session is initiated.
+ * <p> The most useful code here would be to start the activity or service that handles most of the Livio
+ * Connect code.
+ * @param context this is the context that was passed to this receiver when it was called.
+ * @param intent this is the intent that alerted this broadcast. Make sure to pass all extra it came with to your service/activity
+ * {@inheritDoc}
+ */
+ public abstract void onSdlEnabled(Context context, Intent intent);
+
+ //public abstract void onSdlDisabled(Context context); //Removing for now until we're able to abstract from developer
+
+
+}
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/SdlPsm.java b/sdl_android_lib/src/com/smartdevicelink/transport/SdlPsm.java
new file mode 100644
index 000000000..b04b4453b
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/SdlPsm.java
@@ -0,0 +1,247 @@
+package com.smartdevicelink.transport;
+
+import com.smartdevicelink.protocol.SdlPacket;
+
+
+public class SdlPsm{
+ //private static final String TAG = "Sdl PSM";
+ //Each state represents the byte that should be incomming
+
+ public static final int START_STATE = 0x0;
+ public static final int SERVICE_TYPE_STATE = 0x02;
+ public static final int CONTROL_FRAME_INFO_STATE = 0x03;
+ public static final int SESSION_ID_STATE = 0x04;
+ public static final int DATA_SIZE_1_STATE = 0x05;
+ public static final int DATA_SIZE_2_STATE = 0x06;
+ public static final int DATA_SIZE_3_STATE = 0x07;
+ public static final int DATA_SIZE_4_STATE = 0x08;
+ public static final int MESSAGE_1_STATE = 0x09;
+ public static final int MESSAGE_2_STATE = 0x0A;
+ public static final int MESSAGE_3_STATE = 0x0B;
+ public static final int MESSAGE_4_STATE = 0x0C;
+ public static final int DATA_PUMP_STATE = 0x0D;
+ public static final int FINISHED_STATE = 0xFF;
+ public static final int ERROR_STATE = -1;
+
+
+ private static final byte FIRST_FRAME_DATA_SIZE = 0x08;
+
+ private static final int VERSION_MASK = 0xF0; //4 highest bits
+ private static final int COMPRESSION_MASK = 0x08; //4th lowest bit
+ private static final int FRAME_TYPE_MASK = 0x07; //3 lowest bits
+
+
+
+ int state ;
+
+ int version;
+ boolean compression;
+ int frameType;
+ int serviceType;
+ int controlFrameInfo;
+ int sessionId;
+ int dumpSize, dataLength;
+ int messageId = 0;
+
+ byte[] payload;
+
+ public SdlPsm(){
+ reset();
+ }
+
+ public boolean handleByte(byte data) {
+ //Log.trace(TAG, data + " = incomming");
+ state = transitionOnInput(data,state);
+
+ if(state==ERROR_STATE){
+ return false;
+ }
+ return true;
+ }
+
+ private int transitionOnInput(byte rawByte, int state){
+ switch(state){
+ case START_STATE:
+ version = (rawByte&(byte)VERSION_MASK)>>4;
+ //Log.trace(TAG, "Version: " + version);
+ if(version==0){ //It should never be 0
+ return ERROR_STATE;
+ }
+ compression = (1 == ((rawByte&(byte)COMPRESSION_MASK)>>3));
+
+
+ frameType = rawByte&(byte)FRAME_TYPE_MASK;
+ //Log.trace(TAG, rawByte + " = Frame Type: " + frameType);
+
+ if((version < 1 || version > 4) //These are known versions supported by this library.
+ && frameType!=SdlPacket.FRAME_TYPE_CONTROL){
+ return ERROR_STATE;
+ }
+
+ if(frameType<SdlPacket.FRAME_TYPE_CONTROL || frameType > SdlPacket.FRAME_TYPE_CONSECUTIVE){
+ return ERROR_STATE;
+ }
+
+ return SERVICE_TYPE_STATE;
+
+ case SERVICE_TYPE_STATE:
+ serviceType = (int)(rawByte&0xFF);
+ return CONTROL_FRAME_INFO_STATE;
+
+ case CONTROL_FRAME_INFO_STATE:
+ controlFrameInfo = (int)(rawByte&0xFF);
+ //Log.trace(TAG,"Frame Info: " + controlFrameInfo);
+ switch(frameType){
+ case SdlPacket.FRAME_TYPE_CONTROL:
+ /*if(frameInfo<FRAME_INFO_HEART_BEAT
+ || (frameInfo>FRAME_INFO_END_SERVICE_ACK
+ && (frameInfo!=FRAME_INFO_SERVICE_DATA_ACK || frameInfo!=FRAME_INFO_HEART_BEAT_ACK))){
+ return ERROR_STATE;
+ }*/ //Although some bits are reserved...whatever
+ break;
+ case SdlPacket.FRAME_TYPE_SINGLE: //Fall through since they are both the same
+ case SdlPacket.FRAME_TYPE_FIRST:
+ if(controlFrameInfo!=0x00){
+ return ERROR_STATE;
+ }
+ break;
+ case SdlPacket.FRAME_TYPE_CONSECUTIVE:
+ //It might be a good idea to check packet sequence numbers here
+ break;
+
+ default:
+ return ERROR_STATE;
+ }
+ return SESSION_ID_STATE;
+
+ case SESSION_ID_STATE:
+ sessionId = (int)(rawByte&0xFF);
+ return DATA_SIZE_1_STATE;
+
+ case DATA_SIZE_1_STATE:
+ //First data size byte
+ //Log.d(TAG, "Data byte 1: " + rawByte);
+ dataLength += ((int)(rawByte& 0xFF))<<24; //3 bytes x 8 bits
+ //Log.d(TAG, "Data Size 1 : " + dataLength);
+ return DATA_SIZE_2_STATE;
+
+ case DATA_SIZE_2_STATE:
+ //Log.d(TAG, "Data byte 2: " + rawByte);
+ dataLength += ((int)(rawByte& 0xFF))<<16; //2 bytes x 8 bits
+ //Log.d(TAG, "Data Size 2 : " + dataLength);
+ return DATA_SIZE_3_STATE;
+
+ case DATA_SIZE_3_STATE:
+ //Log.d(TAG, "Data byte 3: " + rawByte);
+ dataLength += ((int)(rawByte& 0xFF))<<8; //1 byte x 8 bits
+ //Log.d(TAG, "Data Size 3 : " + dataLength);
+ return DATA_SIZE_4_STATE;
+
+ case DATA_SIZE_4_STATE:
+ //Log.d(TAG, "Data byte 4: " + rawByte);
+ dataLength+=((int)rawByte) & 0xFF;
+ //Log.trace(TAG, "Data Size: " + dataLength);
+ //We should have data length now for the pump state
+ switch(frameType){ //If all is correct we should break out of this switch statement
+ case SdlPacket.FRAME_TYPE_SINGLE:
+ case SdlPacket.FRAME_TYPE_CONSECUTIVE:
+ break;
+ case SdlPacket.FRAME_TYPE_CONTROL:
+ //Ok, well here's some interesting bit of knowledge. Because the start session request is from the phone with no knowledge of version it sends out
+ //a v1 packet. THEREFORE there is no message id field. **** Now you know and knowing is half the battle ****
+ if(version==1 && controlFrameInfo == SdlPacket.FRAME_INFO_START_SERVICE){
+ if(dataLength==0){
+ return FINISHED_STATE; //We are done if we don't have any payload
+ }
+ payload = new byte[dataLength];
+ dumpSize = dataLength;
+ return DATA_PUMP_STATE;
+ }
+ break;
+
+ case SdlPacket.FRAME_TYPE_FIRST:
+ if(dataLength==FIRST_FRAME_DATA_SIZE){
+ break;
+ }
+ default:
+ return ERROR_STATE;
+ }
+ if(version==1){ //Version 1 packets will not have message id's
+ if(dataLength == 0){
+ return FINISHED_STATE; //We are done if we don't have any payload
+ }
+ payload = new byte[dataLength];
+ dumpSize = dataLength;
+ return DATA_PUMP_STATE;
+ }else{
+ return MESSAGE_1_STATE;
+ }
+
+ case MESSAGE_1_STATE:
+ messageId += ((int)(rawByte& 0xFF))<<24; //3 bytes x 8 bits
+ return MESSAGE_2_STATE;
+
+ case MESSAGE_2_STATE:
+ messageId += ((int)(rawByte& 0xFF))<<16; //2 bytes x 8 bits
+ return MESSAGE_3_STATE;
+
+ case MESSAGE_3_STATE:
+ messageId += ((int)(rawByte& 0xFF))<<8; //1 byte x 8 bits
+ return MESSAGE_4_STATE;
+
+ case MESSAGE_4_STATE:
+ messageId+=((int)rawByte) & 0xFF;
+
+ if(dataLength==0){
+ return FINISHED_STATE; //We are done if we don't have any payload
+ }
+ payload = new byte[dataLength];
+ dumpSize = dataLength;
+ return DATA_PUMP_STATE;
+
+ case DATA_PUMP_STATE:
+ payload[dataLength-dumpSize] = rawByte;
+ dumpSize--;
+ //Log.trace(TAG,rawByte + " read. Data Length remaining: " + dumpSize);
+ //Do we have any more bytes to read in?
+ if(dumpSize>0){
+ return DATA_PUMP_STATE;
+ }
+ else if(dumpSize==0){
+ return FINISHED_STATE;
+ }else{
+ return ERROR_STATE;
+ }
+ case FINISHED_STATE: //We shouldn't be here...Should have been reset
+ default:
+ return ERROR_STATE;
+
+ }
+
+ }
+
+ public SdlPacket getFormedPacket(){
+ if(state==FINISHED_STATE){
+ //Log.trace(TAG, "Finished packet.");
+ return new SdlPacket(version, compression, frameType,
+ serviceType, controlFrameInfo, sessionId,
+ dataLength, messageId, payload);
+ }else{
+ return null;
+ }
+ }
+
+ public int getState() {
+ return state;
+ }
+
+ public void reset() {
+ version = 0;
+ state = START_STATE;
+ messageId = 0;
+ dataLength = 0;
+ frameType = 0x00; //Set it to null
+ payload = null;
+ }
+
+}
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/SdlRouterService.java b/sdl_android_lib/src/com/smartdevicelink/transport/SdlRouterService.java
new file mode 100644
index 000000000..1bb7f3d72
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/SdlRouterService.java
@@ -0,0 +1,2448 @@
+package com.smartdevicelink.transport;
+
+import static com.smartdevicelink.transport.TransportConstants.CONNECTED_DEVICE_STRING_EXTRA_NAME;
+import static com.smartdevicelink.transport.TransportConstants.FORMED_PACKET_EXTRA_NAME;
+import static com.smartdevicelink.transport.TransportConstants.HARDWARE_DISCONNECTED;
+import static com.smartdevicelink.transport.TransportConstants.SEND_PACKET_TO_APP_LOCATION_EXTRA_NAME;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Set;
+import java.util.Vector;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.Notification;
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.smartdevicelink.R;
+import com.smartdevicelink.marshal.JsonRPCMarshaller;
+import com.smartdevicelink.protocol.BinaryFrameHeader;
+import com.smartdevicelink.protocol.ProtocolMessage;
+import com.smartdevicelink.protocol.SdlPacket;
+import com.smartdevicelink.protocol.SdlPacketFactory;
+import com.smartdevicelink.protocol.enums.FrameType;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.protocol.enums.MessageType;
+import com.smartdevicelink.protocol.enums.SessionType;
+import com.smartdevicelink.proxy.rpc.UnregisterAppInterface;
+import com.smartdevicelink.transport.enums.TransportType;
+import com.smartdevicelink.transport.utl.ByteAraryMessageAssembler;
+import com.smartdevicelink.transport.utl.ByteArrayMessageSpliter;
+import com.smartdevicelink.util.BitConverter;
+
+/**
+ * <b>This class should not be modified by anyone outside of the approved contributors of the SmartDeviceLink project.</b>
+ * This service is a central point of communication between hardware and the registered clients. It will multiplex a single transport
+ * to provide a connection for a theoretical infinite amount of SDL sessions.
+ * @author Joey Grover
+ *
+ */
+public class SdlRouterService extends Service{
+
+ private static final String TAG = "Sdl Router Service";
+ /**
+ * <b> NOTE: DO NOT MODIFY THIS UNLESS YOU KNOW WHAT YOU'RE DOING.</b>
+ */
+ protected static final int ROUTER_SERVICE_VERSION_NUMBER = 1;
+
+ private static final String ROUTER_SERVICE_PROCESS = "com.smartdevicelink.router";
+
+ private static final int FOREGROUND_SERVICE_ID = 849;
+
+ private static final long CLIENT_PING_DELAY = 1000;
+
+ public static final String REGISTER_NEWER_SERVER_INSTANCE_ACTION = "com.sdl.android.newservice";
+ public static final String START_SERVICE_ACTION = "sdl.router.startservice";
+ public static final String REGISTER_WITH_ROUTER_ACTION = "com.sdl.android.register";
+
+ /** Message types sent from the BluetoothReadService Handler */
+ public static final int MESSAGE_STATE_CHANGE = 1;
+ public static final int MESSAGE_READ = 2;
+ public static final int MESSAGE_WRITE = 3;
+ public static final int MESSAGE_DEVICE_NAME = 4;
+ public static final int MESSAGE_TOAST = 5;
+
+ private final int UNREGISTER_APP_INTERFACE_CORRELATION_ID = 65530;
+
+ private static MultiplexBluetoothTransport mSerialService = null;
+
+ private static boolean connectAsClient = false;
+ private static boolean closing = false;
+ private boolean isTarnsportConnected = false;
+ private static Context currentContext = null;
+
+ private Handler versionCheckTimeOutHandler, altTransportTimerHandler;
+ private Runnable versionCheckRunable, altTransportTimerRunnable;
+ private LocalRouterService localCompareTo = null;
+ private final static int VERSION_TIMEOUT_RUNNABLE = 750;
+ private final static int ALT_TRANSPORT_TIMEOUT_RUNNABLE = 30000;
+
+ private boolean wrongProcess = false;
+
+ private Intent lastReceivedStartIntent = null;
+ public static HashMap<Long,RegisteredApp> registeredApps;
+ private SparseArray<Long> sessionMap;
+ private SparseArray<Integer> sessionHashIdMap;
+ private final Object SESSION_LOCK = new Object(), REGISTERED_APPS_LOCK = new Object(), PING_COUNT_LOCK = new Object();
+
+ private static Messenger altTransportService = null;
+
+ private String connectedDeviceName = ""; //The name of the connected Device
+ private boolean startSequenceComplete = false;
+
+ private ExecutorService packetExecuter = null;
+ PacketWriteTaskMaster packetWriteTaskMaster = null;
+
+ private static LocalRouterService selfRouterService;
+
+ /**
+ * This flag is to keep track of if we are currently acting as a foreground service
+ */
+ private boolean isForeground = false;
+
+ private int cachedModuleVersion = -1;
+
+ /**
+ * Executor for making sure clients are still running during trying times
+ */
+ private ScheduledExecutorService clientPingExecutor = null;
+ Intent pingIntent = null;
+ private boolean isPingingClients = false;
+ int pingCount = 0;
+
+
+ /* **************************************************************************************************************************************
+ ****************************************************************************************************************************************
+ *********************************************** Broadcast Receivers START **************************************************************
+ ****************************************************************************************************************************************
+ ****************************************************************************************************************************************/
+
+ /** create our receiver from the router service */
+ BroadcastReceiver mainServiceReceiver = new BroadcastReceiver()
+ {
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ //Let's grab where to reply to this intent at. We will keep it temp right now because we may have to deny registration
+ String action =intent.getStringExtra(SEND_PACKET_TO_APP_LOCATION_EXTRA_NAME);
+ sendBroadcast(prepareRegistrationIntent(action));
+ }
+ };
+
+ private Intent prepareRegistrationIntent(String action){
+ Intent registrationIntent = new Intent();
+ registrationIntent.setAction(action);
+ registrationIntent.putExtra(TransportConstants.BIND_LOCATION_PACKAGE_NAME_EXTRA, this.getPackageName());
+ registrationIntent.putExtra(TransportConstants.BIND_LOCATION_CLASS_NAME_EXTRA, this.getClass().getName());
+ return registrationIntent;
+ }
+
+ private void onAppRegistered(RegisteredApp app){
+ //Log.enableDebug(receivedIntent.getBooleanExtra(LOG_BASIC_DEBUG_BOOLEAN_EXTRA, false));
+ //Log.enableBluetoothTraceLogging(receivedIntent.getBooleanExtra(LOG_TRACE_BT_DEBUG_BOOLEAN_EXTRA, false));
+ //Ok this is where we should do some authenticating...maybe.
+ //Should we ask for all relevant data in this packet?
+ if(BluetoothAdapter.getDefaultAdapter()!=null && BluetoothAdapter.getDefaultAdapter().isEnabled()){
+
+ if(startSequenceComplete &&
+ !connectAsClient && (mSerialService ==null
+ || mSerialService.getState() == MultiplexBluetoothTransport.STATE_NONE)){
+ Log.e(TAG, "Serial service not initliazed while registering app");
+ //Maybe we should try to do a connect here instead
+ Log.d(TAG, "Serial service being restarted");
+ if(mSerialService ==null){
+ Log.e(TAG, "Local copy of BT Server is null");
+ mSerialService = MultiplexBluetoothTransport.getBluetoothSerialServerInstance();
+ if(mSerialService==null){
+ Log.e(TAG, "Local copy of BT Server is still null and so is global");
+ mSerialService = MultiplexBluetoothTransport.getBluetoothSerialServerInstance(mHandlerBT);
+
+ }
+ }
+ mSerialService.start();
+
+ }
+ }
+
+ Log.i(TAG, app.appId + " has just been registered with SDL Router Service");
+ }
+
+
+ /**
+ * this is to make sure the AceeptThread is still running
+ */
+ BroadcastReceiver registerAnInstanceOfSerialServer = new BroadcastReceiver() {
+ final Object COMPARE_LOCK = new Object();
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ LocalRouterService tempService = intent.getParcelableExtra(SdlBroadcastReceiver.LOCAL_ROUTER_SERVICE_EXTRA);
+ synchronized(COMPARE_LOCK){
+ //Let's make sure we are on the same version.
+ if(tempService != null && (localCompareTo == null || localCompareTo.isNewer(tempService))){
+ LocalRouterService self = getLocalRouterService();
+ if(!self.isEqual(tempService)){ //We want to ignore self
+ Log.i(TAG, "Newer service received than previously stored service - " + tempService.launchIntent.getAction());
+ localCompareTo = tempService;
+ }else{
+ Log.i(TAG, "Ignoring self local router service");
+ }
+ }
+ }
+ if(intent!=null && intent.getBooleanExtra(SdlBroadcastReceiver.LOCAL_ROUTER_SERVICE_DID_START_OWN, false)){
+ Log.w(TAG, "Another serivce has been started, let's resend our version info to make sure they know about us too");
+ //notifyStartedService(context);
+ }
+
+ }
+ @SuppressWarnings("unused")
+ private void notifyStartedService(Context context){
+ Intent restart = new Intent(SdlRouterService.REGISTER_NEWER_SERVER_INSTANCE_ACTION);
+ restart.putExtra(SdlBroadcastReceiver.LOCAL_ROUTER_SERVICE_EXTRA, getLocalRouterService());
+ context.sendBroadcast(restart);
+ }
+ };
+
+
+
+ /**
+ * If the user disconnects the bluetooth device we will want to stop SDL and our current
+ * connection through RFCOMM
+ */
+ BroadcastReceiver mListenForDisconnect = new BroadcastReceiver()
+ {
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ String action = intent.getAction();
+ if(action!=null){Log.d(TAG, "Disconnect received. Action: " + intent.getAction());}
+ else{Log.d(TAG, "Disconnect received.");}
+ if(intent.getAction()!=null && intent.getAction().equalsIgnoreCase("android.bluetooth.adapter.action.STATE_CHANGED")
+ &&( (BluetoothAdapter.getDefaultAdapter().getState() == BluetoothAdapter.STATE_TURNING_ON)
+ || (BluetoothAdapter.getDefaultAdapter().getState() == BluetoothAdapter.STATE_ON))){
+ return;
+ }
+
+ connectAsClient=false;
+
+ if(action!=null && intent.getAction().equalsIgnoreCase("android.bluetooth.adapter.action.STATE_CHANGED")
+ &&( (BluetoothAdapter.getDefaultAdapter().getState() == BluetoothAdapter.STATE_TURNING_OFF)
+ || (BluetoothAdapter.getDefaultAdapter().getState() == BluetoothAdapter.STATE_OFF))){
+ Log.d(TAG, "Bluetooth is shutting off, SDL Router Service is closing.");
+ //Since BT is shutting off...there's no reason for us to be on now.
+ //Let's take a break...I'm sleepy
+ shouldServiceRemainOpen(intent);
+ }
+ else{//So we just got d/c'ed from the bluetooth...alright...Let the client know
+ if(legacyModeEnabled){
+ Log.d(TAG, "Legacy mode enabled and bluetooth d/c'ed, restarting router service bluetooth.");
+ enableLegacyMode(false);
+ onTransportDisconnected(TransportType.BLUETOOTH);
+ initBluetoothSerialService();
+ }
+ }
+ }
+ };
+
+/* **************************************************************************************************************************************
+*********************************************** Broadcast Receivers End **************************************************************
+****************************************************************************************************************************************/
+
+ /* **************************************************************************************************************************************
+ *********************************************** Handlers for bound clients **************************************************************
+ ****************************************************************************************************************************************/
+
+
+ /**
+ * Target we publish for clients to send messages to RouterHandler.
+ */
+ final Messenger routerMessenger = new Messenger(new RouterHandler());
+
+ /**
+ * Handler of incoming messages from clients.
+ */
+ class RouterHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ final Bundle receivedBundle = msg.getData();
+ Bundle returnBundle;
+
+ switch (msg.what) {
+ case TransportConstants.ROUTER_REQUEST_BT_CLIENT_CONNECT:
+ if(receivedBundle.getBoolean(TransportConstants.CONNECT_AS_CLIENT_BOOLEAN_EXTRA, false)
+ && !connectAsClient){ //We check this flag to make sure we don't try to connect over and over again. On D/C we should set to false
+ //Log.d(TAG,"Attempting to connect as bt client");
+ BluetoothDevice device = receivedBundle.getParcelable(BluetoothDevice.EXTRA_DEVICE);
+ connectAsClient = true;
+ if(device==null || !bluetoothConnect(device)){
+ Log.e(TAG, "Unable to connect to bluetooth device");
+ connectAsClient = false;
+ }
+ }
+ //**************** We don't break here so we can let the app register as well
+ case TransportConstants.ROUTER_REGISTER_CLIENT: //msg.arg1 is appId
+ pingClients();
+ Message message = Message.obtain();
+ message.what = TransportConstants.ROUTER_REGISTER_CLIENT_RESPONSE;
+ long appId = receivedBundle.getLong(TransportConstants.APP_ID_EXTRA, -1);
+ if(appId<0 || msg.replyTo == null){
+ Log.w(TAG, "Unable to requster app as no id or messenger was included");
+ if(msg.replyTo!=null){
+ message.arg1 = TransportConstants.REGISTRATION_RESPONSE_DENIED_APP_ID_NOT_INCLUDED;
+ try {
+ msg.replyTo.send(message);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ break;
+ }
+ if(SdlRouterService.this.legacyModeEnabled){
+ Log.w(TAG, "Unable to register app as legacy mode is enabled");
+ if(msg.replyTo!=null){
+ message.arg1 = TransportConstants.REGISTRATION_RESPONSE_DENIED_LEGACY_MODE_ENABLED;
+ try {
+ msg.replyTo.send(message);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ break;
+ }
+
+ RegisteredApp app = new RegisteredApp(appId,msg.replyTo);
+ synchronized(REGISTERED_APPS_LOCK){
+ RegisteredApp old = registeredApps.put(app.getAppId(), app);
+ if(old!=null){
+ Log.w(TAG, "Replacing already existing app with this app id");
+ removeAllSessionsForApp(old, true);
+ old.close();
+ }
+ }
+ onAppRegistered(app);
+
+ returnBundle = new Bundle();
+
+ if(MultiplexBluetoothTransport.currentlyConnectedDevice!=null){
+ returnBundle.putString(CONNECTED_DEVICE_STRING_EXTRA_NAME, MultiplexBluetoothTransport.currentlyConnectedDevice);
+ }
+ if(!returnBundle.isEmpty()){
+ message.setData(returnBundle);
+ }
+ int result = app.sendMessage(message);
+ if(result == RegisteredApp.SEND_MESSAGE_ERROR_MESSENGER_DEAD_OBJECT){
+ synchronized(REGISTERED_APPS_LOCK){
+ registeredApps.remove(appId);
+ }
+ }
+
+ break;
+ case TransportConstants.ROUTER_UNREGISTER_CLIENT:
+ long appIdToUnregister = receivedBundle.getLong(TransportConstants.APP_ID_EXTRA, -1);
+ Log.i(TAG, "Unregistering client: " + appIdToUnregister);
+ RegisteredApp unregisteredApp = null;
+ synchronized(REGISTERED_APPS_LOCK){
+ unregisteredApp = registeredApps.remove(appIdToUnregister);
+ }
+ Message response = Message.obtain();
+ response.what = TransportConstants.ROUTER_UNREGISTER_CLIENT_RESPONSE;
+ if(unregisteredApp == null){
+ response.arg1 = TransportConstants.UNREGISTRATION_RESPONSE_FAILED_APP_ID_NOT_FOUND;
+ removeAllSessionsWithAppId(appIdToUnregister);
+ }else{
+ response.arg1 = TransportConstants.UNREGISTRATION_RESPONSE_SUCESS;
+ removeAllSessionsForApp(unregisteredApp,false);
+ }
+ Log.i(TAG, "Unregistering client response: " + response.arg1 );
+ try {
+ msg.replyTo.send(response); //We do this because we aren't guaranteed to find the correct registeredApp to send the message through
+ } catch (RemoteException e) {
+ e.printStackTrace();
+
+ }catch(NullPointerException e2){
+ Log.e(TAG, "No reply address included, can't send a reply");
+ }
+
+ break;
+ case TransportConstants.ROUTER_SEND_PACKET:
+ Log.d(TAG, "Received packet to send");
+ if(receivedBundle!=null){
+ Runnable packetRun = new Runnable(){
+ @Override
+ public void run() {
+ if(receivedBundle!=null){
+
+ Long buffAppId = receivedBundle.getLong(TransportConstants.APP_ID_EXTRA);
+ RegisteredApp buffApp = null;
+ if(buffAppId!=null){
+ synchronized(REGISTERED_APPS_LOCK){
+ buffApp = registeredApps.get(buffAppId);
+ }
+ }
+
+ if(buffApp !=null){
+ buffApp.handleIncommingClientMessage(receivedBundle);
+ }else{
+ writeBytesToTransport(receivedBundle);
+ }
+ }
+ }
+ };
+ if(packetExecuter!=null){
+ packetExecuter.execute(packetRun);
+ }
+ }
+ break;
+ case TransportConstants.ROUTER_REQUEST_NEW_SESSION:
+ long appIdRequesting = receivedBundle.getLong(TransportConstants.APP_ID_EXTRA, -1); Log.i(TAG, "App requesting new session: " + appIdRequesting);
+ Message extraSessionResponse = Message.obtain();
+ extraSessionResponse.what = TransportConstants.ROUTER_REQUEST_NEW_SESSION_RESPONSE;
+ if(appIdRequesting>0){
+ synchronized(REGISTERED_APPS_LOCK){
+ if(registeredApps!=null){
+ RegisteredApp appRequesting = registeredApps.get(appIdRequesting);
+ if(appRequesting!=null){
+ appRequesting.getSessionIds().add((long)-1); //Adding an extra session
+ extraSessionResponse.arg1 = TransportConstants.ROUTER_REQUEST_NEW_SESSION_RESPONSE_SUCESS;
+ }else{
+ extraSessionResponse.arg1 = TransportConstants.ROUTER_REQUEST_NEW_SESSION_RESPONSE_FAILED_APP_NOT_FOUND;
+ }
+ }
+ }
+ }else{
+ extraSessionResponse.arg1 = TransportConstants.ROUTER_REQUEST_NEW_SESSION_RESPONSE_FAILED_APP_ID_NOT_INCL;
+ }
+ try {
+ msg.replyTo.send(extraSessionResponse); //We do this because we aren't guaranteed to find the correct registeredApp to send the message through
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }catch(NullPointerException e2){
+ Log.e(TAG, "No reply address included, can't send a reply");
+ }
+ break;
+ case TransportConstants.ROUTER_REMOVE_SESSION:
+ long appIdWithSession = receivedBundle.getLong(TransportConstants.APP_ID_EXTRA, -1);
+ long sessionId = receivedBundle.getLong(TransportConstants.SESSION_ID_EXTRA, -1);
+ removeSessionFromMap((int)sessionId);
+ Message removeSessionResponse = Message.obtain();
+ removeSessionResponse.what = TransportConstants.ROUTER_REMOVE_SESSION_RESPONSE;
+ if(appIdWithSession>0){
+ if(sessionId>=0){
+ synchronized(REGISTERED_APPS_LOCK){
+ if(registeredApps!=null){
+ RegisteredApp appRequesting = registeredApps.get(appIdWithSession);
+ if(appRequesting!=null){
+ if(appRequesting.removeSession(sessionId)){
+ removeSessionResponse.arg1 = TransportConstants.ROUTER_REMOVE_SESSION_RESPONSE_SUCESS;
+ }else{
+ removeSessionResponse.arg1 = TransportConstants.ROUTER_REMOVE_SESSION_RESPONSE_FAILED_SESSION_NOT_FOUND;
+ }
+ }else{
+ removeSessionResponse.arg1 = TransportConstants.ROUTER_REMOVE_SESSION_RESPONSE_FAILED_APP_NOT_FOUND;
+ }
+ }
+ }
+ }else{
+ removeSessionResponse.arg1 = TransportConstants.ROUTER_REMOVE_SESSION_RESPONSE_FAILED_SESSION_ID_NOT_INCL;
+ }
+ }else{
+ removeSessionResponse.arg1 = TransportConstants.ROUTER_REMOVE_SESSION_RESPONSE_FAILED_APP_ID_NOT_INCL;
+ }
+ try {
+ msg.replyTo.send(removeSessionResponse); //We do this because we aren't guaranteed to find the correct registeredApp to send the message through
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }catch(NullPointerException e2){
+ Log.e(TAG, "No reply address included, can't send a reply");
+ }
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+ }
+ }
+
+
+ /**
+ * Target we publish for alternative transport (USB) clients to send messages to RouterHandler.
+ */
+ final Messenger altTransportMessenger = new Messenger(new AltTransportHandler());
+
+ /**
+ * Handler of incoming messages from an alternative transport (USB).
+ */
+ class AltTransportHandler extends Handler {
+ ClassLoader loader = getClass().getClassLoader();
+ @Override
+ public void handleMessage(Message msg) {
+ Bundle receivedBundle = msg.getData();
+ switch(msg.what){
+ case TransportConstants.HARDWARE_CONNECTION_EVENT:
+ if(receivedBundle.containsKey(TransportConstants.HARDWARE_DISCONNECTED)){
+ //We should shut down, so call
+ if(altTransportService != null
+ && altTransportService.equals(msg.replyTo)){
+ //The same transport that was connected to the router service is now telling us it's disconnected. Let's inform clients and clear our saved messenger
+ altTransportService = null;
+ storeConnectedStatus(false);
+ onTransportDisconnected(TransportType.valueOf(receivedBundle.getString(TransportConstants.HARDWARE_DISCONNECTED)));
+ shouldServiceRemainOpen(null); //this will close the service if bluetooth is not available
+ }
+ }else if(receivedBundle.containsKey(TransportConstants.HARDWARE_CONNECTED)){
+ Message retMsg = Message.obtain();
+ retMsg.what = TransportConstants.ROUTER_REGISTER_ALT_TRANSPORT_RESPONSE;
+ if(altTransportService == null){ //Ok no other transport is connected, this is good
+ Log.d(TAG, "Alt transport connected.");
+ if(msg.replyTo == null){
+ break;
+ }
+ altTransportService = msg.replyTo;
+ //Clear out the timer to make sure the service knows we're good to go
+ if(altTransportTimerHandler!=null && altTransportTimerRunnable!=null){
+ altTransportTimerHandler.removeCallbacks(altTransportTimerRunnable);
+ }
+ altTransportTimerHandler = null;
+ altTransportTimerRunnable = null;
+
+ storeConnectedStatus(true);
+ //Let the alt transport know they are good to go
+ retMsg.arg1 = TransportConstants.ROUTER_REGISTER_ALT_TRANSPORT_RESPONSE_SUCESS;
+ onTransportConnected(TransportType.valueOf(receivedBundle.getString(TransportConstants.HARDWARE_CONNECTED)));
+ }else{ //There seems to be some other transport connected
+ //Error
+ retMsg.arg1 = TransportConstants.ROUTER_REGISTER_ALT_TRANSPORT_ALREADY_CONNECTED;
+ }
+ if(msg.replyTo!=null){
+ try {msg.replyTo.send(retMsg);} catch (RemoteException e) {e.printStackTrace();}
+ }
+ }
+ break;
+ case TransportConstants.ROUTER_RECEIVED_PACKET:
+ if(receivedBundle!=null){
+ receivedBundle.setClassLoader(loader);//We do this because loading a custom parceable object isn't possible without it
+ }else{
+ Log.e(TAG, "Bundle was null while sending packet to router service from alt transport");
+ }
+ if(receivedBundle.containsKey(TransportConstants.FORMED_PACKET_EXTRA_NAME)){
+ SdlPacket packet = receivedBundle.getParcelable(TransportConstants.FORMED_PACKET_EXTRA_NAME);
+ if(packet!=null){
+ onPacketRead(packet);
+ }else{
+ Log.w(TAG, "Received null packet from alt transport service");
+ }
+ }else{
+ Log.w(TAG, "Flase positive packet reception");
+ }
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+
+ }
+ };
+
+/* **************************************************************************************************************************************
+*********************************************** Life Cycle **************************************************************
+****************************************************************************************************************************************/
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ //Check intent to send back the correct binder (client binding vs alt transport)
+ if(intent!=null){
+ if(closing){
+ Log.w(TAG, "Denying bind request due to service shutting down.");
+ return null;
+ }
+ String requestType = intent.getAction();//intent.getIntExtra(TransportConstants.ROUTER_BIND_REQUEST_TYPE_EXTRA, TransportConstants.BIND_REQUEST_TYPE_CLIENT);
+ if(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT.equals(requestType)){
+ return this.altTransportMessenger.getBinder();
+ }else if(TransportConstants.BIND_REQUEST_TYPE_CLIENT.equals(requestType)){
+ return this.routerMessenger.getBinder();
+ }else{
+ Log.w(TAG, "Uknown bind request type");
+ }
+
+ }
+ return null;
+ }
+
+
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ Log.d(TAG, "Unbind being called.");
+ return super.onUnbind(intent);
+ }
+
+
+ private void notifyClients(Message message){
+ if(message==null){
+ Log.w(TAG, "Can't notify clients, message was null");
+ return;
+ }
+ Log.d(TAG, "Notifying "+ registeredApps.size()+ " clients");
+ int result;
+ synchronized(REGISTERED_APPS_LOCK){
+ Collection<RegisteredApp> apps = registeredApps.values();
+ Iterator<RegisteredApp> it = apps.iterator();
+ while(it.hasNext()){
+ RegisteredApp app = it.next();
+ result = app.sendMessage(message);
+ if(result == RegisteredApp.SEND_MESSAGE_ERROR_MESSENGER_DEAD_OBJECT){
+ app.close();
+ it.remove();
+ }
+ }
+ }
+ }
+
+ private void pingClients(){
+ Message message = Message.obtain();
+ Log.d(TAG, "Pinging "+ registeredApps.size()+ " clients");
+ int result;
+ synchronized(REGISTERED_APPS_LOCK){
+ Collection<RegisteredApp> apps = registeredApps.values();
+ Iterator<RegisteredApp> it = apps.iterator();
+ while(it.hasNext()){
+ RegisteredApp app = it.next();
+ result = app.sendMessage(message);
+ if(result == RegisteredApp.SEND_MESSAGE_ERROR_MESSENGER_DEAD_OBJECT){
+ app.close();
+ Vector<Long> sessions = app.getSessionIds();
+ for(Long session:sessions){
+ if(session !=null && session != -1){
+ attemptToCleanUpModule(session.intValue(), cachedModuleVersion);
+ }
+ }
+ it.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * We want to make sure we are in the right process here. If there is somesort of developer error
+ * we want to just close out right away.
+ * @return
+ */
+ private boolean processCheck(){
+ int myPid = android.os.Process.myPid();
+ ActivityManager am = (ActivityManager)this.getSystemService(ACTIVITY_SERVICE);
+ for (RunningAppProcessInfo processInfo : am.getRunningAppProcesses())
+ {
+ if (processInfo.pid == myPid)
+ {
+ return ROUTER_SERVICE_PROCESS.equals(processInfo.processName);
+ }
+ }
+ return false;
+
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ if(!processCheck()){
+ Log.e(TAG, "Not using correct process. Shutting down");
+ wrongProcess = true;
+ stopSelf();
+ return;
+ }
+ else{Log.d(TAG, "We are in the correct process");}
+ synchronized(REGISTERED_APPS_LOCK){
+ registeredApps = new HashMap<Long,RegisteredApp>();
+ }
+ closing = false;
+ currentContext = getBaseContext();
+ storeConnectedStatus(false);
+
+ startVersionCheck();
+ Log.i(TAG, "SDL Router Service has been created");
+
+
+ synchronized(SESSION_LOCK){
+ this.sessionMap = new SparseArray<Long>();
+ this.sessionHashIdMap = new SparseArray<Integer>();
+ }
+ packetExecuter = Executors.newSingleThreadExecutor();
+ }
+
+ public void startVersionCheck(){
+ registerReceiver(registerAnInstanceOfSerialServer, new IntentFilter(REGISTER_NEWER_SERVER_INSTANCE_ACTION));
+ newestServiceCheck(currentContext);
+ }
+
+ public void startUpSequence(){
+ IntentFilter stateChangeFilter = new IntentFilter("android.bluetooth.adapter.action.STATE_CHANGED");
+ stateChangeFilter.addAction("android.bluetooth.device.action.CLASS_CHANGED");
+ IntentFilter disconnectFilter1 = new IntentFilter("android.bluetooth.device.action.ACL_DISCONNECTED");
+ IntentFilter disconnectFilter2 = new IntentFilter("android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED");
+
+ registerReceiver(mListenForDisconnect,stateChangeFilter );
+ registerReceiver(mListenForDisconnect,disconnectFilter1 );
+ registerReceiver(mListenForDisconnect,disconnectFilter2 );
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(REGISTER_WITH_ROUTER_ACTION);
+ registerReceiver(mainServiceReceiver,filter);
+
+ if(!connectAsClient){
+ if(bluetoothAvailable()){
+ initBluetoothSerialService();
+ }
+ }
+
+ if(altTransportTimerHandler!=null){
+ //There's an alt transport waiting for this service to be started
+ Intent intent = new Intent(TransportConstants.ALT_TRANSPORT_RECEIVER);
+ sendBroadcast(intent);
+ }
+
+ startSequenceComplete= true;
+ }
+
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if(registeredApps == null){
+ synchronized(REGISTERED_APPS_LOCK){
+ registeredApps = new HashMap<Long,RegisteredApp>();
+ }
+ }
+ if(intent != null ){
+ if(intent.hasExtra(TransportConstants.PING_ROUTER_SERVICE_EXTRA)){
+ //Make sure we are listening on RFCOMM
+ if(startSequenceComplete){ //We only check if we are sure we are already through the start up process
+ Log.i(TAG, "Received ping, making sure we are listening to bluetooth rfcomm");
+ initBluetoothSerialService();
+ }
+ }
+ }
+ shouldServiceRemainOpen(intent);
+ return super.onStartCommand(intent, flags, startId);
+ }
+
+ @Override
+ public void onDestroy(){
+ stopClientPings();
+ if(versionCheckTimeOutHandler!=null){versionCheckTimeOutHandler.removeCallbacks(versionCheckRunable);}
+ if(altTransportTimerHandler!=null){
+ altTransportTimerHandler.removeCallbacks(versionCheckRunable);
+ altTransportTimerHandler = null;
+ versionCheckRunable = null;
+ }
+ Log.w(TAG, "Sdl Router Service Destroyed");
+ closing = true;
+ currentContext = null;
+ //No need for this Broadcast Receiver anymore
+ unregisterAllReceivers();
+ closeBluetoothSerialServer();
+ if(registeredApps!=null){
+ synchronized(REGISTERED_APPS_LOCK){
+ registeredApps.clear();
+ registeredApps = null;
+ }
+ }
+ synchronized(SESSION_LOCK){
+ if(this.sessionMap!=null){
+ this.sessionMap.clear();
+ this.sessionMap = null;
+ }
+ if(this.sessionHashIdMap!=null){
+ this.sessionHashIdMap.clear();
+ this.sessionHashIdMap = null;
+ }
+ }
+
+ //SESSION_LOCK = null;
+
+ startSequenceComplete=false;
+ if(packetExecuter!=null){
+ packetExecuter.shutdownNow();
+ packetExecuter = null;
+ }
+
+ exitForeground();
+
+ if(packetWriteTaskMaster!=null){
+ packetWriteTaskMaster.close();
+ packetWriteTaskMaster = null;
+ }
+
+ super.onDestroy();
+ System.gc(); //Lower end phones need this hint
+ if(!wrongProcess){
+ try{
+ android.os.Process.killProcess(android.os.Process.myPid());
+ }catch(Exception e){}
+ }
+ }
+
+ private void unregisterAllReceivers(){
+ try{
+ unregisterReceiver(registerAnInstanceOfSerialServer); ///This should be first. It will always be registered, these others may not be and cause an exception.
+ unregisterReceiver(mListenForDisconnect);
+ unregisterReceiver(mainServiceReceiver);
+ }catch(Exception e){}
+ }
+
+ private void notifyAltTransportOfClose(int reason){
+ if(altTransportService!=null){
+ Message msg = Message.obtain();
+ msg.what = TransportConstants.ROUTER_SHUTTING_DOWN_NOTIFICATION;
+ msg.arg1 = reason;
+ try {
+ altTransportService.send(msg);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @SuppressLint("NewApi")
+ @SuppressWarnings("deprecation")
+ private void enterForeground() {
+ if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB){
+ Log.w(TAG, "Unable to start service as foreground due to OS SDK version being lower than 11");
+ isForeground = false;
+ return;
+ }
+
+
+ Bitmap icon;
+ int resourcesIncluded = getResources().getIdentifier("sdl_128", "drawable", getPackageName());
+
+ if ( resourcesIncluded != 0 ) { //No additional pylons required
+ icon = BitmapFactory.decodeResource(getResources(), R.drawable.sdl_128);
+ }
+ else {
+ icon = BitmapFactory.decodeResource(getResources(), android.R.drawable.stat_sys_data_bluetooth);
+ }
+ // Bitmap icon = BitmapFactory.decodeByteArray(SdlLogo.SDL_LOGO_STRING, 0, SdlLogo.SDL_LOGO_STRING.length);
+
+ Notification.Builder builder = new Notification.Builder(this);
+ if(0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)){ //If we are in debug mode, include what app has the router service open
+ ComponentName name = new ComponentName(this, this.getClass());
+ builder.setContentTitle("SDL: " + name.getPackageName());
+ }else{
+ builder.setContentTitle("SmartDeviceLink");
+ }
+ builder.setTicker("SmartDeviceLink Connected");
+ builder.setContentText("Connected to " + this.getConnectedDeviceName());
+
+ //We should use icon from library resources if available
+ builder.setSmallIcon(android.R.drawable.stat_sys_data_bluetooth);
+ builder.setLargeIcon(icon);
+ builder.setOngoing(true);
+
+ Notification notification;
+ if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN){
+ notification = builder.getNotification();
+
+ }else{
+ notification = builder.build();
+ }
+ if(notification == null){
+ Log.e(TAG, "Notification was null");
+ }
+ startForeground(FOREGROUND_SERVICE_ID, notification);
+ isForeground = true;
+
+ }
+
+ private void exitForeground(){
+ if(isForeground){
+ this.stopForeground(true);
+ }
+ }
+
+
+ /* **************************************************************************************************************************************
+ *********************************************** Helper Methods **************************************************************
+ ****************************************************************************************************************************************/
+
+ public String getConnectedDeviceName(){
+ return connectedDeviceName;
+ }
+
+ /**
+ * Checks to make sure bluetooth adapter is available and on
+ * @return
+ */
+ private boolean bluetoothAvailable(){
+ boolean retVal = (!(BluetoothAdapter.getDefaultAdapter()==null) && BluetoothAdapter.getDefaultAdapter().isEnabled());
+ //Log.d(TAG, "Bluetooth Available? - " + retVal);
+ return retVal;
+ }
+
+ /**
+ *
+ * 1. If the app has SDL shut off, shut down
+ * 2. if The app has an Alt Transport address or was started by one, stay open
+ * 3. If Bluetooth is off/NA shut down
+ * 4. Anything else
+ */
+ public boolean shouldServiceRemainOpen(Intent intent){
+ //Log.d(TAG, "Determining if this service should remain open");
+
+ if(altTransportService!=null || altTransportTimerHandler !=null){
+ //We have been started by an alt transport, we must remain open. "My life for Auir...."
+ Log.d(TAG, "Alt Transport connected, remaining open");
+ return true;
+
+ }else if(intent!=null && TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT.equals(intent.getAction())){
+ Log.i(TAG, "Received start intent with alt transprt request.");
+ startAltTransportTimer();
+ return true;
+ }else if(!bluetoothAvailable()){//If bluetooth isn't on...there's nothing to see here
+ //Bluetooth is off, we should shut down
+ Log.d(TAG, "Bluetooth not available, shutting down service");
+ closeSelf();
+ return false;
+ }else{
+ Log.d(TAG, "Service to remain open");
+ return true;
+ }
+ }
+ /**
+ * This method is needed so that apps that choose not to implement this as a service as defined by Android, but rather
+ * just a simple class we have to know how to shut down.
+ */
+ public void closeSelf(){
+ closing = true;
+ storeConnectedStatus(false);
+ if(getBaseContext()!=null){
+ stopSelf();
+ }else{
+ onDestroy();
+ }
+ }
+ private synchronized void initBluetoothSerialService(){
+ if(legacyModeEnabled){
+ Log.d(TAG, "Not starting own bluetooth during legacy mode");
+ return;
+ }
+ Log.i(TAG, "Iniitializing Bluetooth Serial Class");
+ //init serial service
+ if(mSerialService ==null){
+ Log.d(TAG, "Local copy of BT Server is null");
+ mSerialService = MultiplexBluetoothTransport.getBluetoothSerialServerInstance();
+ if(mSerialService==null){
+ Log.d(TAG, "Local copy of BT Server is still null and so is global");
+ mSerialService = MultiplexBluetoothTransport.getBluetoothSerialServerInstance(mHandlerBT);
+ }
+ }
+ if (mSerialService != null) {
+ // Only if the state is STATE_NONE, do we know that we haven't started already
+ if (mSerialService.getState() == MultiplexBluetoothTransport.STATE_NONE || mSerialService.getState() == MultiplexBluetoothTransport.STATE_ERROR) {
+ // Start the Bluetooth services
+ mSerialService.start();
+ }
+
+ }
+ }
+
+ public void onTransportConnected(final TransportType type){
+ isTarnsportConnected = true;
+ enterForeground();
+ if(packetWriteTaskMaster!=null){
+ packetWriteTaskMaster.close();
+ packetWriteTaskMaster = null;
+ }
+ packetWriteTaskMaster = new PacketWriteTaskMaster();
+ packetWriteTaskMaster.start();
+
+ Intent startService = new Intent();
+ startService.setAction(START_SERVICE_ACTION);
+ startService.putExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_EXTRA, true);
+ startService.putExtra(TransportConstants.FORCE_TRANSPORT_CONNECTED, true);
+ startService.putExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_APP_PACKAGE, getBaseContext().getPackageName());
+ startService.putExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_CMP_NAME, new ComponentName(this, this.getClass()));
+ sendBroadcast(startService);
+ //HARDWARE_CONNECTED
+ }
+
+ public void onTransportDisconnected(TransportType type){
+ if(altTransportService!=null){ //If we still have an alt transport open, then we don't need to tell the clients to close
+ return;
+ }
+ Log.e(TAG, "Notifying client service of hardware disconnect.");
+
+ isTarnsportConnected = false;
+ stopClientPings();
+
+ exitForeground();//Leave our foreground state as we don't have a connection anymore
+
+ if(packetWriteTaskMaster!=null){
+ packetWriteTaskMaster.close();
+ packetWriteTaskMaster = null;
+ }
+
+ cachedModuleVersion = -1; //Reset our cached version
+ if(registeredApps== null || registeredApps.isEmpty()){
+ Log.w(TAG, "No clients to notify. Sending global notification.");
+ Intent unregisterIntent = new Intent();
+ unregisterIntent.putExtra(HARDWARE_DISCONNECTED, type.name());
+ unregisterIntent.putExtra(TransportConstants.ENABLE_LEGACY_MODE_EXTRA, legacyModeEnabled);
+ unregisterIntent.setAction(TransportConstants.START_ROUTER_SERVICE_ACTION);
+ sendBroadcast(unregisterIntent);
+ //return;
+ }else{
+ Message message = Message.obtain();
+ message.what = TransportConstants.HARDWARE_CONNECTION_EVENT;
+ Bundle bundle = new Bundle();
+ bundle.putString(HARDWARE_DISCONNECTED, type.name());
+ bundle.putBoolean(TransportConstants.ENABLE_LEGACY_MODE_EXTRA, legacyModeEnabled);
+ message.setData(bundle);
+ notifyClients(message);
+ }
+ //We've notified our clients, less clean up the mess now.
+ synchronized(SESSION_LOCK){
+ this.sessionMap.clear();
+ this.sessionHashIdMap.clear();
+ }
+ synchronized(REGISTERED_APPS_LOCK){
+ if(registeredApps==null){
+ return;
+ }
+ registeredApps.clear();
+ }
+ }
+
+ public void onPacketRead(SdlPacket packet){
+ try {
+ //Log.i(TAG, "******** Read packet with header: " +(packet).toString());
+ if(packet.getVersion() == 1){
+ if( packet.getFrameType() == FrameType.Control && packet.getFrameInfo() == SdlPacket.FRAME_INFO_START_SERVICE_ACK){
+ //We received a v1 packet from the head unit, this means we can't use the router service.
+ //Enable legacy mode
+ enableLegacyMode(true);
+ return;
+ }
+ }else if(cachedModuleVersion == -1){
+ cachedModuleVersion = packet.getVersion();
+ }
+ //Send the received packet to the registered app
+ sendPacketToRegisteredApp(packet);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private final Handler mHandlerBT = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_DEVICE_NAME:
+ connectedDeviceName = msg.getData().getString(MultiplexBluetoothTransport.DEVICE_NAME);
+ break;
+ case MESSAGE_STATE_CHANGE:
+ switch (msg.arg1) {
+ case MultiplexBluetoothTransport.STATE_CONNECTED:
+ storeConnectedStatus(true);
+ onTransportConnected(TransportType.BLUETOOTH);
+ break;
+ case MultiplexBluetoothTransport.STATE_CONNECTING:
+ // Currently attempting to connect - update UI?
+ break;
+ case MultiplexBluetoothTransport.STATE_LISTEN:
+ storeConnectedStatus(false);
+ break;
+ case MultiplexBluetoothTransport.STATE_NONE:
+ // We've just lost the connection
+ storeConnectedStatus(false);
+ if(!connectAsClient && !closing){
+ if(!legacyModeEnabled){
+ initBluetoothSerialService();
+ }
+ onTransportDisconnected(TransportType.BLUETOOTH);
+ }
+ break;
+ case MultiplexBluetoothTransport.STATE_ERROR:
+ if(mSerialService!=null){
+ Log.d(TAG, "Bluetooth serial server error received, setting state to none, and clearing local copy");
+ mSerialService.setStateManually(MultiplexBluetoothTransport.STATE_NONE);
+ mSerialService = null;
+ }
+ break;
+ }
+ break;
+
+ case MESSAGE_READ:
+ onPacketRead((SdlPacket) msg.obj);
+ break;
+ }
+ }
+ };
+
+ @SuppressWarnings("unused") //The return false after the packet null check is not dead code. Read the getByteArray method from bundle
+ public boolean writeBytesToTransport(Bundle bundle){
+ if(bundle == null){
+ return false;
+ }
+ if(mSerialService !=null && mSerialService.getState()==MultiplexBluetoothTransport.STATE_CONNECTED){
+ byte[] packet = bundle.getByteArray(TransportConstants.BYTES_TO_SEND_EXTRA_NAME);
+ int offset = bundle.getInt(TransportConstants.BYTES_TO_SEND_EXTRA_OFFSET, 0); //If nothing, start at the begining of the array
+ int count = bundle.getInt(TransportConstants.BYTES_TO_SEND_EXTRA_COUNT, packet.length); //In case there isn't anything just send the whole packet.
+ if(packet!=null){
+ mSerialService.write(packet,offset,count); Log.i(TAG, "Wrote out bytes");
+ return true;
+ }
+ return false;
+ }else if(sendThroughAltTransport(bundle)){
+ return true;
+ }
+ else{
+ Log.e(TAG, "Can't send data, no transport connected");
+ return false;
+ }
+ }
+
+ private boolean manuallyWriteBytes(byte[] bytes, int offset, int count){
+ if(mSerialService !=null && mSerialService.getState()==MultiplexBluetoothTransport.STATE_CONNECTED){
+ if(bytes!=null){
+ mSerialService.write(bytes,offset,count);Log.i(TAG, "Wrote out bytes manually");
+ return true;
+ }
+ return false;
+ }else if(sendThroughAltTransport(bytes,offset,count)){
+ return true;
+ }else{
+ return false;
+ }
+ }
+
+
+ /**
+ * This Method will send the packets through the alt transport that is connected
+ * @param array The byte array of data to be wrote out
+ * @return If it was possible to send the packet off.
+ * <p><b>NOTE: This is not guaranteed. It is a best attempt at sending the packet, it may fail.</b>
+ */
+ private boolean sendThroughAltTransport(Bundle bundle){
+ if(altTransportService!=null){
+ Log.d(TAG, "Sending packet through alt transport");
+ Message msg = Message.obtain();
+ msg.what = TransportConstants.ROUTER_SEND_PACKET;
+ msg.setData(bundle);
+ try {
+ altTransportService.send(msg);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to send through alt transport!");
+ e.printStackTrace();
+ }
+ return true;
+ }else{
+ Log.w(TAG, "Unable to send packet through alt transport, it was null");
+ }
+ return false;
+ }
+
+ /** This Method will send the packets through the alt transport that is connected
+ * @param array The byte array of data to be wrote out
+ * @return If it was possible to send the packet off.
+ * <p><b>NOTE: This is not guaranteed. It is a best attempt at sending the packet, it may fail.</b>
+ */
+ private boolean sendThroughAltTransport(byte[] bytes, int offset, int count){
+ if(altTransportService!=null){
+ Log.d(TAG, "Sending packet through alt transport");
+ Message msg = Message.obtain();
+ msg.what = TransportConstants.ROUTER_SEND_PACKET;
+ Bundle bundle = new Bundle();
+ bundle.putByteArray(TransportConstants.BYTES_TO_SEND_EXTRA_NAME,bytes);
+ bundle.putInt(TransportConstants.BYTES_TO_SEND_EXTRA_OFFSET, offset);
+ bundle.putInt(TransportConstants.BYTES_TO_SEND_EXTRA_COUNT, count);
+ msg.setData(bundle);
+ try {
+ altTransportService.send(msg);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to send through alt transport!");
+ e.printStackTrace();
+ }
+ return true;
+ }else{
+ Log.w(TAG, "Unable to send packet through alt transport, it was null");
+ }
+ return false;
+ }
+ /**
+ * This will send the received packet to the registered service. It will default to the single registered "foreground" app.
+ * This can be overridden to provide more specific functionality.
+ * @param packet the packet that is received
+ * @return whether or not the sending was successful
+ */
+ public boolean sendPacketToRegisteredApp(SdlPacket packet) {
+ if(registeredApps!=null && (registeredApps.size()>0)){
+ int session = packet.getSessionId();
+ boolean shouldAssertNewSession = packet.getFrameType() == FrameType.Control && (packet.getFrameInfo() == SdlPacket.FRAME_INFO_START_SERVICE_ACK || packet.getFrameInfo() == SdlPacket.FRAME_INFO_START_SERVICE_NAK);
+ Long appid = getAppIDForSession(session, shouldAssertNewSession); //Find where this packet should go
+ if(appid!=null){
+ RegisteredApp app = null;
+ synchronized(REGISTERED_APPS_LOCK){
+ app = registeredApps.get(appid);
+ }
+ if(app==null){Log.e(TAG, "No app found for app id " + appid + " Removing session maping and sending unregisterAI to head unit.");
+ //We have no app to match the app id tied to this session
+ removeSessionFromMap(session);
+ byte[] uai = createForceUnregisterApp((byte)session, (byte)packet.getVersion());
+ manuallyWriteBytes(uai,0,uai.length);
+ int hashId = 0;
+ synchronized(this.SESSION_LOCK){
+ if(this.sessionHashIdMap.indexOfKey(session)>=0){
+ hashId = this.sessionHashIdMap.get(session);
+ this.sessionHashIdMap.remove(session);
+ }
+ }
+ byte[] stopService = (SdlPacketFactory.createEndSession(SessionType.RPC, (byte)session, 0, (byte)packet.getVersion(),BitConverter.intToByteArray(hashId))).constructPacket();
+ manuallyWriteBytes(stopService,0,stopService.length);
+ return false;
+ }
+ byte version = (byte)packet.getVersion();
+
+ if(shouldAssertNewSession && version>1 && packet.getFrameInfo() == SdlPacket.FRAME_INFO_START_SERVICE_ACK){ //we know this was a start session response
+ if (packet.getPayload() != null && packet.getDataSize() == 4){ //hashid will be 4 bytes in length
+ synchronized(SESSION_LOCK){
+ this.sessionHashIdMap.put(session, (BitConverter.intFromByteArray(packet.getPayload(), 0)));
+ }
+ }
+ }
+
+ int packetSize = (int) (packet.getDataSize() + SdlPacket.HEADER_SIZE);
+ //Log.i(TAG, "Checking packet size: " + packetSize);
+ Message message = Message.obtain();
+ Bundle bundle = new Bundle();
+
+ if(packetSize < ByteArrayMessageSpliter.MAX_BINDER_SIZE){ //This is a small enough packet just send on through
+ //Log.w(TAG, " Packet size is just right " + packetSize + " is smaller than " + ByteArrayMessageSpliter.MAX_BINDER_SIZE + " = " + (packetSize<ByteArrayMessageSpliter.MAX_BINDER_SIZE));
+ message.what = TransportConstants.ROUTER_RECEIVED_PACKET;
+ bundle.putParcelable(FORMED_PACKET_EXTRA_NAME, packet);
+ bundle.putInt(TransportConstants.BYTES_TO_SEND_FLAGS, TransportConstants.BYTES_TO_SEND_FLAG_NONE);
+ message.setData(bundle);
+ return sendPacketMessageToClient(app,message, version);
+ }else{
+ //Log.w(TAG, "Packet too big for IPC buffer. Breaking apart and then sending to client.");
+ //We need to churn through the packet payload and send it in chunks
+ byte[] bytes = packet.getPayload();
+ SdlPacket copyPacket = new SdlPacket(packet.getVersion(),packet.isCompression(),
+ (int)packet.getFrameType().getValue(),
+ packet.getServiceType(),packet.getFrameInfo(), session,
+ (int)packet.getDataSize(),packet.getMessageId(),null);
+ message.what = TransportConstants.ROUTER_RECEIVED_PACKET;
+ bundle.putParcelable(FORMED_PACKET_EXTRA_NAME, copyPacket);
+ bundle.putInt(TransportConstants.BYTES_TO_SEND_FLAGS, TransportConstants.BYTES_TO_SEND_FLAG_SDL_PACKET_INCLUDED);
+ message.setData(bundle);
+ Log.d(TAG, "First packet before sending: " + message.getData().toString());
+ if(!sendPacketMessageToClient(app, message, version)){
+ Log.w(TAG, "Error sending first message of split packet to client " + app.appId);
+ return false;
+ }
+ //Log.w(TAG, "Message too big for single IPC transaction. Breaking apart. Size - " + packet.getDataSize());
+ ByteArrayMessageSpliter splitter = new ByteArrayMessageSpliter(appid,TransportConstants.ROUTER_RECEIVED_PACKET,bytes,0);
+ while(splitter.isActive()){
+ if(!sendPacketMessageToClient(app,splitter.nextMessage(),version)){
+ Log.w(TAG, "Error sending first message of split packet to client " + app.appId);
+ splitter.close();
+ return false;
+ }
+ }
+ Log.i(TAG, "Large packet finished being sent");
+ }
+
+ }else{ //If we can't find a session for this packet we just drop the packet
+ Log.e(TAG, "App Id was NULL for session!");
+ if(removeSessionFromMap(session)){ //If we found the session id still tied to an app in our map we need to remove it and send the proper shutdown sequence.
+ Log.i(TAG, "Removed session from map. Sending unregister request to module.");
+ attemptToCleanUpModule(session, packet.getVersion());
+ }else{ //There was no mapping so let's try to resolve this
+
+ if(packet.getFrameType() == FrameType.Single && packet.getServiceType() == SdlPacket.SERVICE_TYPE_RPC){
+ BinaryFrameHeader binFrameHeader = BinaryFrameHeader.parseBinaryHeader(packet.getPayload());
+ if(binFrameHeader!=null && FunctionID.UNREGISTER_APP_INTERFACE.getId() == binFrameHeader.getFunctionID()){
+ Log.d(TAG, "Received an unregister app interface with no where to send it, dropping the packet.");
+ }else{
+ attemptToCleanUpModule(session, packet.getVersion());
+ }
+ }else if((packet.getFrameType() == FrameType.Control
+ && (packet.getFrameInfo() == SdlPacket.FRAME_INFO_END_SERVICE_ACK || packet.getFrameInfo() == SdlPacket.FRAME_INFO_END_SERVICE_NAK))){
+ //We want to ignore this
+ Log.d(TAG, "Received a stop service ack/nak with no where to send it, dropping the packet.");
+ }else{
+ attemptToCleanUpModule(session, packet.getVersion());
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This method is an all else fails situation. If the head unit is out of synch with the apps on the phone
+ * this method will clear out an unwanted or out of date session.
+ * @param session
+ * @param version
+ */
+ private void attemptToCleanUpModule(int session, int version){
+ Log.i(TAG, "Attempting to stop session " + session);
+ byte[] uai = createForceUnregisterApp((byte)session, (byte)version);
+ manuallyWriteBytes(uai,0,uai.length);
+ int hashId = 0;
+ synchronized(this.SESSION_LOCK){
+ if(this.sessionHashIdMap.indexOfKey(session)>=0){
+ hashId = this.sessionHashIdMap.get(session);
+ this.sessionHashIdMap.remove(session);
+ }
+ }
+ byte[] stopService = (SdlPacketFactory.createEndSession(SessionType.RPC, (byte)session, 0, (byte)version,BitConverter.intToByteArray(hashId))).constructPacket();
+ manuallyWriteBytes(stopService,0,stopService.length);
+ }
+
+ private boolean sendPacketMessageToClient(RegisteredApp app, Message message, byte version){
+ int result = app.sendMessage(message);
+ if(result == RegisteredApp.SEND_MESSAGE_ERROR_MESSENGER_DEAD_OBJECT){
+ Log.d(TAG, "Dead object, removing app and sessions");
+ //Get all their sessions and send out unregister info
+ //Use the version in this packet as a best guess
+ app.close();
+ Vector<Long> sessions = app.getSessionIds();
+ byte[] unregister,stopService;
+ int size = sessions.size(), sessionId;
+ for(int i=0; i<size;i++){
+ sessionId = sessions.get(i).intValue();
+ unregister = createForceUnregisterApp((byte)sessionId,version);
+ manuallyWriteBytes(unregister,0,unregister.length);
+ int hashId = 0;
+ synchronized(this.SESSION_LOCK){
+ if(this.sessionHashIdMap.indexOfKey(sessionId)>=0){
+ hashId = this.sessionHashIdMap.get(sessionId);
+ }
+ }
+ stopService = (SdlPacketFactory.createEndSession(SessionType.RPC, (byte)sessionId, 0, version,BitConverter.intToByteArray(hashId))).constructPacket();
+
+ manuallyWriteBytes(stopService,0,stopService.length);
+ synchronized(SESSION_LOCK){
+ this.sessionMap.remove(sessionId);
+ this.sessionHashIdMap.remove(sessionId);
+ }
+ }
+ synchronized(REGISTERED_APPS_LOCK){
+ registeredApps.remove(app.appId);
+ }
+ return false;//We did our best to correct errors
+ }
+ return true;//We should have sent our packet, so we can return true now
+ }
+
+ private synchronized void closeBluetoothSerialServer(){
+ if(mSerialService != null){
+ mSerialService.stop();
+ mSerialService = null;
+ }
+ }
+
+ /**
+ * bluetoothQuerryAndConnect()
+ * This function looks through the phones currently paired bluetooth devices
+ * If one of the devices' names contain "sync", or livio it will attempt to connect the RFCOMM
+ * And start SDL
+ * @return a boolean if a connection was attempted
+ */
+ public synchronized boolean bluetoothQuerryAndConnect(){
+ if( BluetoothAdapter.getDefaultAdapter().isEnabled()){
+ Set<BluetoothDevice> pairedBT= BluetoothAdapter.getDefaultAdapter().getBondedDevices();
+ Log.d(TAG, "Querry Bluetooth paired devices");
+ if (pairedBT.size() > 0) {
+ for (BluetoothDevice device : pairedBT) {
+ if(device.getName().toLowerCase(Locale.US).contains("sync")
+ || device.getName().toLowerCase(Locale.US).contains("livio")){
+ bluetoothConnect(device);
+ return true;
+ }
+ }
+ }
+ }
+ else{
+ Log.e(TAG, "There was an issue with connecting as client");
+ }
+ return false;
+ }
+
+ private synchronized boolean bluetoothConnect(BluetoothDevice device){
+ Log.d(TAG,"Connecting to device: " + device.getName().toString());
+ if(mSerialService == null || !mSerialService.isConnected())
+ { // Set up the Bluetooth serial object
+ mSerialService = MultiplexBluetoothTransport.getBluetoothSerialServerInstance(mHandlerBT);
+ }
+ // We've been given a device - let's connect to it
+ if(mSerialService.getState()!=MultiplexBluetoothTransport.STATE_CONNECTING){//mSerialService.stop();
+ mSerialService.connect(device);
+ if(mSerialService.getState() == MultiplexBluetoothTransport.STATE_CONNECTING){
+ return true;
+ }
+ }
+
+ Log.d(TAG, "Bluetooth SPP Connect Attempt Completed");
+ return false;
+ }
+
+
+ //**************************************************************************************************************************************
+ //********************************************************* PREFERENCES ****************************************************************
+ //**************************************************************************************************************************************
+
+ @SuppressLint("WorldReadableFiles")
+ @SuppressWarnings("deprecation")
+ private void storeConnectedStatus(boolean isConnected){
+ SharedPreferences prefs = getApplicationContext().getSharedPreferences(getApplicationContext().getPackageName()+SdlBroadcastReceiver.TRANSPORT_GLOBAL_PREFS,
+ Context.MODE_WORLD_READABLE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(SdlBroadcastReceiver.IS_TRANSPORT_CONNECTED, isConnected);
+ editor.commit();
+ }
+
+ /**
+ * This method will set the last known bluetooth connection method that worked with this phone.
+ * This helps speed up the process of connecting
+ * @param level The level of bluetooth connecting method that last worked
+ * @param prefLocation Where the preference should be stored
+ */
+ public final static void setBluetoothPrefs (int level, String prefLocation) {
+ if(currentContext==null){
+ return;
+ }
+ SharedPreferences mBluetoothPrefs = currentContext.getSharedPreferences(prefLocation, Context.MODE_PRIVATE);
+ // Write the new prefs
+ SharedPreferences.Editor prefAdd = mBluetoothPrefs.edit();
+ prefAdd.putInt("level", level);
+ prefAdd.commit();
+ }
+
+ public final static int getBluetoothPrefs(String prefLocation)
+ {
+ if(currentContext==null){
+ return 0;
+ }
+ SharedPreferences mBluetoothPrefs = currentContext.getSharedPreferences(prefLocation, Context.MODE_PRIVATE);
+ return mBluetoothPrefs.getInt("level", 0);
+ }
+
+ /* ***********************************************************************************************************************************************************************
+ * ***************************************************************** CUSTOM ADDITIONS ************************************************************************************
+ *************************************************************************************************************************************************************************/
+
+ private LocalRouterService getLocalBluetoothServiceComapre(){
+ return this.localCompareTo;
+ }
+
+ protected static LocalRouterService getLocalRouterService(Intent launchIntent, ComponentName name){
+ if(SdlRouterService.selfRouterService == null){
+ if(launchIntent == null){
+ Log.w(TAG, "Supplied intent was null, local router service will not contain intent");
+ //Log.e(TAG, "Unable to create local router service instance. Supplied intent was null");
+ //return null;
+ }
+ if(name == null){
+ Log.e(TAG, "Unable to create local router service object because component name was null");
+ return null;
+ }
+ selfRouterService = new LocalRouterService(launchIntent,ROUTER_SERVICE_VERSION_NUMBER, System.currentTimeMillis(), name);
+ }
+ if(launchIntent!=null){
+ //Assume we want to set this in our service
+ //Log.d(TAG, "Setting new intent on our local router service object");
+ selfRouterService.launchIntent = launchIntent;
+ }
+ return selfRouterService;
+ }
+
+ private LocalRouterService getLocalRouterService(){
+ //return getLocalRouterService(new Intent(getBaseContext(),SdlRouterService.class));
+ return getLocalRouterService(null, new ComponentName(this, this.getClass()));
+ }
+ /**
+ * This method is used to check for the newest version of this class to make sure the latest and greatest is up and running.
+ * @param context
+ */
+ private void newestServiceCheck(final Context context){
+ getLocalRouterService(); //Make sure our timestamp is set
+ versionCheckTimeOutHandler = new Handler();
+ versionCheckRunable = new Runnable() {
+ public void run() {
+ Log.i(TAG, "Starting up Version Checking ");
+
+ LocalRouterService newestServiceReceived = getLocalBluetoothServiceComapre();
+ LocalRouterService self = getLocalRouterService(); //We can send in null here, because it should have already been created
+ Log.v(TAG, "Self service info " + self);
+ Log.v(TAG, "Newest compare to service info " + newestServiceReceived);
+ if(newestServiceReceived!=null && self.isNewer(newestServiceReceived)){
+ Log.d(TAG, "There is a newer version of the Router Service, starting it up");
+ closing = true;
+ closeBluetoothSerialServer();
+ Intent serviceIntent = newestServiceReceived.launchIntent;
+ if(getLastReceivedStartIntent()!=null){
+ serviceIntent.putExtras(getLastReceivedStartIntent());
+ }
+ if(newestServiceReceived.launchIntent == null){
+ Log.e(TAG, "Service didn't include launch intent");
+ }
+ context.startService(newestServiceReceived.launchIntent);
+ notifyAltTransportOfClose(TransportConstants.ROUTER_SHUTTING_DOWN_REASON_NEWER_SERVICE);
+ if(getBaseContext()!=null){
+ stopSelf();
+ }else{
+ onDestroy();
+ }
+ }
+ else{ //Let's start up like normal
+ Log.d(TAG, "No newer services found. Starting up bluetooth transport");
+ startUpSequence();
+ }
+ }
+ };
+ versionCheckTimeOutHandler.postDelayed(versionCheckRunable, VERSION_TIMEOUT_RUNNABLE);
+ }
+
+ /**
+ * This method is used to check for the newest version of this class to make sure the latest and greatest is up and running.
+ * @param context
+ */
+ private void startAltTransportTimer(){
+ altTransportTimerHandler = new Handler();
+ altTransportTimerRunnable = new Runnable() {
+ public void run() {
+ altTransportTimerHandler = null;
+ altTransportTimerRunnable = null;
+ shouldServiceRemainOpen(null);
+ }
+ };
+ altTransportTimerHandler.postDelayed(altTransportTimerRunnable, ALT_TRANSPORT_TIMEOUT_RUNNABLE);
+ }
+
+ private Intent getLastReceivedStartIntent(){
+ return lastReceivedStartIntent;
+ }
+
+ /**
+ * Removes session from map if the key is found.
+ * @param sessionId
+ * @return if the key was found
+ */
+ private boolean removeSessionFromMap(int sessionId){
+ synchronized(SESSION_LOCK){
+ if(sessionMap!=null){
+ if(sessionMap.indexOfKey(sessionId)>=0){
+ sessionMap.remove(sessionId);
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private boolean removeAllSessionsWithAppId(long appId){
+ synchronized(SESSION_LOCK){
+ if(sessionMap!=null){
+ SparseArray<Long> iter = sessionMap.clone();
+ int size = iter.size();
+ for(int i = 0; i<size; i++){
+ Log.d(TAG, "Investigating session " +iter.keyAt(i));
+ Log.d(TAG, "App id is: " + iter.valueAt(i));
+ if(((Long)iter.valueAt(i)).compareTo(appId) == 0){
+ sessionHashIdMap.remove(iter.keyAt(i));
+ sessionMap.removeAt(i);
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes all sessions from the sessions map for this given app id
+ * @param app
+ */
+ private void removeAllSessionsForApp(RegisteredApp app, boolean cleanModule){
+ Vector<Long> sessions = app.getSessionIds();
+ int size = sessions.size(), sessionId;
+ for(int i=0; i<size;i++){
+ Log.d(TAG, "Investigating session " +sessions.get(i).intValue());
+ Log.d(TAG, "App id is: " + sessionMap.get(sessions.get(i).intValue()));
+ sessionId = sessions.get(i).intValue();
+ removeSessionFromMap(sessionId);
+ if(cleanModule){
+ attemptToCleanUpModule(sessionId, cachedModuleVersion);
+ }
+ }
+ }
+
+ private boolean removeAppFromMap(RegisteredApp app){
+ synchronized(REGISTERED_APPS_LOCK){
+ RegisteredApp old = registeredApps.remove(app);
+ if(old!=null){
+ old.close();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private Long getAppIDForSession(int sessionId, boolean shouldAssertNewSession){
+ synchronized(SESSION_LOCK){
+ //Log.d(TAG, "Looking for session: " + sessionId);
+ if(sessionMap == null){
+ Log.w(TAG, "Session map was null during look up. Creating one on the fly");
+ sessionMap = new SparseArray<Long>(); //THIS SHOULD NEVER BE NULL! WHY IS THIS HAPPENING?!?!?!
+ }
+ Long appId = sessionMap.get(sessionId);// SdlRouterService.this.sessionMap.get(sessionId);
+ if(appId==null && shouldAssertNewSession){
+ int pos;
+ synchronized(REGISTERED_APPS_LOCK){
+ for (RegisteredApp app : registeredApps.values()) {
+ pos = app.containsSessionId(-1);
+ if(pos != -1){
+ app.setSessionId(pos,sessionId);
+ appId = app.getAppId();
+ sessionMap.put(sessionId, appId);
+ break;
+ }
+ }
+ }
+ }
+ Log.d(TAG, sessionId + " session returning App Id: " + appId);
+ return appId;
+ }
+ }
+
+ /* ****************************************************************************************************************************************
+ // *********************************************************** LEGACY ****************************************************************
+ //*****************************************************************************************************************************************/
+ private boolean legacyModeEnabled = false;
+
+ private void enableLegacyMode(boolean enable){
+ Log.d(TAG, "Enable legacy mode: " + enable);
+ legacyModeEnabled = enable; //We put this at the end to avoid a race condition between the bluetooth d/c and notify of legacy mode enabled
+
+ if(legacyModeEnabled){
+ //So we need to let the clients know they need to host their own bluetooth sessions because the currently connected head unit only supports a very old version of SDL/Applink
+ //Start by closing our own bluetooth connection. The following calls will handle actually notifying the clients of legacy mode
+ closeBluetoothSerialServer();
+ //Now wait until we get a d/c, then the apps should shut their bluetooth down and go back to normal
+
+ }//else{}
+
+ }
+
+ /* ****************************************************************************************************************************************
+ // *********************************************************** UTILITY ****************************************************************
+ //*****************************************************************************************************************************************/
+
+ @SuppressWarnings("unused")
+ private void debugPacket(byte[] bytes){
+ //DEBUG
+
+ if(bytes[0] != 0x00){
+ Log.d(TAG, "Writing packet with header: " + BitConverter.bytesToHex(bytes, 12)); //just want the header
+ }else{
+
+ //Log.d(TAG, "Writing packet with binary header: " + BitConverter.bytesToHex(bytes, 12)); //just want the header
+ //int length = bytes.length-12;
+ if(bytes.length<=8){
+ Log.w(TAG, "No payload to debug or too small");
+ return;
+ }
+ //Check first byte if 0, make to json
+ char[] buffer = new char[bytes.length];
+ for(int i = 12;i<bytes.length;i++){
+ buffer[i-12] = (char)(bytes[i] & 0xFF);
+ }
+ try {
+
+ JSONObject object = new JSONObject(new String(buffer));
+ Log.d(TAG, "JSON: " + object.toString());
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
+ }
+
+ /**
+ * If an app crashes the only way we can handle it being on the head unit is to send an unregister app interface rpc.
+ * This method should only be called when the router service recognizes the client is no longer valid
+ * @param sessionId
+ * @param version
+ * @return
+ */
+ private byte[] createForceUnregisterApp(byte sessionId,byte version){
+ UnregisterAppInterface request = new UnregisterAppInterface();
+ request.setCorrelationID(UNREGISTER_APP_INTERFACE_CORRELATION_ID);
+ byte[] msgBytes = JsonRPCMarshaller.marshall(request, version);
+ ProtocolMessage pm = new ProtocolMessage();
+ pm.setData(msgBytes);
+ pm.setSessionID(sessionId);
+ pm.setMessageType(MessageType.RPC);
+ pm.setSessionType(SessionType.RPC);
+ pm.setFunctionID(FunctionID.getFunctionId(request.getFunctionName()));
+ pm.setCorrID(request.getCorrelationID());
+ if (request.getBulkData() != null)
+ pm.setBulkData(request.getBulkData());
+ byte[] data = null;
+ if (version > 1) {
+ data = new byte[12 + pm.getJsonSize()];
+ } else {
+ data = pm.getData();
+ }
+ BinaryFrameHeader binFrameHeader = new BinaryFrameHeader();
+ binFrameHeader = SdlPacketFactory.createBinaryFrameHeader(pm.getRPCType(), pm.getFunctionID(), pm.getCorrID(), pm.getJsonSize());
+
+ System.arraycopy(binFrameHeader.assembleHeaderBytes(), 0, data, 0, 12);
+ System.arraycopy(pm.getData(), 0, data, 12, pm.getJsonSize());
+
+ SdlPacket packet = new SdlPacket(version,false,SdlPacket.FRAME_TYPE_SINGLE,SdlPacket.SERVICE_TYPE_RPC,0,sessionId,data.length,data.length+100,data);
+ return packet.constructPacket();
+ }
+
+
+ /**
+ * Method for finding the next, highest priority write task from all connected apps.
+ * @return
+ */
+ protected PacketWriteTask getNextTask(){
+ final long currentTime = System.currentTimeMillis();
+ RegisteredApp priorityApp = null;
+ long currentPriority = -Long.MAX_VALUE, peekWeight;
+ synchronized(REGISTERED_APPS_LOCK){
+ PacketWriteTask peekTask = null;
+ for (RegisteredApp app : registeredApps.values()) {
+ peekTask = app.peekNextTask();
+ if(peekTask!=null){
+ peekWeight = peekTask.getWeight(currentTime);
+ Log.v(TAG, "App " + app.appId +" has a task with weight "+ peekWeight);
+ if(peekWeight>currentPriority){
+ if(app.queuePaused){
+ app.notIt();//Reset the timer
+ continue;
+ }
+ if(priorityApp!=null){
+ priorityApp.notIt();
+ }
+ currentPriority = peekWeight;
+ priorityApp = app;
+ }
+ }
+ }
+ if(priorityApp!=null){
+ return priorityApp.getNextTask();
+ }
+ }
+ return null;
+ }
+
+ private void startClientPings(){
+ synchronized(this){
+ if(!isTarnsportConnected){ //If we aren't connected, bail
+ return;
+ }
+ if(isPingingClients){
+ Log.w(TAG, "Already pinging clients. Resting count");
+ synchronized(PING_COUNT_LOCK){
+ pingCount = 0;
+ }
+ return;
+ }
+ if(clientPingExecutor == null){
+ clientPingExecutor = Executors.newSingleThreadScheduledExecutor();
+ }
+ isPingingClients = true;
+ synchronized(PING_COUNT_LOCK){
+ pingCount = 0;
+ }
+ clientPingExecutor.scheduleAtFixedRate(new Runnable(){
+
+ @Override
+ public void run() {
+ if(getPingCount()>=10){
+ Log.d(TAG, "Hit ping limit");
+ stopClientPings();
+ return;
+ }
+ if(pingIntent == null){
+ pingIntent = new Intent();
+ pingIntent.setAction(START_SERVICE_ACTION);
+ pingIntent.putExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_EXTRA, true);
+ pingIntent.putExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_APP_PACKAGE, getBaseContext().getPackageName());
+ pingIntent.putExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_CMP_NAME, new ComponentName(SdlRouterService.this, SdlRouterService.this.getClass()));
+ pingIntent.putExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_PING, true);
+ }
+ getBaseContext().sendBroadcast(pingIntent);
+ synchronized(PING_COUNT_LOCK){
+ pingCount++;
+ }
+
+ }
+ }, CLIENT_PING_DELAY, CLIENT_PING_DELAY, TimeUnit.MILLISECONDS); //Give a little delay for first call
+ }
+ }
+
+ private int getPingCount(){
+ synchronized(PING_COUNT_LOCK){
+ return pingCount;
+ }
+ }
+
+ private void stopClientPings(){
+ if(clientPingExecutor!=null && !clientPingExecutor.isShutdown()){
+ clientPingExecutor.shutdownNow();
+ clientPingExecutor = null;
+ isPingingClients = false;
+ }
+ pingIntent = null;
+ }
+
+ /* ****************************************************************************************************************************************
+ // ********************************************************** TINY CLASSES ************************************************************
+ //*****************************************************************************************************************************************/
+
+ /**
+ *This class enables us to compare two router services
+ * from different apps and determine which is the newest
+ * and therefore which one should be the one spun up.
+ * @author Joey Grover
+ *
+ */
+ static class LocalRouterService implements Parcelable{
+ Intent launchIntent = null;
+ int version = 0;
+ long timestamp;
+ ComponentName name;
+
+ private LocalRouterService(Intent intent, int version, long timeStamp,ComponentName name ){
+ this.launchIntent = intent;
+ this.version = version;
+ this.timestamp = timeStamp;
+ this.name = name;
+ }
+ /**
+ * Check if input is newer than this version
+ * @param service
+ * @return
+ */
+ public boolean isNewer(LocalRouterService service){
+ if(service.version>this.version){
+ return true;
+ }else if(service.version == this.version){ //If we have the same version, we will use a timestamp
+ return service.timestamp<this.timestamp;
+ }
+ return false;
+ }
+
+
+ public boolean isEqual(LocalRouterService service) {
+ if(service != null && service.name!= null
+ && this.name !=null){
+ return (this.name.equals(service.name));
+ }
+ return false;
+ }
+ @Override
+ public String toString() {
+ StringBuilder build = new StringBuilder();
+ build.append("Intent action: ");
+ if(launchIntent!=null){
+ build.append(launchIntent.getComponent().getClassName());
+ }else if(name!=null){
+ build.append(name.getClassName());
+ }
+
+ build.append(" Version: ");
+ build.append(version);
+ build.append(" Timestamp: ");
+ build.append(timestamp);
+
+ return build.toString();
+ }
+ public LocalRouterService(Parcel p) {
+ this.version = p.readInt();
+ this.timestamp = p.readLong();
+ this.launchIntent = p.readParcelable(Intent.class.getClassLoader());
+ this.name = p.readParcelable(ComponentName.class.getClassLoader());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(version);
+ dest.writeLong(timestamp);
+ dest.writeParcelable(launchIntent, 0);
+ dest.writeParcelable(name, 0);
+
+ }
+
+ public static final Parcelable.Creator<LocalRouterService> CREATOR = new Parcelable.Creator<LocalRouterService>() {
+ public LocalRouterService createFromParcel(Parcel in) {
+ return new LocalRouterService(in);
+ }
+
+ @Override
+ public LocalRouterService[] newArray(int size) {
+ return new LocalRouterService[size];
+ }
+
+ };
+
+ }
+
+
+ /**
+ * This class helps keep track of all the different sessions established with the head unit
+ * and to which app they belong to.
+ * @author Joey Grover
+ *
+ */
+ class RegisteredApp {
+ protected static final int SEND_MESSAGE_SUCCESS = 0x00;
+ protected static final int SEND_MESSAGE_ERROR_MESSAGE_NULL = 0x01;
+ protected static final int SEND_MESSAGE_ERROR_MESSENGER_NULL = 0x02;
+ protected static final int SEND_MESSAGE_ERROR_MESSENGER_GENERIC_EXCEPTION = 0x03;
+ protected static final int SEND_MESSAGE_ERROR_MESSENGER_DEAD_OBJECT = 0x04;
+
+ protected static final int PAUSE_TIME_FOR_QUEUE = 1500;
+
+ long appId;
+ Messenger messenger;
+ Vector<Long> sessionIds;
+ ByteAraryMessageAssembler buffer;
+ int prioirtyForBuffingMessage;
+ DeathRecipient deathNote = null;
+ //Packey queue vars
+ PacketWriteTaskBlockingQueue queue;
+ Handler queueWaitHandler= null;
+ Runnable queueWaitRunnable = null;
+ boolean queuePaused = false;
+
+ /**
+ * This is a simple class to hold onto a reference of a registered app.
+ * @param appId
+ * @param messenger
+ */
+ public RegisteredApp(long appId, Messenger messenger){
+ this.appId = appId;
+ this.messenger = messenger;
+ this.sessionIds = new Vector<Long>();
+ this.queue = new PacketWriteTaskBlockingQueue();
+ queueWaitHandler = new Handler();
+ setDeathNote();
+ }
+
+ /**
+ * Closes this app properly.
+ */
+ public void close(){
+ clearDeathNote();
+ clearBuffer();
+ if(this.queue!=null){
+ this.queue.clear();
+ queue = null;
+ }
+ if(queueWaitHandler!=null){
+ if(queueWaitRunnable!=null){
+ queueWaitHandler.removeCallbacks(queueWaitRunnable);
+ }
+ queueWaitHandler = null;
+ }
+ }
+
+ public long getAppId() {
+ return appId;
+ }
+
+ /**
+ * This is a convenience variable and may not be used or useful in different protocols
+ * @return
+ */
+ public Vector<Long> getSessionIds() {
+ return sessionIds;
+ }
+
+ /**
+ * Returns the position of the desired object if it is contained in the vector. If not it will return -1.
+ * @param id
+ * @return
+ */
+ public int containsSessionId(long id){
+ return sessionIds.indexOf(id);
+ }
+ /**
+ * This will remove a session from the session id list
+ * @param sessionId
+ * @return
+ */
+ public boolean removeSession(Long sessionId){
+ int location = sessionIds.indexOf(sessionId);
+ if(location>=0){
+ Long removedSessionId = sessionIds.remove(location);
+ if(removedSessionId!=null){
+ return true;
+ }else{
+ return false;
+ }
+ }else{
+ return false;
+ }
+ }
+ /**
+ * @param sessionId
+ */
+ public void setSessionId(int position,long sessionId) throws ArrayIndexOutOfBoundsException {
+ this.sessionIds.set(position, (long)sessionId);
+ }
+
+ public void clearSessionIds(){
+ this.sessionIds.clear();
+ }
+
+ public boolean handleIncommingClientMessage(final Bundle receivedBundle){
+ int flags = receivedBundle.getInt(TransportConstants.BYTES_TO_SEND_FLAGS, TransportConstants.BYTES_TO_SEND_FLAG_NONE);
+ Log.d(TAG, "Flags received: " + flags);
+ if(flags!=TransportConstants.BYTES_TO_SEND_FLAG_NONE){
+ byte[] packet = receivedBundle.getByteArray(TransportConstants.BYTES_TO_SEND_EXTRA_NAME);
+ if(flags == TransportConstants.BYTES_TO_SEND_FLAG_LARGE_PACKET_START){
+ this.prioirtyForBuffingMessage = receivedBundle.getInt(TransportConstants.PACKET_PRIORITY_COEFFICIENT,0);
+ }
+ handleMessage(flags, packet);
+ }else{
+ //Add the write task on the stack
+ if(queue!=null){
+ queue.add(new PacketWriteTask(receivedBundle));
+ if(packetWriteTaskMaster!=null){
+ packetWriteTaskMaster.alert();
+ }
+ }
+ }
+ return true;
+ }
+
+ public int sendMessage(Message message){
+ if(this.messenger == null){return SEND_MESSAGE_ERROR_MESSENGER_NULL;}
+ if(message == null){return SEND_MESSAGE_ERROR_MESSAGE_NULL;}
+ try {
+ this.messenger.send(message);
+ return SEND_MESSAGE_SUCCESS;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ if(e instanceof DeadObjectException){
+ return SEND_MESSAGE_ERROR_MESSENGER_DEAD_OBJECT;
+ }else{
+ return SEND_MESSAGE_ERROR_MESSENGER_GENERIC_EXCEPTION;
+ }
+ }
+ }
+
+ public void handleMessage(int flags, byte[] packet){
+ if(flags == TransportConstants.BYTES_TO_SEND_FLAG_LARGE_PACKET_START){
+ clearBuffer();
+ buffer = new ByteAraryMessageAssembler();
+ buffer.init();
+ }
+ if(buffer == null){
+ Log.e(TAG, "Unable to assemble message as buffer was null/not started");
+ }
+ if(!buffer.handleMessage(flags, packet)){ //If this returns false
+ Log.e(TAG, "Error handling bytes");
+ }
+ if(buffer.isFinished()){ //We are finished building the buffer so we should write the bytes out
+ byte[] bytes = buffer.getBytes();
+ if(queue!=null){
+ queue.add(new PacketWriteTask(bytes, 0, bytes.length,this.prioirtyForBuffingMessage));
+ if(packetWriteTaskMaster!=null){
+ packetWriteTaskMaster.alert();
+ }
+ }
+ buffer.close();
+ }
+ }
+
+ protected PacketWriteTask peekNextTask(){
+ if(queue !=null){
+ return queue.peek();
+ }
+ return null;
+ }
+
+ protected PacketWriteTask getNextTask(){
+ if(queue !=null){
+ return queue.poll();
+ }
+ return null;
+ }
+
+ /**
+ * This will inform the local app object that it was not picked to have the highest priority. This will allow the user to continue to perform interactions
+ * with the module and not be bogged down by large packet requests.
+ */
+ protected void notIt(){
+ if(queue!=null && queue.peek().priorityCoefficient>0){ //If this has any sort of priority coefficient we want to make it wait.
+ //Flag to wait
+ if(queueWaitHandler == null){
+ Log.e(TAG, "Unable to pause queue, handler was null");
+ }
+ if(queueWaitRunnable == null){
+ queueWaitRunnable = new Runnable(){
+
+ @Override
+ public void run() {
+ pauseQueue(false);
+ if(packetWriteTaskMaster!=null){
+ packetWriteTaskMaster.alert();
+ }
+
+ }
+
+ };
+ }
+ if(queuePaused){
+ queueWaitHandler.removeCallbacks(queueWaitRunnable);
+ }
+ pauseQueue(queueWaitHandler.postDelayed(queueWaitRunnable, PAUSE_TIME_FOR_QUEUE));
+ }
+ }
+ private void pauseQueue(boolean paused){
+ this.queuePaused = paused;
+ }
+ protected void clearBuffer(){
+ if(buffer!=null){
+ buffer.close();
+ buffer = null;
+ }
+ }
+
+ protected boolean setDeathNote(){
+ if(messenger!=null){
+ if(deathNote == null){
+ deathNote = new DeathRecipient(){
+ @Override
+ public void binderDied() {
+ Log.w(TAG, "Binder died");
+ removeAllSessionsForApp(RegisteredApp.this,true);
+ removeAppFromMap(RegisteredApp.this);
+ startClientPings();
+ }
+ };
+ }
+ try {
+ messenger.getBinder().linkToDeath(deathNote, 0);
+ return true;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+ return false;
+ }
+
+ protected boolean clearDeathNote(){
+ if(messenger!=null && messenger.getBinder()!=null && deathNote!=null){
+ return messenger.getBinder().unlinkToDeath(deathNote, 0);
+ }
+ return false;
+ }
+ }
+
+ /**
+ * A runnable task for writing out packets.
+ * @author Joey Grover
+ *
+ */
+ public class PacketWriteTask implements Runnable{
+ private static final long DELAY_CONSTANT = 500; //250ms
+ private static final long SIZE_CONSTANT = 1000; //1kb
+ private static final long PRIORITY_COEF_CONSTATNT = 500;
+ private static final int DELAY_COEF = 1;
+ private static final int SIZE_COEF = 1;
+
+ private byte[] bytesToWrite = null;
+ private int offset, size, priorityCoefficient;
+ private final long timestamp;
+ final Bundle receivedBundle;
+
+ public PacketWriteTask(byte[] bytes, int offset, int size, int priorityCoefficient){
+ timestamp = System.currentTimeMillis();
+ bytesToWrite = bytes;
+ this.offset = offset;
+ this.size = size;
+ this.priorityCoefficient = priorityCoefficient;
+ receivedBundle = null;
+ }
+
+ public PacketWriteTask(Bundle bundle){
+ this.receivedBundle = bundle;
+ timestamp = System.currentTimeMillis();
+ bytesToWrite = bundle.getByteArray(TransportConstants.BYTES_TO_SEND_EXTRA_NAME);
+ offset = bundle.getInt(TransportConstants.BYTES_TO_SEND_EXTRA_OFFSET, 0); //If nothing, start at the begining of the array
+ size = bundle.getInt(TransportConstants.BYTES_TO_SEND_EXTRA_COUNT, bytesToWrite.length); //In case there isn't anything just send the whole packet.
+ this.priorityCoefficient = bundle.getInt(TransportConstants.PACKET_PRIORITY_COEFFICIENT,0); Log.d(TAG, "packet priority coef: "+ this.priorityCoefficient);
+ }
+
+ @Override
+ public void run() {
+ if(receivedBundle!=null){
+ writeBytesToTransport(receivedBundle);
+ }else if(bytesToWrite !=null){
+ manuallyWriteBytes(bytesToWrite, offset, size);
+ }
+ }
+
+ private long getWeight(long currentTime){ //Time waiting - size - prioirty_coef
+ return ((((currentTime-timestamp) + DELAY_CONSTANT) * DELAY_COEF ) - ((size -SIZE_CONSTANT) * SIZE_COEF) - (priorityCoefficient * PRIORITY_COEF_CONSTATNT));
+ }
+ }
+
+ /**
+ * Extends thread to consume PacketWriteTasks in a priority queue fashion. It will attempt to look
+ * at all apps serial queue of tasks and compare them
+ * @author Joey Grover
+ *
+ */
+ private class PacketWriteTaskMaster extends Thread{
+ protected final Object QUEUE_LOCK = new Object();
+ private boolean isHalted = false, isWaiting = false;
+
+ public PacketWriteTaskMaster(){
+ this.setName("PacketWriteTaskMaster");
+ }
+
+ @Override
+ public void run() {
+ while(!isHalted){
+ try{
+ PacketWriteTask task = null;
+ synchronized(QUEUE_LOCK){
+ task = getNextTask();
+ if(task != null){
+ task.run();
+ }else{
+ isWaiting = true;
+ QUEUE_LOCK.wait();
+ isWaiting = false;
+ }
+ }
+ }catch(InterruptedException e){
+ break;
+ }
+ }
+ }
+
+ private void alert(){
+ if(isWaiting){
+ synchronized(QUEUE_LOCK){
+ QUEUE_LOCK.notify();
+ }
+ }
+ }
+
+ private void close(){
+ this.isHalted = true;
+ }
+ }
+
+ /**
+ * Custom queue to prioritize packet write tasks based on their priority coefficient.<br> The queue is a doubly linked list.<br><br>
+ * When a tasks is added to the queue, it will be evaluated using it's priority coefficient. If the coefficient is greater than 0, it will simply
+ * be placed at the end of the queue. If the coefficient is equal to 0, the queue will begin to iterate at the head and work it's way back. Once it is found that the current
+ * tasks has a priority coefficient greater than 0, it will be placed right before that task. The idea is to keep a semi-serial queue but creates a priority that allows urgent
+ * tasks such as UI related to skip near the front. However, it is assumed those tasks of higher priority should also be handled in a serial fashion.
+ *
+ * @author Joey Grover
+ *
+ */
+ private class PacketWriteTaskBlockingQueue{
+ final class Node<E> {
+ E item;
+ Node<E> prev;
+ Node<E> next;
+ Node(E item, Node<E> previous, Node<E> next) {
+ this.item = item;
+ this.prev = previous;
+ this.next = next;
+ }
+ }
+
+ private Node<PacketWriteTask> head;
+ private Node<PacketWriteTask> tail;
+
+ /**
+ * This will take the given task and insert it at the tail of the queue
+ * @param task the task to be inserted at the tail of the queue
+ */
+ private void insertAtTail(PacketWriteTask task){
+ if (task == null){
+ throw new NullPointerException();
+ }
+ Node<PacketWriteTask> oldTail = tail;
+ Node<PacketWriteTask> newTail = new Node<PacketWriteTask>(task, oldTail, null);
+ tail = newTail;
+ if (head == null){
+ head = newTail;
+ }else{
+ oldTail.next = newTail;
+ }
+
+ }
+
+ /**
+ * This will take the given task and insert it at the head of the queue
+ * @param task the task to be inserted at the head of the queue
+ */
+ private void insertAtHead(PacketWriteTask task){
+ if (task == null){
+ throw new NullPointerException();
+ }
+ Node<PacketWriteTask> oldHead = head;
+ Node<PacketWriteTask> newHead = new Node<PacketWriteTask>(task, null, oldHead);
+ head = newHead;
+ if (tail == null){
+ tail = newHead;
+ }else{
+ if(oldHead!=null){
+ oldHead.prev = newHead;
+ }
+ }
+ }
+
+ /**
+ * Insert the task in the queue where it belongs
+ * @param task
+ */
+ public void add(PacketWriteTask task){
+ synchronized(this){
+ if (task == null){
+ throw new NullPointerException();
+ }
+
+ //If we currently don't have anything in our queue
+ if(head == null || tail == null){
+ Node<PacketWriteTask> taskNode = new Node<PacketWriteTask>(task, head, tail);
+ head = taskNode;
+ tail = taskNode;
+ return;
+ }else if(task.priorityCoefficient>0){ //If the task is already a not high priority task, we just need to insert it at the tail
+ insertAtTail(task);
+ return;
+ }else if(head.item.priorityCoefficient>0){ //If the head task is already a not high priority task, we just need to insert at head
+ insertAtHead(task);
+ return;
+ }else{
+ if(tail!=null && tail.item.priorityCoefficient==0){ //Saves us from going through the entire list if all of these tasks are priority coef == 0
+ insertAtTail(task);
+ return;
+ }
+ Node<PacketWriteTask> currentPlace = head;
+ while(true){
+ if(currentPlace.item.priorityCoefficient==0){
+ if(currentPlace.next==null){
+ //We've reached the end of the list
+ insertAtTail(task);
+ return;
+ }else{
+ currentPlace = currentPlace.next;
+ continue;
+ }
+ }else{
+ //We've found where this task should be inserted
+ Node<PacketWriteTask> previous = currentPlace.prev;
+ Node<PacketWriteTask> taskNode = new Node<PacketWriteTask>(task, previous, currentPlace);
+ previous.next = taskNode;
+ currentPlace.prev = taskNode;
+ return;
+
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Peek at the current head of the queue
+ * @return the task at the head of the queue but does not remove it from the queue
+ */
+ public PacketWriteTask peek(){
+ synchronized(this){
+ if(head == null){
+ return null;
+ }else{
+ return head.item;
+ }
+ }
+ }
+
+ /**
+ * Remove the head of the queue
+ * @return the old head of the queue
+ */
+ public PacketWriteTask poll(){
+ synchronized(this){
+ if(head == null){
+ return null;
+ }else{
+ Node<PacketWriteTask> retValNode = head;
+ Node<PacketWriteTask> newHead = head.next;
+ if(newHead == null){
+ tail = null;
+ }
+ head = newHead;
+
+ return retValNode.item;
+ }
+ }
+ }
+
+ /**
+ * Currently only clears the head and the tail of the queue.
+ */
+ public void clear(){
+ //Should probably go through the linked list and clear elements, but gc should clear them out automatically.
+ head = null;
+ tail = null;
+ }
+ }
+
+
+}
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/SdlTransport.java b/sdl_android_lib/src/com/smartdevicelink/transport/SdlTransport.java
index 1f76b0af4..27c87bcef 100644
--- a/sdl_android_lib/src/com/smartdevicelink/transport/SdlTransport.java
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/SdlTransport.java
@@ -1,6 +1,7 @@
package com.smartdevicelink.transport;
import com.smartdevicelink.exception.SdlException;
+import com.smartdevicelink.protocol.SdlPacket;
import com.smartdevicelink.trace.SdlTrace;
import com.smartdevicelink.trace.enums.InterfaceActivityDirection;
import com.smartdevicelink.transport.enums.TransportType;
@@ -30,15 +31,15 @@ public abstract class SdlTransport {
// This method is called by the subclass to indicate that data has arrived from
// the transport.
- protected void handleReceivedBytes(byte[] receivedBytes, int receivedBytesLength) {
+ protected void handleReceivedPacket(SdlPacket packet) {
try {
// Trace received data
- if (receivedBytesLength > 0) {
+ if (packet!=null) {
// Send transport data to the siphon server
- SiphonServer.sendBytesFromSDL(receivedBytes, 0, receivedBytesLength);
- SdlTrace.logTransportEvent("", null, InterfaceActivityDirection.Receive, receivedBytes, receivedBytesLength, SDL_LIB_TRACE_KEY);
+ //FIXME SiphonServer.sendBytesFromSDL(receivedBytes, 0, receivedBytesLength);
+ //SdlTrace.logTransportEvent("", null, InterfaceActivityDirection.Receive, receivedBytes, receivedBytesLength, SDL_LIB_TRACE_KEY);
- _transportListener.onTransportBytesReceived(receivedBytes, receivedBytesLength);
+ _transportListener.onTransportPacketReceived(packet);
} // end-if
} catch (Exception excp) {
DebugTool.logError(FailurePropagating_Msg + "handleBytesFromTransport: " + excp.toString(), excp);
@@ -49,25 +50,25 @@ public abstract class SdlTransport {
// This method must be implemented by transport subclass, and is called by this
// base class to actually send an array of bytes out over the transport. This
// method is meant to only be callable within the class hierarchy.
- protected abstract boolean sendBytesOverTransport(byte[] msgBytes, int offset, int length);
+ protected abstract boolean sendBytesOverTransport(SdlPacket packet);
// This method is called by whomever has reference to transport to have bytes
// sent out over transport.
- public boolean sendBytes(byte[] message) {
+ /* public boolean sendBytes(byte[] message) {
return sendBytes(message, 0, message.length);
- } // end-method
+ }*/ // end-method
// This method is called by whomever has reference to transport to have bytes
// sent out over transport.
- public boolean sendBytes(byte[] message, int offset, int length) {
+ public boolean sendBytes(SdlPacket packet) {
boolean bytesWereSent = false;
synchronized (_sendLockObj) {
- bytesWereSent = sendBytesOverTransport(message, offset, length);
+ bytesWereSent = sendBytesOverTransport(packet);//message, offset, length);
} // end-lock
// Send transport data to the siphon server
- SiphonServer.sendBytesFromAPP(message, offset, length);
+ //FIXME SiphonServer.sendBytesFromAPP(message, offset, length);
- SdlTrace.logTransportEvent("", null, InterfaceActivityDirection.Transmit, message, offset, length, SDL_LIB_TRACE_KEY);
+ //FIXME SdlTrace.logTransportEvent("", null, InterfaceActivityDirection.Transmit, message, offset, length, SDL_LIB_TRACE_KEY);
return bytesWereSent;
} // end-method
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/TCPTransport.java b/sdl_android_lib/src/com/smartdevicelink/transport/TCPTransport.java
index 69500bf6b..9ba789ef8 100644
--- a/sdl_android_lib/src/com/smartdevicelink/transport/TCPTransport.java
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/TCPTransport.java
@@ -1,17 +1,18 @@
package com.smartdevicelink.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
import android.util.Log;
import com.smartdevicelink.exception.SdlException;
import com.smartdevicelink.exception.SdlExceptionCause;
+import com.smartdevicelink.protocol.SdlPacket;
import com.smartdevicelink.transport.enums.TransportType;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-
/**
* General comments:
*
@@ -107,10 +108,11 @@ public class TCPTransport extends SdlTransport {
* @return True if data was sent successfully, False otherwise
*/
@Override
- protected boolean sendBytesOverTransport(byte[] msgBytes, int offset, int length) {
+ protected boolean sendBytesOverTransport(SdlPacket packet) {
TCPTransportState currentState = getCurrentState();
+ byte[] msgBytes = packet.constructPacket();
logInfo(String.format("TCPTransport: sendBytesOverTransport requested. Size: %d, Offset: %d, Length: %d, Current state is: %s"
- , msgBytes.length, offset, length, currentState.name()));
+ , msgBytes.length, 0, msgBytes.length, currentState.name()));
boolean bResult = false;
@@ -118,7 +120,7 @@ public class TCPTransport extends SdlTransport {
if (mOutputStream != null) {
logInfo("TCPTransport: sendBytesOverTransport request accepted. Trying to send data");
try {
- mOutputStream.write(msgBytes, offset, length);
+ mOutputStream.write(msgBytes, 0, msgBytes.length);
bResult = true;
logInfo("TCPTransport.sendBytesOverTransport: successfully send data");
} catch (IOException e) {
@@ -288,7 +290,10 @@ public class TCPTransport extends SdlTransport {
* Internal class that represents separate thread, that does actual work, related to connecting/reading/writing data
*/
private class TCPTransportThread extends Thread {
-
+ SdlPsm psm;
+ public TCPTransportThread(){
+ psm = new SdlPsm();
+ }
/**
* Represents current thread state - halted or not. This flag is used to change internal behavior depending
* on current state.
@@ -313,7 +318,7 @@ public class TCPTransport extends SdlTransport {
private boolean connect() {
boolean bConnected;
int remainingRetry = RECONNECT_RETRY_COUNT;
-
+
synchronized (TCPTransport.this) {
do {
try {
@@ -359,7 +364,7 @@ public class TCPTransport extends SdlTransport {
@Override
public void run() {
logInfo("TCPTransport.run: transport thread created. Starting connect stage");
-
+ psm.reset();
while(!isHalted) {
setCurrentState(TCPTransportState.CONNECTING);
if(!connect()){
@@ -377,13 +382,13 @@ public class TCPTransport extends SdlTransport {
handleTransportConnected();
}
- byte[] buffer = new byte[READ_BUFFER_SIZE];
-
+ byte input;
+ boolean stateProgress = false;
while (!isHalted) {
logInfo("TCPTransport.run: Waiting for data...");
- int bytesRead;
try {
- bytesRead = mInputStream.read(buffer);
+ input = (byte) mInputStream.read();
+ //bytesRead = mInputStream.read(buffer);
} catch (IOException e) {
internalHandleStreamReadError();
break;
@@ -397,17 +402,25 @@ public class TCPTransport extends SdlTransport {
}
logInfo("TCPTransport.run: Got new data");
- if (-1 == bytesRead) {
- internalHandleTCPDisconnect();
- break;
- } else if (0 == bytesRead) {
- logInfo("TCPTransport.run: Received zero bytes");
- } else {
- logInfo(String.format("TCPTransport.run: Received %d bytes", bytesRead));
- synchronized (TCPTransport.this) {
- handleReceivedBytes(buffer, bytesRead);
+ // Send the response of what we received
+ stateProgress = psm.handleByte(input);
+ if(!stateProgress){//We are trying to weed through the bad packet info until we get something
+
+ //Log.w(TAG, "Packet State Machine did not move forward from state - "+ psm.getState()+". PSM being Reset.");
+ psm.reset();
}
- }
+
+ if(psm.getState() == SdlPsm.FINISHED_STATE)
+ {
+ synchronized (TCPTransport.this) {
+ //Log.d(TAG, "Packet formed, sending off");
+ handleReceivedPacket((SdlPacket)psm.getFormedPacket());
+ }
+ //We put a trace statement in the message read so we can avoid all the extra bytes
+ psm.reset();
+ }
+ //FIXME logInfo(String.format("TCPTransport.run: Received %d bytes", bytesRead));
+
}
}
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/TransportBroker.java b/sdl_android_lib/src/com/smartdevicelink/transport/TransportBroker.java
new file mode 100644
index 000000000..613c857d9
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/TransportBroker.java
@@ -0,0 +1,597 @@
+package com.smartdevicelink.transport;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningServiceInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.TransactionTooLargeException;
+import android.util.Log;
+
+import com.smartdevicelink.protocol.SdlPacket;
+import com.smartdevicelink.transport.enums.TransportType;
+import com.smartdevicelink.transport.utl.ByteAraryMessageAssembler;
+import com.smartdevicelink.transport.utl.ByteArrayMessageSpliter;
+
+
+public class TransportBroker {
+
+ private static final String TAG = "SdlTransportBroker";
+ private final String WHERE_TO_REPLY_PREFIX = "com.sdl.android.";
+ private Long appId = null;
+ private String whereToReply = null;
+ private Context currentContext = null;
+
+ private final Object INIT_LOCK = new Object();
+
+ private TransportType queuedOnTransportConnect = null;
+
+ Messenger routerServiceMessenger = null;
+ final Messenger clientMessenger = new Messenger(new ClientHandler());
+
+ boolean isBound = false, registeredWithRouterService = false;
+ private String routerPackage = null, routerClassName = null;
+ private ComponentName routerService = null;
+
+
+ private SdlPacket bufferedPacket = null;
+ private ByteAraryMessageAssembler bufferedPayloadAssembler = null;
+
+ private ServiceConnection routerConnection;
+
+ private void initRouterConnection(){
+ routerConnection= new ServiceConnection() {
+
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Log.d(TAG, "Bound to service " + className.toString());
+ routerServiceMessenger = new Messenger(service);
+ isBound = true;
+ //So we just established our connection
+ //Register with router service
+ sendRegistrationMessage();
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ Log.d(TAG, "UN-Bound from service " + className.getClassName());
+ routerServiceMessenger = null;
+ registeredWithRouterService = false;
+ isBound = false;
+ onHardwareDisconnected(null);
+ }
+ };
+ }
+
+ protected synchronized boolean sendMessageToRouterService(Message message){
+ return sendMessageToRouterService(message,0);
+ }
+
+ protected synchronized boolean sendMessageToRouterService(Message message, int retryCount){
+ if(message == null){
+ Log.w(TAG, "Attempted to send null message");
+ return false;
+ }
+ //Log.i(TAG, "Attempting to send message type - " + message.what);
+ if(isBound && routerServiceMessenger !=null){
+ if(registeredWithRouterService
+ || message.what == TransportConstants.ROUTER_REGISTER_CLIENT){ //We can send a message if we are registered or are attempting to register
+ try {
+ routerServiceMessenger.send(message);
+ return true;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ //Let's check to see if we should retry
+ if(e instanceof TransactionTooLargeException
+ || (retryCount<5 && routerServiceMessenger.getBinder().isBinderAlive() && routerServiceMessenger.getBinder().pingBinder())){ //We probably just failed on a small transaction =\
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e1) {
+ e1.printStackTrace();
+ }
+ return sendMessageToRouterService(message, retryCount++);
+ }else{
+ //DeadObject, time to kill our connection
+ Log.d(TAG, "Dead object while attempting to send packet");
+ routerServiceMessenger = null;
+ registeredWithRouterService = false;
+ isBound = false;
+ onHardwareDisconnected(null);
+ return false;
+ }
+ }
+ }else{
+ Log.e(TAG, "Unable to send message to router service. Not registered.");
+ return false;
+ }
+ }else{
+ Log.e(TAG, "Unable to send message to router service. Not bound.");
+ return false;
+ }
+ }
+
+
+ /**
+ * Handler of incoming messages from service.
+ */
+ @SuppressLint("HandlerLeak")
+ class ClientHandler extends Handler {
+ ClassLoader loader = getClass().getClassLoader();
+ @Override
+ public void handleMessage(Message msg) {
+
+ Bundle bundle = msg.getData();
+
+ if(bundle!=null){
+ bundle.setClassLoader(loader);
+ }
+ //Log.d(TAG, "Bundle: " + bundle.toString());
+ /* DO NOT MOVE
+ * This needs to be first to make sure we already know if we are attempting to enter legacy mode or not
+ */
+ if(bundle !=null
+ && bundle.containsKey(TransportConstants.ENABLE_LEGACY_MODE_EXTRA)){
+ boolean enableLegacy = bundle.getBoolean(TransportConstants.ENABLE_LEGACY_MODE_EXTRA, false);
+ Log.d(TAG, "Setting legacy mode: " +enableLegacy );
+ enableLegacyMode(enableLegacy);
+ }
+
+ //Find out what message we have and what to do with it
+ switch (msg.what) {
+ case TransportConstants.ROUTER_REGISTER_CLIENT_RESPONSE:
+ switch(msg.arg1){
+ case TransportConstants.REGISTRATION_RESPONSE_SUCESS:
+ // yay! we have been registered. Now what?
+ registeredWithRouterService = true;
+ if(bundle!=null && bundle.containsKey(TransportConstants.CONNECTED_DEVICE_STRING_EXTRA_NAME)){
+ //Keep track if we actually get this
+ }
+ if(queuedOnTransportConnect!=null){
+ onHardwareConnected(queuedOnTransportConnect);
+ queuedOnTransportConnect = null;
+ }else if(SdlBroadcastReceiver.isTransportConnected(getContext())){
+ onHardwareConnected(null); //FIXME to include type
+ }
+ break;
+ case TransportConstants.REGISTRATION_RESPONSE_DENIED_LEGACY_MODE_ENABLED:
+ Log.d(TAG, "Denied registration because router is in legacy mode" );
+ registeredWithRouterService = false;
+ enableLegacyMode(true);
+ //We call this so we can start the process of legacy connection
+ //onHardwareDisconnected(TransportType.BLUETOOTH);
+ onLegacyModeEnabled();
+ break;
+ default:
+ registeredWithRouterService = false;
+ Log.w(TAG, "Registration denied from router service. Reason - " + msg.arg1);
+ break;
+ };
+
+
+ break;
+ case TransportConstants.ROUTER_UNREGISTER_CLIENT_RESPONSE:
+ if(msg.arg1==TransportConstants.UNREGISTRATION_RESPONSE_SUCESS){
+ // We've been unregistered. Now what?
+
+
+ }else{ //We were denied our unregister request to the router service, let's see why
+ Log.w(TAG, "Unregister request denied from router service. Reason - " + msg.arg1);
+ //Do we care?
+ }
+
+ break;
+ case TransportConstants.ROUTER_RECEIVED_PACKET:
+ //So the intent has a packet with it. PEFRECT! Let's send it through the library
+ int flags = bundle.getInt(TransportConstants.BYTES_TO_SEND_FLAGS, TransportConstants.BYTES_TO_SEND_FLAG_NONE);
+
+ if(bundle.containsKey(TransportConstants.FORMED_PACKET_EXTRA_NAME)){
+ Parcelable packet = bundle.getParcelable(TransportConstants.FORMED_PACKET_EXTRA_NAME);
+
+ if(flags == TransportConstants.BYTES_TO_SEND_FLAG_NONE){
+ if(packet!=null){ Log.i(TAG, "received packet to process "+ packet.toString());
+ onPacketReceived(packet);
+ }else{
+ Log.w(TAG, "Received null packet from router service, not passing along");
+ }
+ }else if(flags == TransportConstants.BYTES_TO_SEND_FLAG_SDL_PACKET_INCLUDED){
+ Log.i(TAG, "Starting a buffered split packet");
+ bufferedPacket = (SdlPacket) packet;
+ if(bufferedPayloadAssembler !=null){
+ bufferedPayloadAssembler.close();
+ bufferedPayloadAssembler = null;
+ }
+
+ bufferedPayloadAssembler = new ByteAraryMessageAssembler();
+ bufferedPayloadAssembler.init();
+ }
+ }else if(bundle.containsKey(TransportConstants.BYTES_TO_SEND_EXTRA_NAME)){
+ //This should contain the payload
+ if(bufferedPayloadAssembler!=null){
+ byte[] chunk = bundle.getByteArray(TransportConstants.BYTES_TO_SEND_EXTRA_NAME);
+ if(!bufferedPayloadAssembler.handleMessage(flags, chunk)){
+ //If there was a problem
+ Log.e(TAG, "Error handling bytes for split packet");
+ }
+ if(bufferedPayloadAssembler.isFinished()){
+ bufferedPacket.setPayload(bufferedPayloadAssembler.getBytes());
+
+ bufferedPayloadAssembler.close();
+ bufferedPayloadAssembler = null;
+ Log.i(TAG, "Split packet finished from router service = " + bufferedPacket.toString());
+ onPacketReceived(bufferedPacket);
+ bufferedPacket = null;
+ }
+ }
+ //}
+ //}
+ }else{
+ Log.w(TAG, "Flase positive packet reception");
+ }
+ break;
+ case TransportConstants.HARDWARE_CONNECTION_EVENT:
+ if(bundle.containsKey(TransportConstants.HARDWARE_DISCONNECTED)){
+ //We should shut down, so call
+ Log.d(TAG, "Hardware disconnected");
+ if(isLegacyModeEnabled()){
+ onLegacyModeEnabled();
+ }else{
+ onHardwareDisconnected(TransportType.valueOf(bundle.getString(TransportConstants.HARDWARE_DISCONNECTED)));
+ }
+ break;
+ }
+
+ if(bundle.containsKey(TransportConstants.HARDWARE_CONNECTED)){
+ onHardwareConnected(TransportType.valueOf(bundle.getString(TransportConstants.HARDWARE_CONNECTED)));
+ break;
+ }
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+
+ }
+ }
+
+
+
+ /***************************************************************************************************************************************
+ *********************************************** Life Cycle **************************************************************
+ ****************************************************************************************************************************************/
+
+
+ @SuppressLint("SimpleDateFormat")
+ public TransportBroker(Context context, String appId, ComponentName service){
+ synchronized(INIT_LOCK){
+ initRouterConnection();
+ //So the user should have set the AppId, lets define where the intents need to be sent
+ SimpleDateFormat s = new SimpleDateFormat("hhmmssss"); //So we have a time stamp of the event
+ String timeStamp = s.format(new Date(System.currentTimeMillis()));
+ if(whereToReply==null){
+ if(appId==null){ //This should really just throw an error
+ whereToReply = WHERE_TO_REPLY_PREFIX + "."+ timeStamp;
+ }else{
+ whereToReply = WHERE_TO_REPLY_PREFIX + appId +"."+ timeStamp;
+ }
+ }
+ this.appId = Long.valueOf(appId.concat(timeStamp));
+ queuedOnTransportConnect = null;
+ currentContext = context;
+ //Log.d(TAG, "Registering our reply receiver: " + whereToReply);
+ this.routerService = service;
+ }
+ }
+
+ /**
+ * This beings the initial connection with the router service.
+ */
+ public boolean start(){
+ Log.d(TAG, "Starting up transport broker for " + whereToReply);
+ synchronized(INIT_LOCK){
+ if(currentContext==null){
+ throw new IllegalStateException("This instance can't be started since it's local reference of context is null. Ensure when suppling a context to the TransportBroker that it is valid");
+ }
+ if(routerConnection==null){
+ initRouterConnection();
+ }
+ //Log.d(TAG, "Registering our reply receiver: " + whereToReply);
+ if(!isBound){
+ return registerWithRouterService();
+ }else{
+ return false;
+ }
+ }
+ }
+
+ public void resetSession(){
+ Log.d(TAG, "RESETING transport broker for " + whereToReply);
+ synchronized(INIT_LOCK){
+ unregisterWithRouterService();
+ routerServiceMessenger = null;
+ queuedOnTransportConnect = null;
+ unBindFromRouterService();
+ }
+ }
+ /**
+ * This method will end our communication with the router service.
+ */
+ public void stop(){
+ Log.d(TAG, "STOPPING transport broker for " + whereToReply);
+ synchronized(INIT_LOCK){
+ unregisterWithRouterService();
+ unBindFromRouterService();
+ routerServiceMessenger = null;
+ queuedOnTransportConnect = null;
+ currentContext = null;
+
+ }
+ }
+
+ private void unBindFromRouterService(){
+ try{
+ if(getContext()!=null && routerConnection!=null){
+ getContext().unbindService(routerConnection);
+ }else{
+ Log.w(TAG, "Unable to unbind from router service, context was null");
+ }
+
+ }catch(IllegalArgumentException e){
+ //This is ok
+ }
+ }
+ /***************************************************************************************************************************************
+ *********************************************** Event Callbacks **************************************************************
+ ****************************************************************************************************************************************/
+
+
+ public void onServiceUnregsiteredFromRouterService(int unregisterCode){
+ queuedOnTransportConnect = null;
+ }
+
+ public void onHardwareDisconnected(TransportType type){
+ synchronized(INIT_LOCK){Log.d(TAG, "onHardwareDisconnect");
+ unBindFromRouterService();
+ routerServiceMessenger = null;
+ routerConnection = null;
+ queuedOnTransportConnect = null;
+ }
+ }
+
+ public boolean onHardwareConnected(TransportType type){
+ synchronized(INIT_LOCK){
+ if(routerServiceMessenger==null){
+ queuedOnTransportConnect = type;
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+ public void onPacketReceived(Parcelable packet){
+
+ }
+
+ public void onLegacyModeEnabled(){
+
+ }
+
+ /**
+ * We want to check to see if the Router service is already up and running
+ * @param context
+ * @return
+ */
+ private boolean isRouterServiceRunning(Context context){
+ Log.d(TAG,whereToReply + " checking if a bluetooth service is running");
+ if(context==null){
+
+ return false;
+ }
+ ActivityManager manager = (ActivityManager) context.getSystemService("activity");
+ for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
+ //We will check to see if it contains this name, should be pretty specific
+ if ((service.service.getClassName()).toLowerCase(Locale.US).contains(SdlBroadcastReceiver.SDL_ROUTER_SERVICE_CLASS_NAME)) {
+ this.routerClassName = service.service.getClassName();
+ this.routerPackage = service.service.getPackageName();
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ public boolean sendPacketToRouterService(SdlPacket packet){ //We use ints because that is all that is supported by the outputstream class
+ //Log.d(TAG,whereToReply + "Sending packet to router service");
+
+ if(routerServiceMessenger==null){
+ Log.d(TAG,whereToReply + " tried to send packet, but no where to send");
+ return false;
+ }
+ if(packet == null
+ //|| offset<0
+ //|| count<0
+ ){//|| count>(bytes.length-offset)){
+ Log.w(TAG,whereToReply + "incorrect params supplied");
+ return false;
+ }
+ byte[] bytes = packet.constructPacket();
+ if(bytes.length<ByteArrayMessageSpliter.MAX_BINDER_SIZE){//Determine if this is under the packet length.
+ Message message = Message.obtain(); //Do we need to always obtain new? or can we just swap bundles?
+ message.what = TransportConstants.ROUTER_SEND_PACKET;
+ Bundle bundle = new Bundle();
+ bundle.putLong(TransportConstants.APP_ID_EXTRA, appId);
+ bundle.putByteArray(TransportConstants.BYTES_TO_SEND_EXTRA_NAME, bytes); //Do we just change this to the args and objs
+ bundle.putInt(TransportConstants.BYTES_TO_SEND_EXTRA_OFFSET, 0);
+ bundle.putInt(TransportConstants.BYTES_TO_SEND_EXTRA_COUNT, bytes.length);
+ bundle.putInt(TransportConstants.BYTES_TO_SEND_FLAGS, TransportConstants.BYTES_TO_SEND_FLAG_NONE);
+ bundle.putInt(TransportConstants.PACKET_PRIORITY_COEFFICIENT, packet.getPrioirtyCoefficient());
+ message.setData(bundle);
+
+ sendMessageToRouterService(message);
+ return true;
+ }else{ //Message is too big for IPC transaction
+ Log.w(TAG, "Message too big for single IPC transaction. Breaking apart. Size - " + bytes.length);
+ ByteArrayMessageSpliter splitter = new ByteArrayMessageSpliter(appId,TransportConstants.ROUTER_SEND_PACKET,bytes,packet.getPrioirtyCoefficient() );
+ while(splitter.isActive()){
+ sendMessageToRouterService(splitter.nextMessage());
+ }
+ return splitter.close();
+ }
+
+ }
+
+ /**
+ * This registers this service with the router service
+ */
+ private boolean registerWithRouterService(){
+ if(getContext()==null){
+ Log.e(TAG, "Context set to null, failing out");
+ return false;
+ }
+
+ if(routerServiceMessenger!=null){
+ Log.w(TAG, "Already registered with router service");
+ return false;
+ }
+ //Make sure we know where to bind to
+ if(this.routerService==null){
+ if(!isRouterServiceRunning(getContext())){//We should be able to ignore this case because of the validation now
+ Log.d(TAG,whereToReply + " found no router service. Shutting down.");
+ this.onHardwareDisconnected(null);
+ return false;
+ }
+ }else{//We were already told where to bind. This should be the case.
+ this.routerClassName = this.routerService.getClassName();
+ this.routerPackage = this.routerService.getPackageName();
+ }
+
+ if(!sendBindingIntent()){
+ Log.e(TAG, "Something went wrong while trying to bind with the router service.");
+ return false;
+ }
+ return true;
+
+ }
+
+ @SuppressLint("InlinedApi")
+ private boolean sendBindingIntent(){
+ if(this.routerPackage !=null && this.routerClassName !=null){
+ Log.d(TAG, "Sending bind request to " + this.routerPackage + " - " + this.routerClassName);
+ Intent bindingIntent = new Intent();
+ bindingIntent.setClassName(this.routerPackage, this.routerClassName);//This sets an explicit intent
+ //Quickly make sure it's just up and running
+ getContext().startService(bindingIntent);
+ bindingIntent.setAction( TransportConstants.BIND_REQUEST_TYPE_CLIENT);
+ return getContext().bindService(bindingIntent, routerConnection, Context.BIND_AUTO_CREATE);
+ }else{
+ return false;
+ }
+ }
+
+ private void sendRegistrationMessage(){
+ Message msg = Message.obtain();
+ msg.what = TransportConstants.ROUTER_REGISTER_CLIENT;
+ msg.replyTo = this.clientMessenger;
+ Bundle bundle = new Bundle();
+ bundle.putLong(TransportConstants.APP_ID_EXTRA, appId);
+ msg.setData(bundle);
+ sendMessageToRouterService(msg);
+ }
+
+ private void unregisterWithRouterService(){
+ Log.i(TAG, "Attempting to unregister with Sdl Router Service");
+ if(isBound && routerServiceMessenger!=null){
+ Message msg = Message.obtain();
+ msg.what = TransportConstants.ROUTER_UNREGISTER_CLIENT;
+ msg.replyTo = this.clientMessenger; //Including this in case this app isn't actually registered with the router service
+ Bundle bundle = new Bundle();
+ bundle.putLong(TransportConstants.APP_ID_EXTRA, appId);
+ msg.setData(bundle);
+ sendMessageToRouterService(msg);
+ }else{
+ Log.w(TAG, "Unable to unregister, not bound to router service");
+ }
+
+ routerServiceMessenger = null;
+ }
+
+
+
+ /**
+ * Since it doesn't always make sense to add another service, use this method to get
+ * the appropriate context that the rest of this class is using.
+ * @return The currently used context for this class
+ */
+ private Context getContext(){
+ return currentContext;
+ }
+
+ /***************************************************************************************************************************************
+ *********************************************** LEGACY *******************************************************************************
+ ****************************************************************************************************************************************/
+ /*
+ * Due to old implementations of SDL/Applink, old versions can't support multiple sessions per RFCOMM channel.
+ * This causes a race condition in the router service where only the first app registered will be able to communicate with the
+ * head unit. With this additional code, the router service will:
+ * 1) Acknowledge it's connected to an old system
+ * 2) d/c its bluetooth
+ * 3) Send a message to all clients connected that legacy mode is enabled
+ * 4) Each client spins up their own bluetooth RFCOMM listening channel
+ * 5) Head unit will need to query apps again
+ * 6) HU should then connect to each app by their own RFCOMM channel bypassing the router service
+ * 7) When the phone is D/C from the head unit the router service will reset and tell clients legacy mode is now off
+ */
+
+ private static boolean legacyModeEnabled = false;
+ private static Object LEGACY_LOCK = new Object();
+
+ protected void enableLegacyMode(boolean enable){
+ synchronized(LEGACY_LOCK){
+ legacyModeEnabled = enable;
+ }
+ }
+ protected static boolean isLegacyModeEnabled(){
+ synchronized(LEGACY_LOCK){
+ return legacyModeEnabled;
+ }
+
+ }
+
+ /***************************************************************************************************************************************
+ **************************************************** LEGACY END ***********************************************************************
+ ****************************************************************************************************************************************/
+
+ /**
+ * Use this method to let the router service know that you are requesting a new session from the head unit.
+ */
+ public void requestNewSession(){
+ Message msg = Message.obtain();
+ msg.what = TransportConstants.ROUTER_REQUEST_NEW_SESSION;
+ msg.replyTo = this.clientMessenger; //Including this in case this app isn't actually registered with the router service
+ Bundle bundle = new Bundle();
+ bundle.putLong(TransportConstants.APP_ID_EXTRA, appId);
+ msg.setData(bundle);
+ this.sendMessageToRouterService(msg);
+ }
+
+ public void removeSession(long sessionId){
+ Message msg = Message.obtain();
+ msg.what = TransportConstants.ROUTER_REMOVE_SESSION;
+ msg.replyTo = this.clientMessenger; //Including this in case this app isn't actually registered with the router service
+ Bundle bundle = new Bundle();
+ bundle.putLong(TransportConstants.APP_ID_EXTRA, appId);
+ bundle.putLong(TransportConstants.SESSION_ID_EXTRA, sessionId);
+ msg.setData(bundle);
+ this.sendMessageToRouterService(msg);
+ }
+}
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/TransportConstants.java b/sdl_android_lib/src/com/smartdevicelink/transport/TransportConstants.java
new file mode 100644
index 000000000..00b7e54b0
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/TransportConstants.java
@@ -0,0 +1,196 @@
+package com.smartdevicelink.transport;
+
+
+/**
+ * These constants are shared between the router service and the SDL base service.
+ * They are defined as strings/actions/values that both of them can understand.
+ * Attempting to use standard HTTP error codes as definitions.
+ * @author Joey Grover
+ *
+ */
+public class TransportConstants {
+ public static final String START_ROUTER_SERVICE_ACTION ="sdl.router.startservice";
+
+ public static final String UNREGISTER_WITH_ROUTER_ACTION = "com.sdl.android.unregister";
+ public static final String SEND_PACKET_ACTION = "com.sdl.android.sendpacket";
+ public static final String SEND__GLOBAL_PACKET_ACTION = "com.sdl.android.sendglobalpacket";
+
+ public static final String BIND_LOCATION_PACKAGE_NAME_EXTRA = "BIND_LOCATION_PACKAGE_NAME_EXTRA";
+ public static final String BIND_LOCATION_CLASS_NAME_EXTRA = "BIND_LOCATION_CLASS_NAME_EXTRA";
+
+ public static final String ALT_TRANSPORT_RECEIVER = "com.sdl.android.alttransport";
+ public static final String ALT_TRANSPORT_CONNECTION_STATUS_EXTRA = "connection_status";
+ public static final int ALT_TRANSPORT_DISCONNECTED = 0;
+ public static final int ALT_TRANSPORT_CONNECTED = 1;
+ public static final String ALT_TRANSPORT_READ = "read";//Read from the alt transport, goes to the app
+ public static final String ALT_TRANSPORT_WRITE = "write";//Write to the alt transport, comes from the app
+ public static final String ALT_TRANSPORT_ADDRESS_EXTRA = "altTransportAddress";
+
+ public static final String START_ROUTER_SERVICE_SDL_ENABLED_EXTRA = "sdl_enabled";
+ public static final String START_ROUTER_SERVICE_SDL_ENABLED_APP_PACKAGE = "package_name";
+ public static final String START_ROUTER_SERVICE_SDL_ENABLED_CMP_NAME = "component_name";
+ public static final String START_ROUTER_SERVICE_SDL_ENABLED_PING = "ping";
+ public static final String FORCE_TRANSPORT_CONNECTED = "force_connect"; //This is legacy, do not refactor this.
+
+ public static final String REPLY_TO_INTENT_EXTRA = "ReplyAddress";
+ public static final String CONNECT_AS_CLIENT_BOOLEAN_EXTRA = "connectAsClient";
+ public static final String PACKAGE_NAME_STRING = "package.name";
+ public static final String APP_ID_EXTRA = "app.id";
+ public static final String SESSION_ID_EXTRA = "session.id";
+
+ public static final String LOG_BASIC_DEBUG_BOOLEAN_EXTRA = "basicDebugBool";
+ public static final String LOG_TRACE_BT_DEBUG_BOOLEAN_EXTRA = "btTraceBool";
+
+
+ public static final String ENABLE_LEGACY_MODE_EXTRA = "ENABLE_LEGACY_MODE_EXTRA";
+
+ public static final String HARDWARE_DISCONNECTED = "hardware.disconect";
+ public static final String HARDWARE_CONNECTED = "hardware.connected";
+
+ public static final String SEND_PACKET_TO_APP_LOCATION_EXTRA_NAME = "senderintent";
+ public static final String SEND_PACKET_TO_ROUTER_LOCATION_EXTRA_NAME = "routerintent";
+
+
+ public static final String BIND_REQUEST_TYPE_CLIENT = "BIND_REQUEST_TYPE_CLIENT";
+ public static final String BIND_REQUEST_TYPE_ALT_TRANSPORT = "BIND_REQUEST_TYPE_ALT_TRANSPORT";
+
+
+ public static final String PING_ROUTER_SERVICE_EXTRA = "ping.router.service";
+ /**
+ * Alt transport
+ *
+ */
+ /**
+ * This will be the response when a hardware connect event comes through from an alt transport.
+ * This is because it only makes sense to register an alt transport when a connection is established with that
+ * transport, not waiting for one.
+ */
+ public static final int ROUTER_REGISTER_ALT_TRANSPORT_RESPONSE = 0x02;
+ public static final int ROUTER_REGISTER_ALT_TRANSPORT_RESPONSE_SUCESS = 0x00;
+ /**
+ * There is already another alt transport connected, so we are unable to register this one
+ */
+ public static final int ROUTER_REGISTER_ALT_TRANSPORT_ALREADY_CONNECTED = 0x01;
+
+ /**
+ * This means the router service is shutting down for some reason. Most likely
+ */
+ public static final int ROUTER_SHUTTING_DOWN_NOTIFICATION = 0x0F;
+
+ /**
+ * There is a newer service to start up, so this one is shutting down
+ */
+ public static final int ROUTER_SHUTTING_DOWN_REASON_NEWER_SERVICE = 0x00;
+
+ /**
+ * Router to Client binding service
+ *
+ */
+
+ //WHATS
+ /**
+ * Command to the service to register a client, receiving callbacks
+ * from the service. The Message's replyTo field must be a Messenger of
+ * the client where callbacks should be sent.
+ */
+ public static final int ROUTER_REGISTER_CLIENT = 0x01;
+ /**
+ * This response message will contain if the registration request was successful or not. If not, the reason will be
+ * great or equal to 1 and be descriptive of why it was denied.
+ */
+ public static final int ROUTER_REGISTER_CLIENT_RESPONSE = 0x02;
+ //Response arguments
+ public static final int REGISTRATION_RESPONSE_SUCESS = 0x00;
+ public static final int REGISTRATION_RESPONSE_DENIED_AUTHENTICATION_FAILED = 0x01;
+ public static final int REGISTRATION_RESPONSE_DENIED_NO_CONNECTION = 0x02;
+ public static final int REGISTRATION_RESPONSE_DENIED_APP_ID_NOT_INCLUDED = 0x03;
+ public static final int REGISTRATION_RESPONSE_DENIED_LEGACY_MODE_ENABLED = 0x04;
+ public static final int REGISTRATION_RESPONSE_DENIED_UNKNOWN = 0xFF;
+
+ /**
+ * Command to the service to unregister a client, to stop receiving callbacks
+ * from the service. The Message's replyTo field must be a Messenger of
+ * the client as previously given with MSG_REGISTER_CLIENT. Also include the app id as arg1.
+ */
+ public static final int ROUTER_UNREGISTER_CLIENT = 0x03;
+ public static final int ROUTER_UNREGISTER_CLIENT_RESPONSE = 0x04;
+ //Response arguments
+ public static final int UNREGISTRATION_RESPONSE_SUCESS = 0x00;
+ public static final int UNREGISTRATION_RESPONSE_FAILED_APP_ID_NOT_FOUND = 0x01;
+
+
+ /**
+ * what message type to notify apps of a hardware connection event. The connection event will be placed in the bundle
+ * attached to the message
+ */
+ public static final int HARDWARE_CONNECTION_EVENT = 0x05;
+
+
+ public static final int ROUTER_REQUEST_BT_CLIENT_CONNECT = 0x10;
+ public static final int ROUTER_REQUEST_BT_CLIENT_CONNECT_RESPONSE = 0x11;
+
+ /**
+ * This provides the app with an ability to request another session within the router service.
+ * A replyTo must be provided or else there won't be a response
+ */
+ public static final int ROUTER_REQUEST_NEW_SESSION = 0x12;
+ public static final int ROUTER_REQUEST_NEW_SESSION_RESPONSE = 0x13;
+ //Response arguments
+ public static final int ROUTER_REQUEST_NEW_SESSION_RESPONSE_SUCESS = 0x00;
+ public static final int ROUTER_REQUEST_NEW_SESSION_RESPONSE_FAILED_APP_NOT_FOUND = 0x01;
+ public static final int ROUTER_REQUEST_NEW_SESSION_RESPONSE_FAILED_APP_ID_NOT_INCL = 0x02;
+
+ /**
+ * This provides the app with an ability to request another session within the router service.
+ * A replyTo must be provided or else there won't be a response
+ */
+ public static final int ROUTER_REMOVE_SESSION = 0x14;
+ public static final int ROUTER_REMOVE_SESSION_RESPONSE = 0x15;
+ //Response arguments
+ public static final int ROUTER_REMOVE_SESSION_RESPONSE_SUCESS = 0x00;
+ public static final int ROUTER_REMOVE_SESSION_RESPONSE_FAILED_APP_NOT_FOUND = 0x01;
+ public static final int ROUTER_REMOVE_SESSION_RESPONSE_FAILED_APP_ID_NOT_INCL = 0x02;
+ public static final int ROUTER_REMOVE_SESSION_RESPONSE_FAILED_SESSION_NOT_FOUND = 0x03;
+ public static final int ROUTER_REMOVE_SESSION_RESPONSE_FAILED_SESSION_ID_NOT_INCL = 0x04;
+ /**
+ * Command to have router service to send a packet
+ */
+ public static final int ROUTER_SEND_PACKET = 0x20;
+
+
+
+ //response
+ /**
+ * Router has received a packet and sent it to the client
+ */
+ public static final int ROUTER_RECEIVED_PACKET = 0x26;
+ //response
+
+ //BUNDLE EXTRAS
+
+ public static final String FORMED_PACKET_EXTRA_NAME = "packet";
+
+ public static final String BYTES_TO_SEND_EXTRA_NAME = "bytes";
+ public static final String BYTES_TO_SEND_EXTRA_OFFSET = "offset";
+ public static final String BYTES_TO_SEND_EXTRA_COUNT = "count";
+ public static final String BYTES_TO_SEND_FLAGS = "flags";
+
+ public static final String PACKET_PRIORITY_COEFFICIENT = "priority_coefficient";
+
+ public static final int BYTES_TO_SEND_FLAG_NONE = 0x00;
+ public static final int BYTES_TO_SEND_FLAG_SDL_PACKET_INCLUDED = 0x01;
+ public static final int BYTES_TO_SEND_FLAG_LARGE_PACKET_START = 0x02;
+ public static final int BYTES_TO_SEND_FLAG_LARGE_PACKET_CONT = 0x04;
+ public static final int BYTES_TO_SEND_FLAG_LARGE_PACKET_END = 0x08;
+
+ public static final String CONNECTED_DEVICE_STRING_EXTRA_NAME = "devicestring";
+
+ public static final int PACKET_SENDING_ERROR_NOT_REGISTERED_APP = 0x00;
+ public static final int PACKET_SENDING_ERROR_NOT_CONNECTED = 0x01;
+ public static final int PACKET_SENDING_ERROR_UKNOWN = 0xFF;
+
+
+
+
+
+}
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/USBTransport.java b/sdl_android_lib/src/com/smartdevicelink/transport/USBTransport.java
index eecd91cfc..72616084a 100644
--- a/sdl_android_lib/src/com/smartdevicelink/transport/USBTransport.java
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/USBTransport.java
@@ -1,5 +1,12 @@
package com.smartdevicelink.transport;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -8,24 +15,17 @@ import android.content.IntentFilter;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.ParcelFileDescriptor;
-
import com.smartdevicelink.exception.SdlException;
import com.smartdevicelink.exception.SdlExceptionCause;
+import com.smartdevicelink.protocol.SdlPacket;
import com.smartdevicelink.trace.SdlTrace;
-import com.smartdevicelink.trace.enums.InterfaceActivityDirection;
+import com.smartdevicelink.trace.enums.InterfaceActivityDirection;
import com.smartdevicelink.transport.ITransportListener;
import com.smartdevicelink.transport.SdlTransport;
import com.smartdevicelink.transport.SiphonServer;
import com.smartdevicelink.transport.enums.TransportType;
import com.smartdevicelink.util.DebugTool;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
/**
* Class that implements USB transport.
*
@@ -35,6 +35,7 @@ import java.io.OutputStream;
* the other side will NOT be notified and unblocked from reading data until
* some data is sent again or the USB is physically disconnected.
*/
+@SuppressLint("NewApi")
public class USBTransport extends SdlTransport {
/**
* Broadcast action: sent when a USB accessory is attached.
@@ -204,10 +205,10 @@ public class USBTransport extends SdlTransport {
* @return true if the bytes are sent successfully
*/
@Override
- protected boolean sendBytesOverTransport(byte[] msgBytes, int offset,
- int length) {
- logD("SendBytes: array size " + msgBytes.length + ", offset " + offset +
- ", length " + length);
+ protected boolean sendBytesOverTransport(SdlPacket packet) {
+ byte[] msgBytes = packet.constructPacket();
+ logD("SendBytes: array size " + msgBytes.length + ", offset " + 0 +
+ ", length " + msgBytes.length);
boolean result = false;
final State state = getState();
@@ -215,13 +216,13 @@ public class USBTransport extends SdlTransport {
case CONNECTED:
if (mOutputStream != null) {
try {
- mOutputStream.write(msgBytes, offset, length);
+ mOutputStream.write(msgBytes, 0, msgBytes.length);
result = true;
logI("Bytes successfully sent");
SdlTrace.logTransportEvent(TAG + ": bytes sent",
null, InterfaceActivityDirection.Transmit,
- msgBytes, offset, length,
+ msgBytes, 0, msgBytes.length,
SDL_LIB_TRACE_KEY);
} catch (IOException e) {
final String msg = "Failed to send bytes over USB";
@@ -650,6 +651,8 @@ public class USBTransport extends SdlTransport {
*/
private final String TAG = USBTransportReader.class.getSimpleName();
+ SdlPsm psm;
+
/**
* Checks if the thread has been interrupted.
*
@@ -667,7 +670,8 @@ public class USBTransport extends SdlTransport {
@Override
public void run() {
logD("USB reader started!");
-
+ psm = new SdlPsm();
+ psm.reset();
if (connect()) {
readFromTransport();
}
@@ -742,11 +746,13 @@ public class USBTransport extends SdlTransport {
final int READ_BUFFER_SIZE = 4096;
byte[] buffer = new byte[READ_BUFFER_SIZE];
int bytesRead;
+ // byte input;
+ boolean stateProgress = false;
// read loop
while (!isInterrupted()) {
try {
- bytesRead = mInputStream.read(buffer);
+ bytesRead = mInputStream.read(buffer);
if (bytesRead == -1) {
if (isInterrupted()) {
logI("EOF reached, and thread is interrupted");
@@ -767,19 +773,34 @@ public class USBTransport extends SdlTransport {
}
logD("Read " + bytesRead + " bytes");
- SdlTrace.logTransportEvent(TAG + ": read bytes", null,
- InterfaceActivityDirection.Receive, buffer, bytesRead,
- SDL_LIB_TRACE_KEY);
+ //FIXME SdlTrace.logTransportEvent(TAG + ": read bytes", null,
+ // InterfaceActivityDirection.Receive, buffer, bytesRead,
+ // SDL_LIB_TRACE_KEY);
if (isInterrupted()) {
logI("Read some data, but thread is interrupted");
return;
}
-
- if (bytesRead > 0) {
- synchronized (USBTransport.this) {
- handleReceivedBytes(buffer, bytesRead);
- }
+ byte input;
+ for(int i=0;i<bytesRead; i++){
+ input=buffer[i];
+ stateProgress = psm.handleByte(input);
+ if(!stateProgress){//We are trying to weed through the bad packet info until we get something
+ //Log.w(TAG, "Packet State Machine did not move forward from state - "+ psm.getState()+". PSM being Reset.");
+ psm.reset();
+ buffer = new byte[READ_BUFFER_SIZE];
+ }
+
+ if(psm.getState() == SdlPsm.FINISHED_STATE){
+ synchronized (USBTransport.this) {
+ //Log.d(TAG, "Packet formed, sending off");
+ handleReceivedPacket((SdlPacket)psm.getFormedPacket());
+ }
+ //We put a trace statement in the message read so we can avoid all the extra bytes
+ psm.reset();
+ buffer = new byte[READ_BUFFER_SIZE]; //FIXME just do an array copy and send off
+
+ }
}
}
}
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/enums/TransportType.java b/sdl_android_lib/src/com/smartdevicelink/transport/enums/TransportType.java
index 46e0c49ab..2283d8d01 100644
--- a/sdl_android_lib/src/com/smartdevicelink/transport/enums/TransportType.java
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/enums/TransportType.java
@@ -4,7 +4,10 @@ package com.smartdevicelink.transport.enums;
* Defines available types of the transports.
*/
public enum TransportType {
-
+ /**
+ * Experimental multiplexing (only supports bluetooth at the moment)
+ */
+ MULTIPLEX,
/**
* Transport type is Bluetooth.
*/
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/utl/ByteAraryMessageAssembler.java b/sdl_android_lib/src/com/smartdevicelink/transport/utl/ByteAraryMessageAssembler.java
new file mode 100644
index 000000000..98b8b666e
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/utl/ByteAraryMessageAssembler.java
@@ -0,0 +1,71 @@
+package com.smartdevicelink.transport.utl;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import android.util.Log;
+
+import com.smartdevicelink.transport.TransportConstants;
+
+public class ByteAraryMessageAssembler {
+ private static final String TAG = "ByteAraryMessageAssembler";
+ ByteArrayOutputStream buffer;
+ boolean isFinished;
+
+ public void init(){
+ close();
+ this.isFinished = false;
+ buffer = new ByteArrayOutputStream();
+ }
+
+ public boolean close(){
+ if(buffer!=null){
+ try {
+ buffer.close();
+ buffer = null;
+ return true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public void append(byte[] bytes){
+ try {
+ buffer.write(bytes);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public synchronized boolean handleMessage(int flags, byte[] packet){
+ switch(flags){
+ case TransportConstants.BYTES_TO_SEND_FLAG_LARGE_PACKET_START: //Fall through to write the bytes after they buffer was init'ed
+ case TransportConstants.BYTES_TO_SEND_FLAG_LARGE_PACKET_CONT:
+ append(packet);
+ break;
+ case TransportConstants.BYTES_TO_SEND_FLAG_LARGE_PACKET_END:
+ append(packet);
+ this.isFinished = true;
+ break;
+ default:
+ Log.e(TAG, "Error handling message");
+ return false;
+ }
+
+ return true;
+ }
+
+ public byte[] getBytes(){
+ if(buffer == null){
+ return null;
+ }
+ return this.buffer.toByteArray();
+ }
+
+ public boolean isFinished(){
+ return this.isFinished;
+ }
+}
diff --git a/sdl_android_lib/src/com/smartdevicelink/transport/utl/ByteArrayMessageSpliter.java b/sdl_android_lib/src/com/smartdevicelink/transport/utl/ByteArrayMessageSpliter.java
new file mode 100644
index 000000000..b8c33c614
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/transport/utl/ByteArrayMessageSpliter.java
@@ -0,0 +1,107 @@
+package com.smartdevicelink.transport.utl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import com.smartdevicelink.transport.TransportConstants;
+
+import android.os.Bundle;
+import android.os.Message;
+import android.util.Log;
+
+public class ByteArrayMessageSpliter {
+ private static final String TAG = "ByteArrayMessageSpliter";
+
+ //To test set this value to something very small (eg, 50)
+ public static final int MAX_BINDER_SIZE = 1000000/4; //~1MB/4 We do this as a safety measure. IPC only allows 1MB for everything. We should never fill more than 25% of the buffer so here we make sure we stay under that
+
+ boolean firstPacket;
+ ByteArrayInputStream stream;
+ int bytesRead;
+ int what;
+ Long appId;
+ byte[] buffer;
+ int orginalSize;
+ int priorityCoef;
+
+ public ByteArrayMessageSpliter(String appId,int what, byte[] bytes, int priorityCoef){
+ this.appId = Long.valueOf(appId);
+ this.what = what;
+ stream = new ByteArrayInputStream(bytes);
+ orginalSize = stream.available();
+ bytesRead = 0;
+ firstPacket = true;
+ this.priorityCoef = priorityCoef;
+ }
+
+ public ByteArrayMessageSpliter(Long appId,int what, byte[] bytes, int priorityCoef){
+ this.appId = appId;
+ this.what = what;
+ stream = new ByteArrayInputStream(bytes);
+ orginalSize = stream.available();
+ bytesRead = 0;
+ firstPacket = true;
+ this.priorityCoef = priorityCoef;
+ }
+
+ public boolean isActive(){
+ if(stream!=null){
+ return stream.available()>0;
+ }
+ return false;
+ }
+
+ public boolean close(){
+ if(stream == null){
+ return false;
+ }
+ try {
+ stream.close();
+ stream = null;
+ return true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+
+ }
+
+ public Message nextMessage(){
+ if(stream == null || stream.available()<=0){
+ return null;
+ }
+
+ Message message = Message.obtain(); //Do we need to always obtain new? or can we just swap bundles?
+ message.what = this.what;// TransportConstants.ROUTER_SEND_PACKET;
+ Bundle bundle = new Bundle();
+
+
+ if(stream.available()>=MAX_BINDER_SIZE){
+ buffer = new byte[MAX_BINDER_SIZE];
+ bytesRead = stream.read(buffer, 0, MAX_BINDER_SIZE);
+ }else{
+ buffer = new byte[stream.available()];
+ bytesRead = stream.read(buffer, 0, stream.available());
+ }
+
+ bundle.putByteArray(TransportConstants.BYTES_TO_SEND_EXTRA_NAME, buffer); //Do we just change this to the args and objs
+ bundle.putInt(TransportConstants.BYTES_TO_SEND_EXTRA_OFFSET, 0);
+ bundle.putInt(TransportConstants.BYTES_TO_SEND_EXTRA_COUNT, bytesRead);
+
+ //Determine which flag should be sent for this division of the packet
+ if(firstPacket){
+ bundle.putInt(TransportConstants.BYTES_TO_SEND_FLAGS, TransportConstants.BYTES_TO_SEND_FLAG_LARGE_PACKET_START);
+ bundle.putInt(TransportConstants.PACKET_PRIORITY_COEFFICIENT, this.priorityCoef);
+ firstPacket = false;
+ }else if(stream.available()<=0){ //We are at the end of the stream so let the flag reflect that
+ bundle.putInt(TransportConstants.BYTES_TO_SEND_FLAGS, TransportConstants.BYTES_TO_SEND_FLAG_LARGE_PACKET_END);
+ }else{
+ bundle.putInt(TransportConstants.BYTES_TO_SEND_FLAGS, TransportConstants.BYTES_TO_SEND_FLAG_LARGE_PACKET_CONT);
+ }
+
+ bundle.putLong(TransportConstants.APP_ID_EXTRA, appId);
+ message.setData(bundle);
+ Log.i(TAG, ((100 - ((stream.available()*100)/orginalSize) ))+ " percent complete.");
+ return message;
+ }
+}
diff --git a/sdl_android_lib/src/com/smartdevicelink/util/BitConverter.java b/sdl_android_lib/src/com/smartdevicelink/util/BitConverter.java
index 4cb1927b1..c844e49a4 100644
--- a/sdl_android_lib/src/com/smartdevicelink/util/BitConverter.java
+++ b/sdl_android_lib/src/com/smartdevicelink/util/BitConverter.java
@@ -69,4 +69,22 @@ public class BitConverter {
}
return ret;
}
+
+ /**
+ * Converts the byte array into a string of hex values.
+ * @param bytes
+ * @param end EXCLUSIVE so if it it receives 10 it will print 0-9
+ * @return
+ */
+ public static String bytesToHex(byte[] bytes,int end){
+ if(bytes.length<end){
+ end = bytes.length;
+ }
+ StringBuilder sb = new StringBuilder();
+ for(int i=0;i<end;i++){
+ sb.append(" ");
+ sb.append(String.format("%02X ", bytes[i]));
+ }
+ return sb.toString();
+ }
}
diff --git a/sdl_android_lib/src/com/smartdevicelink/util/HttpRequestTask.java b/sdl_android_lib/src/com/smartdevicelink/util/HttpRequestTask.java
new file mode 100644
index 000000000..f5f56ece4
--- /dev/null
+++ b/sdl_android_lib/src/com/smartdevicelink/util/HttpRequestTask.java
@@ -0,0 +1,172 @@
+package com.smartdevicelink.util;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import android.os.AsyncTask;
+import android.util.Log;
+
+public class HttpRequestTask extends AsyncTask<String, String, String> {
+ private static final String TAG = "Http Request Task";
+
+ public static final String REQUEST_TYPE_POST = "POST";
+ public static final String REQUEST_TYPE_GET = "GET";
+ public static final String REQUEST_TYPE_DELETE = "DELETE";
+
+ HttpRequestTaskCallback cb;
+
+ /**
+ * @param HttpRequestTaskCallback callback for when this task finishes
+ * <br><br><b> - When calling execute, params as followed: </b><br>
+ * 1. Url String<br>
+ * 2. Request type (Defined in this class) REQUEST_TYPE_POST, REQUEST_TYPE_GET, REQUEST_TYPE_DELETE<br>
+ * 3. (Optional) Data to be sent. <br>
+ * 4. (Optional) Content Type Default will be application/json<br>
+ * 5. (Optional) Accept Type default will be application/json
+ *
+ */
+ public HttpRequestTask( HttpRequestTaskCallback hcb){
+ this.cb = hcb;
+ }
+
+ @Override
+ protected String doInBackground(String... params) {
+ int length = params.length;
+ String urlString = params[0];
+ String request_type = params[1];
+
+ //Grab and set data to be written if included
+ String data;
+ if(length>2){
+ data = params[2];
+ }else{
+ data = null;
+ }
+
+ //Grab and set content type for the header if included
+ String contentType;
+ if(length>3){
+ contentType = params[3];
+ }else{
+ contentType = "application/json";
+ }
+ //Grab and set accept type for the header if included
+ String acceptType;
+ if(length>4){
+ acceptType = params[4];
+ }else{
+ acceptType = "application/json";
+ }
+
+ if(urlString == null || request_type == null){
+ Log.e(TAG, "Can't process request, param error");
+ if(cb!=null){
+ cb.httpFailure(-1);
+ }
+ return "Error";
+ }
+
+ HttpURLConnection urlConnection = null;
+ BufferedReader reader = null;
+ try {
+ URL url = new URL(urlString);
+ urlConnection = (HttpURLConnection) url.openConnection();
+ urlConnection.setDoOutput(true);
+ urlConnection.setRequestMethod(request_type);
+ urlConnection.setRequestProperty("Content-Type", contentType);
+ urlConnection.setRequestProperty("Accept", acceptType);
+ //If we have data, we should write it out
+ if(data !=null){
+ Writer writer = new BufferedWriter(new OutputStreamWriter(urlConnection.getOutputStream(), "UTF-8"));
+ writer.write(data);
+ writer.close();
+ }
+ InputStream inputStream = urlConnection.getInputStream();
+
+ int responseCode = urlConnection.getResponseCode();
+ if (responseCode == 200) { //Success
+ //input stream
+ StringBuffer buffer = new StringBuffer();
+ if (inputStream == null) {
+ // Nothing to do.
+ if(cb!=null){
+ cb.httpCallComplete(null);
+ }
+ return null;
+ }
+ reader = new BufferedReader(new InputStreamReader(inputStream));
+
+ String inputLine;
+ while ((inputLine = reader.readLine()) != null)
+ buffer.append(inputLine + "\n");
+ if (buffer.length() == 0) {
+ // Stream was empty. No point in parsing.
+ if(cb!=null){
+ cb.httpCallComplete(null);
+ }
+ return null;
+ }
+ String response = null;
+
+ response = buffer.toString();
+ //send to post execute
+ if(cb!=null){
+ cb.httpCallComplete(response);
+ }
+ return response;
+ }else{
+ if(cb!=null){
+ cb.httpFailure(responseCode);
+ }
+ Log.e(TAG, "Failed to download file - " + responseCode);
+ return null;
+ }
+
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ finally {
+ if (urlConnection != null) {
+ urlConnection.disconnect();
+ }
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (final IOException e) {
+ Log.e(TAG, "Error closing stream", e);
+ }
+ }
+ if(cb!=null){
+ cb.httpFailure(-1);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Callback interface for HTTP requests.
+ * @author Joey Grover
+ *
+ */
+ public interface HttpRequestTaskCallback{
+ /**
+ * Called when HTTP request is successfully completed.
+ * @param response The response to the HTTP request.
+ */
+ public abstract void httpCallComplete(String response);
+ /**
+ * Called when HTTP request failed.
+ * @param statusCode The HTTP failure code.
+ */
+ public abstract void httpFailure(int statusCode);
+ }
+
+}