summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarkos Rapitis <mrapitis@ford.com>2017-05-01 12:38:27 -0400
committerMarkos Rapitis <mrapitis@ford.com>2017-05-01 12:38:27 -0400
commitb7edfd36ad19855d2205e2e27c4f1a7c4c948cc4 (patch)
tree27754478d861105150280184fe57b96e56b797d7
parentef7929077cab597b7158c43a3d53bf127f963eb1 (diff)
downloadsdl_android-feature/issue_469_enhancements.tar.gz
Removed need to utilize OutputStream during streaming. Fixed a race condition in onStreamDataAvailable. Added accessory methods to start and end service on demand. Added sdl service type listener allowing for better lifecycle management.feature/issue_469_enhancements
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java153
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java69
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBuilder.java12
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/proxy/interfaces/ISdlServiceListener.java10
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/streaming/StreamWriterThread.java130
5 files changed, 247 insertions, 127 deletions
diff --git a/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java b/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java
index f77f16aa9..3c44b38ef 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java
@@ -22,14 +22,16 @@ import android.view.View;
import android.view.Window;
import android.view.WindowManager;
+import com.smartdevicelink.protocol.enums.SessionType;
+import com.smartdevicelink.proxy.SdlProxyALM;
+import com.smartdevicelink.proxy.SdlProxyBase;
import com.smartdevicelink.proxy.rpc.OnTouchEvent;
import com.smartdevicelink.proxy.rpc.ScreenParams;
import com.smartdevicelink.proxy.rpc.TouchCoord;
import com.smartdevicelink.proxy.rpc.TouchEvent;
import com.smartdevicelink.proxy.rpc.enums.TouchType;
+import com.smartdevicelink.streaming.StreamWriterThread;
-import java.io.IOException;
-import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.nio.ByteBuffer;
import java.util.List;
@@ -48,15 +50,17 @@ public class VirtualDisplayEncoder {
private volatile VirtualDisplay virtualDisplay = null;
private volatile SdlPresentation presentation = null;
private Class<? extends SdlPresentation> presentationClass = null;
- private VideoStreamWriterThread streamWriterThread = null;
+ private StreamWriterThread streamWriterThread = null;
private Context mContext;
- private OutputStream sdlOutStream = null;
private Boolean initPassed = false;
private Handler uiHandler = new Handler(Looper.getMainLooper());
private final static int REFRESH_RATE_MS = 100;
private final static Object CLOSE_VID_SESSION_LOCK = new Object();
private final static Object START_DISP_LOCK = new Object();
private final static Object STREAMING_LOCK = new Object();
+ private static Integer SLEEP_TIME = 5; //milliseconds
+ private static Integer MAX_SLEEP_DURATION = 500; //milliseconds
+
public class StreamingParameters {
protected int displayDensity = DisplayMetrics.DENSITY_HIGH;
@@ -141,29 +145,37 @@ public class VirtualDisplayEncoder {
* @param screenParams
* @throws Exception
*/
- public void init(Context context, OutputStream videoStream, Class<? extends SdlPresentation> presentationClass, ScreenParams screenParams) throws Exception {
+ public void init(Context context, SdlProxyALM sdlProxy, Class<? extends SdlPresentation> presentationClass) throws Exception {
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
Log.e(TAG, "API level of 21 required for VirtualDisplayEncoder");
throw new Exception("API level of 21 required");
}
+ if (context == null || sdlProxy == null || presentationClass == null) {
+ Log.e(TAG, "init parameters cannot be null for VirtualDisplayEncoder");
+ throw new Exception("init parameters cannot be null");
+ }
+
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mContext = context;
+ //need to check for null values
+ ScreenParams screenParams = sdlProxy.getDisplayCapabilities().getScreenParams();
+
+ //need to check image resolution for null
if(screenParams.getImageResolution().getResolutionHeight() != null){
streamingParams.videoHeight = screenParams.getImageResolution().getResolutionHeight();
}
+ //need to check image resolution for null
if(screenParams.getImageResolution().getResolutionWidth() != null){
streamingParams.videoWidth = screenParams.getImageResolution().getResolutionWidth();
}
- sdlOutStream = videoStream;
-
this.presentationClass = presentationClass;
- setupVideoStreamWriter();
+ setupVideoStreamWriter(sdlProxy);
initPassed = true;
}
@@ -240,20 +252,8 @@ public class VirtualDisplayEncoder {
private void closeVideoSession() {
synchronized (CLOSE_VID_SESSION_LOCK) {
- if (sdlOutStream != null) {
-
- try {
- sdlOutStream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- sdlOutStream = null;
-
- if (streamWriterThread != null) {
- streamWriterThread.clearOutputStream();
- streamWriterThread.clearByteBuffer();
- }
+ if (streamWriterThread != null) {
+ streamWriterThread.clearByteBuffer();
}
}
}
@@ -390,18 +390,18 @@ public class VirtualDisplayEncoder {
}
private void onStreamDataAvailable(byte[] data, int size) {
- if (sdlOutStream != null) {
- try {
- if (streamWriterThread.getOutputStream() == null) {
- streamWriterThread.setOutputStream(sdlOutStream);
- }
+ try {
+ Integer totalSleep = 0;
+ while (streamWriterThread.getByteBuffer() != null) {
+ Thread.sleep(SLEEP_TIME); //try to sleep until the byte buffer is clear
+ totalSleep = totalSleep + SLEEP_TIME;
+ if (totalSleep >= MAX_SLEEP_DURATION)
+ break;
+ }
- streamWriterThread.setByteBuffer(data, size);
- } catch (Exception e) {
+ streamWriterThread.setByteBuffer(data, size);
+ } catch (Exception e) {
e.printStackTrace();
- }
- } else {
- Log.e(TAG, "sdlOutStream is null");
}
}
@@ -539,10 +539,10 @@ public class VirtualDisplayEncoder {
});
}
- private void setupVideoStreamWriter() {
+ private void setupVideoStreamWriter(SdlProxyALM sdlProxy) {
if (streamWriterThread == null) {
// Setup VideoStreamWriterThread thread
- streamWriterThread = new VideoStreamWriterThread();
+ streamWriterThread = new StreamWriterThread(sdlProxy, SessionType.NAV);
streamWriterThread.setName("VideoStreamWriter");
streamWriterThread.setPriority(Thread.MAX_PRIORITY);
streamWriterThread.setDaemon(true);
@@ -560,93 +560,8 @@ public class VirtualDisplayEncoder {
} catch (Exception ex) {
ex.printStackTrace();
}
-
- streamWriterThread.clearOutputStream();
streamWriterThread.clearByteBuffer();
}
streamWriterThread = null;
}
-
- private class VideoStreamWriterThread extends Thread {
- private Boolean isHalted = false;
- private byte[] buf = null;
- private Integer size = 0;
- private OutputStream os = null;
-
- public OutputStream getOutputStream() {
- return os;
- }
-
- public byte[] getByteBuffer() {
- return buf;
- }
-
- public void setOutputStream(OutputStream os) {
- synchronized (STREAMING_LOCK) {
- clearOutputStream();
- this.os = os;
- }
- }
-
- public void setByteBuffer(byte[] buf, Integer size) {
- synchronized (STREAMING_LOCK) {
- clearByteBuffer();
- this.buf = buf;
- this.size = size;
- }
- }
-
- private void clearOutputStream() {
- synchronized (STREAMING_LOCK) {
- try {
- if (os != null) {
- os.close();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- os = null;
- }
- }
-
- private void clearByteBuffer() {
- synchronized (STREAMING_LOCK) {
- try {
- if (buf != null) {
- buf = null;
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
-
- private void writeToStream() {
- synchronized (STREAMING_LOCK) {
- if (buf == null || os == null)
- return;
-
- try {
- os.write(buf, 0, size);
- clearByteBuffer();
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- }
- }
-
- public void run() {
- while (!isHalted) {
- writeToStream();
- }
- }
-
- /**
- * Method that marks thread as halted.
- */
- public void halt() {
- isHalted = true;
- }
- }
-
} \ No newline at end of file
diff --git a/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java b/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java
index 7678eaaa6..fecd20528 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java
@@ -58,6 +58,7 @@ import com.smartdevicelink.proxy.callbacks.OnServiceNACKed;
import com.smartdevicelink.proxy.interfaces.IProxyListenerALM;
import com.smartdevicelink.proxy.interfaces.IProxyListenerBase;
import com.smartdevicelink.proxy.interfaces.IPutFileResponseListener;
+import com.smartdevicelink.proxy.interfaces.ISdlServiceListener;
import com.smartdevicelink.proxy.rpc.*;
import com.smartdevicelink.proxy.rpc.enums.AppHMIType;
import com.smartdevicelink.proxy.rpc.enums.AudioStreamingState;
@@ -89,6 +90,7 @@ import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
import com.smartdevicelink.security.SdlSecurityBase;
import com.smartdevicelink.streaming.StreamRPCPacketizer;
+import com.smartdevicelink.streaming.StreamWriterThread;
import com.smartdevicelink.trace.SdlTrace;
import com.smartdevicelink.trace.TraceDeviceInfo;
import com.smartdevicelink.trace.enums.InterfaceActivityDirection;
@@ -106,7 +108,10 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
private SdlSession sdlSession = null;
private proxyListenerType _proxyListener = null;
-
+
+ private ISdlServiceListener _sdlServiceListener = null;
+ private StreamWriterThread _videoStreamWriter = null;
+
protected Service _appService = null;
private String sPoliciesURL = ""; //for testing only
@@ -333,9 +338,9 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
RPCProtectedServiceStarted();
}
} else if (sessionType.eq(SessionType.NAV)) {
- NavServiceStarted();
+ NavServiceStarted(isEncrypted);
} else if (sessionType.eq(SessionType.PCM)) {
- AudioServiceStarted();
+ AudioServiceStarted(isEncrypted);
} else if (sessionType.eq(SessionType.RPC)){
cycleProxy(SdlDisconnectedReason.RPC_SESSION_ENDED);
}
@@ -3495,7 +3500,29 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
}
public ScheduledExecutorService createScheduler(){
return Executors.newSingleThreadScheduledExecutor();
- }
+ }
+
+ public void setSdlServiceListener(ISdlServiceListener sdlServiceListener) {
+ _sdlServiceListener = sdlServiceListener;
+ }
+
+ public void setVideoStreamWriter(StreamWriterThread videoStreamWriter) {
+ _videoStreamWriter = videoStreamWriter;
+ }
+
+ public void startSdlService(boolean isEncrypted, SessionType serviceType) {
+ if (sdlSession == null) return; //log an error here
+ if (_sdlServiceListener == null) return; //log an error here
+
+ sdlSession.startService(serviceType, sdlSession.getSessionId(), isEncrypted);
+ }
+
+ public void endSdlService(SessionType serviceType) {
+ if (sdlSession == null) return; //log an error here
+ if (_sdlServiceListener == null) return; //log an error here
+
+ sdlSession.endService(serviceType, sdlSession.getSessionId());
+ }
/**
*Opens the video service (serviceType 11) and subsequently streams raw H264 video from an InputStream provided by the app
@@ -3792,48 +3819,78 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
sdlSession.drainEncoder(endOfStream);
}
- private void NavServiceStarted() {
+ private void NavServiceStarted(boolean isEncrypted) {
navServiceStartResponseReceived = true;
navServiceStartResponse = true;
+
+ if (_sdlServiceListener != null)
+ _sdlServiceListener.onSdlServiceStarted(SessionType.NAV, isEncrypted);
+
+ if (_videoStreamWriter != null)
+ _videoStreamWriter.handleSdlSession(sdlSession);
}
private void NavServiceStartedNACK() {
navServiceStartResponseReceived = true;
navServiceStartResponse = false;
+
+ if (_sdlServiceListener != null)
+ _sdlServiceListener.onSdlServiceStartedNacked(SessionType.NAV);
}
- private void AudioServiceStarted() {
+ private void AudioServiceStarted(boolean isEncrypted) {
pcmServiceStartResponseReceived = true;
pcmServiceStartResponse = true;
+
+ if (_sdlServiceListener != null)
+ _sdlServiceListener.onSdlServiceStarted(SessionType.PCM, isEncrypted);
}
private void RPCProtectedServiceStarted() {
rpcProtectedResponseReceived = true;
rpcProtectedStartResponse = true;
+
+ if (_sdlServiceListener != null)
+ _sdlServiceListener.onSdlServiceStarted(SessionType.RPC, true);
}
private void AudioServiceStartedNACK() {
pcmServiceStartResponseReceived = true;
pcmServiceStartResponse = false;
+
+ if (_sdlServiceListener != null)
+ _sdlServiceListener.onSdlServiceStartedNacked(SessionType.PCM);
}
private void NavServiceEnded() {
navServiceEndResponseReceived = true;
navServiceEndResponse = true;
+
+ if (_sdlServiceListener != null)
+ _sdlServiceListener.onSdlServiceEnded(SessionType.NAV);
}
private void NavServiceEndedNACK() {
navServiceEndResponseReceived = true;
navServiceEndResponse = false;
+
+ if (_sdlServiceListener != null)
+ _sdlServiceListener.onSdlServiceEndedNacked(SessionType.NAV);
}
private void AudioServiceEnded() {
pcmServiceEndResponseReceived = true;
pcmServiceEndResponse = true;
+
+ if (_sdlServiceListener != null)
+ _sdlServiceListener.onSdlServiceEnded(SessionType.PCM);
}
private void AudioServiceEndedNACK() {
pcmServiceEndResponseReceived = true;
pcmServiceEndResponse = false;
+
+ if (_sdlServiceListener != null)
+ _sdlServiceListener.onSdlServiceEndedNacked(SessionType.PCM);
}
public void setAppService(Service mService)
diff --git a/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBuilder.java b/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBuilder.java
index 9e6921a61..fbc2b998c 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBuilder.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBuilder.java
@@ -5,6 +5,7 @@ import java.util.Vector;
import com.smartdevicelink.exception.SdlException;
import com.smartdevicelink.proxy.interfaces.IProxyListenerALM;
+import com.smartdevicelink.proxy.interfaces.ISdlServiceListener;
import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
import com.smartdevicelink.proxy.rpc.TTSChunk;
import com.smartdevicelink.proxy.rpc.enums.AppHMIType;
@@ -39,8 +40,10 @@ public class SdlProxyBuilder
private boolean preRegister;
private String sAppResumeHash;
private BaseTransportConfig mTransport;
- private List<Class<? extends SdlSecurityBase>> sdlSecList;
-
+ private List<Class<? extends SdlSecurityBase>> sdlSecList;
+ private ISdlServiceListener sdlServiceListener;
+
+
public static class Builder
{
// Required parameters
@@ -66,6 +69,7 @@ public class SdlProxyBuilder
private String sAppResumeHash = null;
private List<Class<? extends SdlSecurityBase>> sdlSecList = null;
private BaseTransportConfig mTransport; //Initialized in constructor
+ private ISdlServiceListener sdlServiceListener = null;
/**
* @deprecated Use Builder(IProxyListenerALM, String, String, Boolean, Context) instead
@@ -120,12 +124,15 @@ public class SdlProxyBuilder
{ mTransport = val; return this; }
public Builder setSdlSecurity(List<Class<? extends SdlSecurityBase>> val)
{ sdlSecList = val; return this; }
+ public Builder setSdlServiceListener(ISdlServiceListener val)
+ { sdlServiceListener = val; return this; }
public SdlProxyALM build() throws SdlException
{
SdlProxyBuilder obj = new SdlProxyBuilder(this);
SdlProxyALM proxy = new SdlProxyALM(obj.service,obj.listener,obj.sdlProxyConfigurationResources,obj.appName,obj.ttsChunks,obj.sShortAppName,obj.vrSynonyms,obj.isMediaApp,obj.sdlMessageVersion,obj.lang,obj.hmiLang,obj.vrAppHMITypes,obj.appId,obj.autoActivateID,obj.callbackToUIThread,obj.preRegister,obj.sAppResumeHash,obj.mTransport);
proxy.setSdlSecurityClassList(obj.sdlSecList);
+ proxy.setSdlServiceListener(obj.sdlServiceListener);
return proxy;
}
}
@@ -152,6 +159,7 @@ public class SdlProxyBuilder
sAppResumeHash = builder.sAppResumeHash;
mTransport = builder.mTransport;
sdlSecList = builder.sdlSecList;
+ sdlServiceListener = builder.sdlServiceListener;
}
}
diff --git a/sdl_android/src/main/java/com/smartdevicelink/proxy/interfaces/ISdlServiceListener.java b/sdl_android/src/main/java/com/smartdevicelink/proxy/interfaces/ISdlServiceListener.java
new file mode 100644
index 000000000..2e9323a61
--- /dev/null
+++ b/sdl_android/src/main/java/com/smartdevicelink/proxy/interfaces/ISdlServiceListener.java
@@ -0,0 +1,10 @@
+package com.smartdevicelink.proxy.interfaces;
+
+import com.smartdevicelink.protocol.enums.SessionType;
+
+public interface ISdlServiceListener {
+ public void onSdlServiceStarted(SessionType type, boolean isEncrypted);
+ public void onSdlServiceStartedNacked(SessionType type);
+ public void onSdlServiceEnded(SessionType type);
+ public void onSdlServiceEndedNacked(SessionType type);
+}
diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/StreamWriterThread.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/StreamWriterThread.java
new file mode 100644
index 000000000..84579e395
--- /dev/null
+++ b/sdl_android/src/main/java/com/smartdevicelink/streaming/StreamWriterThread.java
@@ -0,0 +1,130 @@
+package com.smartdevicelink.streaming;
+
+import com.smartdevicelink.SdlConnection.SdlSession;
+import com.smartdevicelink.protocol.ProtocolMessage;
+import com.smartdevicelink.protocol.enums.SessionType;
+import com.smartdevicelink.proxy.SdlProxyALM;
+
+public class StreamWriterThread extends Thread {
+ private Boolean isHalted = false;
+ private byte[] buf = null;
+ private Integer size = 0;
+ private final static Object lock = new Object();
+ private SessionType serviceType;
+ private SdlSession sdlSession;
+
+ private boolean isServiceProtected;
+ private final static int TLS_MAX_RECORD_SIZE = 16384;
+ private final static int TLS_RECORD_HEADER_SIZE = 5;
+ private final static int TLS_RECORD_MES_AUTH_CDE_SIZE = 32;
+ private final static int TLS_MAX_RECORD_PADDING_SIZE = 256;
+ private final static int ENC_READ_SIZE = TLS_MAX_RECORD_SIZE - TLS_RECORD_HEADER_SIZE - TLS_RECORD_MES_AUTH_CDE_SIZE - TLS_MAX_RECORD_PADDING_SIZE;
+
+
+ public StreamWriterThread(SdlProxyALM sdlProxy, SessionType serviceType) {
+ if (sdlProxy == null){
+ return;
+ }
+ sdlProxy.setVideoStreamWriter(this);
+ this.serviceType = serviceType;
+ }
+
+ public void handleSdlSession(SdlSession sdlSession){
+ if (sdlSession == null){
+ return;
+ }
+ this.sdlSession = sdlSession;
+ isServiceProtected = sdlSession.isServiceProtected(serviceType);
+ }
+
+ public byte[] getByteBuffer() {
+ synchronized (lock) {
+ return buf;
+ }
+ }
+
+ public void setByteBuffer(byte[] buf, Integer size) {
+ synchronized (lock) {
+ this.buf = buf;
+ this.size = size;
+ }
+ }
+
+ public void clearByteBuffer() {
+ synchronized (lock) {
+ try {
+ if (buf != null) {
+ buf = null;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private ProtocolMessage createStreamPacket(byte[] byteData) {
+ ProtocolMessage pm = new ProtocolMessage();
+ pm.setSessionID(sdlSession.getSessionId());
+ pm.setSessionType(serviceType);
+ pm.setFunctionID(0);
+ pm.setCorrID(0);
+ pm.setData(byteData, byteData.length);
+ pm.setPayloadProtected(isServiceProtected);
+ return pm;
+ }
+
+ public static byte[][] divideArray(byte[] source, int chunksize) {
+ byte[][] ret = new byte[(int)Math.ceil(source.length / (double)chunksize)][chunksize];
+
+ int start = 0;
+
+ for(int i = 0; i < ret.length; i++) {
+ if(start + chunksize > source.length) {
+ System.arraycopy(source, start, ret[i], 0, source.length - start);
+ } else {
+ System.arraycopy(source, start, ret[i], 0, chunksize);
+ }
+ start += chunksize ;
+ }
+
+ return ret;
+ }
+
+ private void writeToStream() {
+ synchronized (lock) {
+ if (buf == null || sdlSession == null) return;
+
+ try {
+ if (isServiceProtected && buf.length > ENC_READ_SIZE){
+ byte[][] ret = divideArray(buf, ENC_READ_SIZE);
+ for(int i = 0; i < ret.length; i++) {
+ byte[] byteData = ret[i];
+ ProtocolMessage pm = createStreamPacket(byteData);
+ sdlSession.sendStreamPacket(pm);
+ }
+ }
+ else {
+ ProtocolMessage pm = createStreamPacket(buf);
+ sdlSession.sendStreamPacket(pm);
+ }
+
+ clearByteBuffer();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ public void run() {
+ while (!isHalted) {
+ writeToStream();
+ }
+ }
+
+ /**
+ * Method that marks thread as halted.
+ */
+ public void halt() {
+ isHalted = true;
+ }
+}