Android BluetoothGattService - exception cannot marshall - java

I have got following problem.
My application architecture more or less consits of 3 main parts:
Devices view (shows available BLE devices)
BLE service (handles BLE connections and data)
Services view (shows services for particular device)
My issue is with passing BluetoothGATTService to the intent second time (first time it works).
More or less actions are like this:
choose device, for which you want to show services
send intent with device and action to perform to the BLEService
BLEService performs service discovery and sends intent with services back
BroadcastReceiver receives intent properly and wats to start new activity using data in received intent.
And on the last step there is a problem as I cannot put ArrayList to the intent/bundle as it causes exception:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.projects.dawid.gattclient, PID: 4133
java.lang.RuntimeException: Parcel: unable to marshal value android.bluetooth.BluetoothGattService#ef62956
at android.os.Parcel.writeValue(Parcel.java:1337)
at android.os.Parcel.writeList(Parcel.java:711)
at android.os.Parcel.writeValue(Parcel.java:1284)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:638)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1313)
at android.os.Bundle.writeToParcel(Bundle.java:1096)
at android.os.Parcel.writeBundle(Parcel.java:663)
at android.content.Intent.writeToParcel(Intent.java:7838)
at android.app.ActivityManagerProxy.startActivity(ActivityManagerNative.java:2530)
for me it's very very strange, as if the operation is unsupported, then we would get this exception on sending ArrayList of services from BLEService to the BroadcastReceiver, not now.
Source code:
BLEService to Broadcast receiver code
private void notifyServicesDiscovered(BluetoothGatt gatt) {
Intent intent = new Intent();
intent.setAction(BLEService.RESPONSE);
intent.putExtra(BLEService.RESPONSE, BLEService.Responses.SERVICES_DISCOVERED);
intent.putExtra(BLEService.Responses.DEVICE, gatt.getDevice());
intent.putParcelableArrayListExtra(BLEService.Responses.SERVICES_LIST, getArrayListServices(gatt));
LocalBroadcastManager.getInstance(mServiceContext).sendBroadcast(intent);
}
#NonNull
private ArrayList<BluetoothGattService> getArrayListServices(BluetoothGatt gatt) {
ArrayList<BluetoothGattService> services = new ArrayList<>();
services.addAll(gatt.getServices());
return services;
}
This works fine. Now
Broadcast receiver to new activity code
After recognizing the proper intent to handle this method is invoked
private void createViewWithServices(Intent intent) {
Log.i(TAG, "creating new activity!");
BluetoothDevice device = intent.getParcelableExtra(BLEService.Responses.DEVICE);
ArrayList<BluetoothGattService> services = intent.getParcelableArrayListExtra(BLEService.Responses.SERVICES_LIST);
Intent serviceShowIntent = new Intent(mActivityContext, ServiceShowActivity.class);
serviceShowIntent.putExtra(BLEService.Responses.DEVICE, device);
serviceShowIntent.putParcelableArrayListExtra(BLEService.Responses.SERVICES_LIST, services); //this line causes error
mActivityContext.startActivity(serviceShowIntent); // here exception is thrown
}
Can anyone explain me the mistery lying behind this? I just cannot understand why first code is just fine, while second fails with exception.
Already tried doing things in lots of different ways, but all of them failed. I was even exchanging bundles between intents as their contents are same, but this also failed, copying list items makes no difference.
EDIT
refering to the AndroidRuntime error: Parcel: unable to marshal value error. Difference is, that I am using objects of classes provided by android itself, that is BluetoothGATTService, which do implement parcelable interface refering to the https://developer.android.com/reference/android/bluetooth/BluetoothGattService.html

There are two issues in play here: one causes everything to work fine when local broadcasts are used, another one makes serialization of BluetoothGattService fail.
In order to pass data between two processes, it must be serialized (written to Parcel). Bundle contents are serialized/deserialized lazily — as long as Bundle object does not cross process boundaries or get stored on disk, all contained objects will be stored in memory (within a HashMap) and won't get serialized.
Local broadcasts never cross process boundaries, thus do not serialize/deserialize Intent extras. Inter-process communications (global broadcasts, asking ActivityManager to start an Activity) do.
In order to be passed between processes, Intent extras must be either Parcelable or Serializable, otherwise Android may treat them as opaque objects under some circumstances, and will attempt to determine approach to their serialization (as Serializable/Parcelable/String etc.) at runtime (see writeValue) — and fail.
As for BluetoothGattService — it clearly didn't implement Parcelable after initial public release and was retroactively changed to implement Parcelable in API 24. This means, that there are devices in the wild, where that class does not implement Parcelable (even if it does in the source code of the latest Android version). Adding a parent to inheritance chain is not technically a breaking change as far as binary compatibility is concerned — Java Classloaders won't complain. But any code, that relies on parcelability of such class, will either fail during bytecode validation or cause ClassCastException/ArrayStoreException on older versions.
Java generics are not reified — when compiled to binary code, ArrayList<String> and ArrayList<Parcelable> look to ClassLoader the same: as ArrayList<?>. You are not casting BluetoothGattService to Parcelable and neither is ArrayList (internally it stores it's contents in Object[]). In order to serialize ArrayList, HashMap and similar classes, Android have to use reflection, and that fails in your case, because the runtime type of BluetoothGattService does not implement Parcelable on the device. Just add a code, that explicitly makes a cast (such as placing BluetoothGattService in Parcelable[]) and you will see.
Also, even on devices, where it does happen to implement Parcelable prior to API 24, trying to serialize a BluetoothGattService is still bad idea (that wasn't officially public API, wasn't covered by CTS, and thus is completely untested).
I recommend you to refrain from passing BluetoothGattService via parcelization and find other way to approach your task (such as passing it's contents or storing an instance in singlton).

You can send BluetoothGattService through LocalBroadcastReceiver

Related

Multiple threads reading from same socket

I am developing an app which displays data from a server. The server is not mine and it is not very stable. Making too many connections crashes the server.
I have one socket to the server in my main activity, but at times I want to open sub activities which read the data and display it. My problem is that I am unable to achieve this with the same socket and have to open a new socket for every activity.
Every activity has a thread which does the reading from the socket and updates the UI elements on that activity as needed.
To use the same socket in multiple activities, I tried to close the inputReader of an activity before starting the new activity, but that simply make the application hang. If I leave it open, then the new thread in the new activity never receives any data. Killing the thread before starting the new activity is not possible because the thread is generally blocked by the read() function.
Is there anyway that I can have a centralized thread which does the reading and then sends the data to all the other threads in other activities so that I don't have to open new sockets in every activity?
I feel that this is a very basic thing that I am asking, but yet I am unable to find a solution.
A pretty straightforward and simple approach is the following:
You create a new Service which runs in the background and communicates with the server through your socket
The Service receives data from the socket and forwards/broadcasts it to all of your Activities which are interested in receiving it (for example to update the UI) by using the LocalBroadcastManager
All of your Activities implement a BroadcastReceiver and receive the data from your Service inside the onReceive() method
To accomplish that, you should read the introduction to Services and BroadcastReceivers to get an idea of how they work. Also to get a basic overview first, you should read about the available App Components.
EDIT, to answer the question in the comment:
You can always stop the Service by calling stopService() but you can also do it differently if you don't want/need all the functionality of a Service. Instead of a Service you could also create a simple Thread or a HandlerThread which communinicates with the server. From inside of your Thread, you can then forward/broadcast the data to your Activities by using the technique mentioned above (LocalBroadcastManager).
Just to give you an example of the basic structure (code untested though):
class SocketThread implements Runnable
{
static final String SOCKET_DATA_RECEIVED = "com.your.package.SOCKET_DATA_RECEIVED";
static final String SOCKET_DATA_IDENTIFIER = "com.your.package.SOCKET_DATA";
private Context context;
SocketThread(Context c) {
context = c.getApplicationContext();
}
#Override
public void run() { // code running in your thread
// fetch data from socket ...
Intent intent = new Intent();
intent.putExtra(SOCKET_DATA_IDENTIFIER, data); // store data in your intent
// send data to registered receivers
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
// your code ...
}
}
Then you have your Activities, for example MyActivity1, MyActivity2, ... MyActivityN. They all register their embedded SocketDataReceiver to receive the broadcast intent SOCKET_DATA_RECEIVED, which is sent by your thread.
Inside your onReceive() methods you can then extract the data from your intent object by using the identifier SOCKET_DATA_IDENTIFIER.
public class MyActivity1 extends Activity
{
private SocketDataReceiver socketDataReceiver;
#Override
protected void onResume() {
super.onResume();
socketDataReceiver = new SocketDataReceiver();
LocalBroadcastManager.getInstance(this).registerReceiver(
socketDataReceiver, new IntentFilter(SocketThread.SOCKET_DATA_RECEIVED));
}
#Override
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(socketDataReceiver);
}
private class SocketDataReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent) {
// intent contains your socket data,
// get data from intent using SocketThread.SOCKET_DATA_IDENTIFIER
}
}
}
Basically you answered your question yourself:
I can have a centralized thread which does the reading and then sends the data to all the other threads in other activities.
Meaning: of course, such a thing is possible. But you have to sit down, design and implement it. You would start by defining a reasonable interface that allows your other threads to communicate with that central service, something like:
enum RequestType { DO_THIS, DO_THAT };
interface ServerConnectionService<T> {
List<T> performRequest(RequestType request);
}
Meaning: instead of having your different threads do "low level" talking on that socket, you create an abstraction that allows you to say: "when I need this kind of information, then I use my service; and it returns some specific answer to me). Of course, this is a very generic answer, but well, your question isn't exactly specific either.
The next step would then be to have some central (maybe singleton) implementation of that interface; which runs on its own thread, and can be used by other threads in a synchronized, well-defined way.
Final word of warning: if you don't own that server, and it has low quality and is causing trouble for you - that is not a good setup. Because no matter what you do in your code, if the server doesn't do a good job, users will perceive your app to be the problem. Users don't care if an operation fails because some remote server crashes. So the other aspect in your question is: right now, you are in a bad spot. You should spent some serious time to find ways out of there. Otherwise you will be wasting a lot of time to build workarounds for that server you are dealing with.

Cordova - Retrieve local storage from Push Plugin java files

I am programming a mobile app using Cordova. I am implementing the push notifications using Push Plugin. The app is meant to run on all platforms but right now I am testing on Android and Windows.
In a particular javascript file I am saving a value call it 'category' in the localstorage:
localStorage.setItem("category", JSON.stringify(categoryarray));
Now when sending a push notification, the category is essential to decide whether to show the notification or not. If a user is subscribed to that particular category, then, the notification is to be shown, otherwise not. For this I simply create a condition and check whether the user has subscribed to the category included in the notification (but this is not really relevant to the point of the question). When the app is running this condition can be handled in javascript. When the app is not running, this is handled in java code:
else {
extras.putBoolean("foreground", false);
// Send a notification if there is a message
if (extras.getString("message") != null && extras.getString("message").length() != 0) {
createNotification(context, extras);
}
}
Now I want to get the value from the local storage at that instance that the notification is being pushed when the app is not running (and be able to check whether the notification should be shown or not).
I came into this link: Android Service reads localStorage?
But it seems to be meant for Android native code (reference to the webview). Apart from that I haven't really understood how it works and furthermore if it is applicable for my problem.
What do you suggest? How can I do it?
Edit: I didn't initially realise that the Push plugin java code won't be compiled with the Cordova app. So editing the code that is retrievable from the Cordova directory is in reality useless. Unless, someone can still suggest something, I know that this is an unanswerable question. Will have to re-attempt to create an API for this purpose and handle who to receive which notification at server side! (The reason why I resorted to this method was because I wasn't managing to create an API for notification purposes)
I didn't initially realise that the Push plugin java code won't be compiled with the Cordova app. So editing the code that is retrievable from the Cordova directory is in reality useless. Unless, someone can still suggest something, I know that this is an unanswerable question.
This isn't true, Cordova plugin code is compiled when you compile your Cordova app. All Cordova plugin's provide native source code that gets compiled into the app when you run cordova build (or cordova run <platform>).
If you wanted to solve this completely on the client side (rather than managing the categories that a user is subscribed to on the backend and only sending a notification if the user is subscribed to a category), you could extend the PushPlugin to manage subscriptions to categories.
As a rough sketch:
In PushNotification.js, add a method to subscribe to a channel:
PushNotification.prototype.subscribeToChannel(successCallback, errorCallback, channel) {
cordova.exec(successCallback, errorCallback, "PushPlugin", "subscribeToChannel", [{channel: channel}]);
}
In PushPlugin.java catch the subscribeToChannel action in the execute function:
public boolean execute(String action, JSONArray data, CallbackContext callbackContext) {
...
if ("subscribeToChannel".equals(action)) {
//get the attached data
JSONObject jo = data.getJSONObject(0);
String channel = (String) jo.get("channel");
addChannelToSubscriptions(channel);
}
...
}
public void addChannelToSubscriptions(String channel) {
//store as a list in a sharedpreferences list
}
Then when a notification is received, you can check if the channel is a channel that has been subscribed to.
// Send a notification if subscribed to the channel
if (extras.getString("channel") != null && isSubscribedTo(extras.getString("channel"))) {
createNotification(context, extras);
}
public boolean isSubscribedTo(String channel) {
//see if the channel is in the shared preferences.
}
Personally, I think it'd be easier to manage subscriptions on the backend as to manage it in the app, you'd have to implement this logic for each platform you support. It would be easier to just add a webservice call in your Javascript. As a further alternative, if you don't want to handle the subscription logic on your backend, you could look at a service like Parse where the concept of subscribing to channels is built into the service.

How to implement 3-way conference call video chat with WebRTC Native Code for Android?

I'm trying to implement 3-way video chat inside an Android app using the WebRTC Native Code package for Android (i.e. not using a WebView). I've written a signalling server using node.js and used the Gottox socket.io java client library inside the client app to connect to the server, exchange SDP packets and establish a 2-way video chat connection.
However now I'm having problems going beyond that to a 3-way call. The AppRTCDemo app that comes with the WebRTC native code package demonstrates 2-way calls only (if a 3rd party attempts to join a room a "room full" message is returned).
According to this answer (which doesn't relate to Android specifically), I'm supposed to do it by creating multiple PeerConnections, so each chat participant will connect to the 2 other participants.
However, when I create more than one PeerConnectionClient (a Java class which wraps a PeerConection, which is implemented on the native side in libjingle_peerconnection_so.so), there is an exception thrown from inside the library resulting from a conflict with both of them trying to access the camera:
E/VideoCapturerAndroid(21170): startCapture failed
E/VideoCapturerAndroid(21170): java.lang.RuntimeException: Fail to connect to camera service
E/VideoCapturerAndroid(21170): at android.hardware.Camera.native_setup(Native Method)
E/VideoCapturerAndroid(21170): at android.hardware.Camera.<init>(Camera.java:548)
E/VideoCapturerAndroid(21170): at android.hardware.Camera.open(Camera.java:389)
E/VideoCapturerAndroid(21170): at org.webrtc.VideoCapturerAndroid.startCaptureOnCameraThread(VideoCapturerAndroid.java:528)
E/VideoCapturerAndroid(21170): at org.webrtc.VideoCapturerAndroid.access$11(VideoCapturerAndroid.java:520)
E/VideoCapturerAndroid(21170): at org.webrtc.VideoCapturerAndroid$6.run(VideoCapturerAndroid.java:514)
E/VideoCapturerAndroid(21170): at android.os.Handler.handleCallback(Handler.java:733)
E/VideoCapturerAndroid(21170): at android.os.Handler.dispatchMessage(Handler.java:95)
E/VideoCapturerAndroid(21170): at android.os.Looper.loop(Looper.java:136)
E/VideoCapturerAndroid(21170): at org.webrtc.VideoCapturerAndroid$CameraThread.run(VideoCapturerAndroid.java:484)
This happens when initializing the local client even before attempting to establish a connection so it's not related to node.js, socket.io or any of the signalling server stuff.
How do I get multiple PeerConnections to share the camera so that I can send the same video to more than one peer?
One idea I had was to implement some kind of singleton camera class to replace VideoCapturerAndroid that could be shared between multiple connections, but I'm not even sure that would work and I'd like to know if there is a way to do 3-way calls using the API before I start hacking around inside the library.
Is it possible and if so, how?
Update:
I tried sharing a VideoCapturerAndroid object between multiple PeerConnectionClients, creating it for the first connection only and passing it into the initialization function for the subsequent ones, but that resulted in this "Capturer can only be taken once!" exception when creating a second VideoTrack from the VideoCapturer object for the second peer connection:
E/AndroidRuntime(18956): FATAL EXCEPTION: Thread-1397
E/AndroidRuntime(18956): java.lang.RuntimeException: Capturer can only be taken once!
E/AndroidRuntime(18956): at org.webrtc.VideoCapturer.takeNativeVideoCapturer(VideoCapturer.java:52)
E/AndroidRuntime(18956): at org.webrtc.PeerConnectionFactory.createVideoSource(PeerConnectionFactory.java:113)
E/AndroidRuntime(18956): at com.example.rtcapp.PeerConnectionClient.createVideoTrack(PeerConnectionClient.java:720)
E/AndroidRuntime(18956): at com.example.rtcapp.PeerConnectionClient.createPeerConnectionInternal(PeerConnectionClient.java:482)
E/AndroidRuntime(18956): at com.example.rtcapp.PeerConnectionClient.access$20(PeerConnectionClient.java:433)
E/AndroidRuntime(18956): at com.example.rtcapp.PeerConnectionClient$2.run(PeerConnectionClient.java:280)
E/AndroidRuntime(18956): at android.os.Handler.handleCallback(Handler.java:733)
E/AndroidRuntime(18956): at android.os.Handler.dispatchMessage(Handler.java:95)
E/AndroidRuntime(18956): at android.os.Looper.loop(Looper.java:136)
E/AndroidRuntime(18956): at com.example.rtcapp.LooperExecutor.run(LooperExecutor.java:56)
Attempting to share the VideoTrack object between PeerConnectionClients resulted in this error from the native code:
E/libjingle(19884): Local fingerprint does not match identity.
E/libjingle(19884): P2PTransportChannel::Connect: The ice_ufrag_ and the ice_pwd_ are not set.
E/libjingle(19884): Local fingerprint does not match identity.
E/libjingle(19884): Failed to set local offer sdp: Failed to push down transport description: Local fingerprint does not match identity.
Sharing the MediaStream between PeerConnectionClients results in the app abruptly closing, without any error message appearing in the Logcat.
The problem you are having is that PeerConnectionClient is not a wrapper around PeerConnection it contains a PeerConnection.
I noticed this question wasn't answered so I wanted to see if I could help out a bit. I looked into the source code and PeerConnectionClient is very much hard coded for a single remote peer. You would need to create a collection of PeerConnection objects rather then this line:
private PeerConnection peerConnection;
If you look around a bit more you would notice that it gets a bit more complicated then that.
The mediaStream logic in createPeerConnectionInternal should only be done once and you need to share the stream between your PeerConnection objects like this:
peerConnection.addStream(mediaStream);
You can consult the WebRTC spec or take a look at this stackoverflow question to confirm that the PeerConnection type was designed to only handle one peer. It is also somewhat vaguely implied here.
So you only maintain one mediaStream object:
private MediaStream mediaStream;
So again the main idea is one MediaStream object and as many PeerConnection objects as you have peers you want to connect to. So you will not be using multiple PeerConnectionClient objects, but rather modify the single PeerConnectionClient to encapsulate the multi-client handling. If you do want to go with a design of multiple PeerConnectionClient objects for whatever reason you would just have to abstract the media stream logic (and any support types that should only be created once) out of it.
You will also need to maintain multiple remote video tracks rather then the existing one:
private VideoTrack remoteVideoTrack;
You would obviously only care to render the one local camera and create multiple renderers for the remote connections.
I hope this is enough information to get you back on track.
With the help of Matthew Sanders' answer I managed to get it working, so in this answer I'm going to describe in more detail one way of adapting the sample code to support video conference calling:
Most of the changes need to be made in PeerConnectionClient, but also in the class that uses PeerConnectionClient, which is where you communicate with the signalling server and set up the connections.
Inside PeerConnectionClient, the following member variables need to be stored per-connection:
private VideoRenderer.Callbacks remoteRender;
private final PCObserver pcObserver = new PCObserver();
private final SDPObserver sdpObserver = new SDPObserver();
private PeerConnection peerConnection;
private LinkedList<IceCandidate> queuedRemoteCandidates;
private boolean isInitiator;
private SessionDescription localSdp;
private VideoTrack remoteVideoTrack;
In my application I needed a maximum of 3 connections (for a 4-way chat), so I just stored an array of each, but you could put them all inside an object and have an array of objects.
private static final int MAX_CONNECTIONS = 3;
private VideoRenderer.Callbacks[] remoteRenders;
private final PCObserver[] pcObservers = new PCObserver[MAX_CONNECTIONS];
private final SDPObserver[] sdpObservers = new SDPObserver[MAX_CONNECTIONS];
private PeerConnection[] peerConnections = new PeerConnection[MAX_CONNECTIONS];
private LinkedList<IceCandidate>[] queuedRemoteCandidateLists = new LinkedList[MAX_CONNECTIONS];
private boolean[] isConnectionInitiator = new boolean[MAX_CONNECTIONS];
private SessionDescription[] localSdps = new SessionDescription[MAX_CONNECTIONS];
private VideoTrack[] remoteVideoTracks = new VideoTrack[MAX_CONNECTIONS];
I added a connectionId field to the PCObserver and SDPObserver classes, and inside the PeerConnectionClient constructor I allocated the observer objects in the array and set the connectionId field for each observer object to its index in the array. All the methods of PCObserver and SDPObserver that reference the member variables listed above should be changed to index into the appropriate array using the connectionId field.
The PeerConnectionClient callbacks need to be changed:
public static interface PeerConnectionEvents {
public void onLocalDescription(final SessionDescription sdp, int connectionId);
public void onIceCandidate(final IceCandidate candidate, int connectionId);
public void onIceConnected(int connectionId);
public void onIceDisconnected(int connectionId);
public void onPeerConnectionClosed(int connectionId);
public void onPeerConnectionStatsReady(final StatsReport[] reports);
public void onPeerConnectionError(final String description);
}
And also the following PeerConnectionClient methods:
private void createPeerConnectionInternal(int connectionId)
private void closeConnectionInternal(int connectionId)
private void getStats(int connectionId)
public void createOffer(final int connectionId)
public void createAnswer(final int connectionId)
public void addRemoteIceCandidate(final IceCandidate candidate, final int connectionId)
public void setRemoteDescription(final SessionDescription sdp, final int connectionId)
private void drainCandidates(int connectionId)
As with the methods in the observer classes, all these functions need to be changed to use the connectionId to index into the appropriate array of per-connection objects, instead of referencing the single objects they were previously. Any invocations of callback functions need to also be changed to pass the connectionId back.
I replaced createPeerConnection with a new function called createMultiPeerConnection, which is passed an array of VideoRenderer.Callbacks objects for displaying the remote video stream, instead of a single one. The function calls createMediaConstraintsInternal() once and createPeerConnectionInternal() for each of the PeerConnections, looping from 0 to MAX_CONNECTIONS - 1. The mediaStream object is created only on the first call to createPeerConnectionInternal(), just by wrapping the initialization code in an if(mediaStream == null) check.
One complication I encountered was when when the app shuts down and the PeerConnection instances are closed and the MediaStream disposed of. In the sample code the mediaStream is added to a PeerConnection using addStream(mediaStream), but the corresponding removeStream(mediaStream) function is never called (dispose() is called instead). However this creates problems (a ref count assert in MediaStreamInterface in the native code) when there are more than one PeerConnection sharing a MediaStream object because dispose() finalizes the MediaStream, which should only happen when the last PeerConnection is closed. Calling removeStream() and close() is not enough either, because it doesn't fully shut down the PeerConnection and this leads to an assert crash when disposing the PeerConnectionFactory object. The only fix I could find was to add the following code to the PeerConnection class:
public void freeConnection()
{
localStreams.clear();
freePeerConnection(nativePeerConnection);
freeObserver(nativeObserver);
}
And then calling these functions when finalizing each PeerConnection except the last:
peerConnections[connectionId].removeStream(mediaStream);
peerConnections[connectionId].close();
peerConnections[connectionId].freeConnection();
peerConnections[connectionId] = null;
and shutting down the last one like this:
peerConnections[connectionId].dispose();
peerConnections[connectionId] = null;
After modifying PeerConnectionClient, it's necessary to change the signalling code to set up the connections in the right order, passing in the correct connection index to each of the functions and handling the callbacks appropriately. I did this by maintaining a hash between socket.io socket ids and a connection id. When a new client joins the room, each of the existing members sends an offer to the new client and receives an answer in turn. It's also necessary to initialize multiple VideoRenderer.Callbacks objects, pass them in to the PeerConnectionClient instance, and divide up the screen however you want for a conference call.

How to queue up data for server dispatch on android

I am working on an android app with an email feature. I want my users to be able to compose and send emails while in airplane mode. For that I need some sort of queue that can check if there is network and send, etc. I image this must have been done 100s of times. But I am not really sure why my searches aren't turning up much. Does anyone know of a library or git project that I can use to accomplish this? If not, does anyone know how to accomplish this?
I believe it is called the Queue and send pattern.
Update
I am starting a bounty on this question. What I hope for is a working example that does not use SMS. For my particular case I am working on an Appengine Connected Android Project. The client needs to send data (String, Bitmap, etc under a particular POJO say Dog) to the server. I want to be able to queue up these data somehow. I can use Gson to save data to file, etc. The bottom line is that I need to be able to check for network. When there is network I dequeue my queue into the server. If there is no network, I keep saving into the queue.
My queue can be Queue<Dog>, where Dog is my class with fields such as Bitmap (or path to image), String, long, etc.
I am looking for a working example. It can be very simple, but the example must work. A git zip would be great. I am giving up close to half of my points for this question.
class Dog{
String dogname;
String pathToImage;
int dogAge;
//etc.
}
//Design pattern for sending Dog to server
0) Unmarshall queue from file using Gson
1) Add dog to queue
2) If there is network, loop through queue and send data to server
3) if there is no network save queue to file
//Ideally, as soon as there is network, the method should be able to detect so and run to send data to server
First you need to set up a receiver to watch the wifi connection to see when they have data, you could also check for normal 3g/4g connections and make a broadcast receiver for that as well. todo this let use implement a broadcast receiver for connection status changes. put something like this in the manifest in the application tag
<receiver android:name=".NetworkChangeReceiver" >
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
now we need to make the receiver we just defined in the manifest
public class NetworkChangeReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//here, check that the network connection is available. If yes, start your email service. If not, stop your email service.
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
if (info != null) {
if (info.isConnected()) {
//start service
Intent intent = new Intent(this, ItemServiceManager.class);
startService(intent);
}
else {
//stop service
Intent intent = new Intent(this, ItemServiceManager.class);
stopService(intent);
}
}
}
}
What this does is puts a big fat antenna called NetworkChangeReceiver out in android land, that is fine tuned to listen in on when android has something to say about a change in the data connection status.
now you need to build your ItemServiceManager.class which should read from a database (it should also extend Service. It should choose the oldest item in the database, (email it, text it, upload to server, whatever), and if the connection was successful then remove the item from the database, and load the next oldest one. If there is no more then close the service and the broadcast receiver.
If you have a connection and the user needs to send more data, then add it to the database, and then make sure the service is started. Maybe notify it that it should double check the database (after a few seconds) before deciding it can close because nothing is there.
This is how you might disable your broadcast receiver.
PackageManager packageManager = context.getPackageManager();
ComponentName componentName = new ComponentName(context, NetworkChangeReceiver.class);
packageManager.setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
When a new item is to be uploaded, if there is no web connection, the email should be saved to the database and the broadcast receiver should be started to know when internet is back so it can know when to upload. You might start it up like this.
PackageManager packageManager = context.getPackageManager();
ComponentName componentName = new ComponentName(context, NetworkChangeReceiver.class);
packageManager.setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_ENABLED,PackageManager.DONT_KILL_APP);
The whole point is you only care about connection broadcasts when you have something stored to be uploaded but can not upload it because of no data connection. When you have nothing to upload, don't waste processing and battery by keeping your receiver/service around. And when you do have emails waiting, then start up you broadcastreceiver, to know when you have data connection so that you can start uploading.
I do not think anyone is going to write a whole working solution for you, hopefully this is more than enough to get you on your way.
Edit:
Another thing you can do, is let the server allow acceptance of an array of your items, that way you can just upload it all at once when you get a valid connection. Generally you would do this if each item was decently small. But if you are uploading pictures or videos or anything large, best to do it one at a time probably.

Android c2dm service

Trying to implement google C2DM service.
registrationIntent.putExtra("app", PendingIntent.getBroadcast(context,0,new Intent(), 0));
registrationIntent.putExtra("sender","example#gmail.com");
context.startService(registrationIntent);
Almost every tutorial features this line of code. Is this a service that I must code? or does Android know how to handle this type of Intent. I am calling this method from a helper class with the default constructor. I pass the current Context to the this above method to create the registration Intent. Anyone have some insight on how this works or where my program will go?
No, you don't have to write a service. You need to send an intent to a Google's service. You've omitted the first line, where the intent is created, and that contains the service name. It's something like this typically:
    Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER");
The line com.google.android.c2dm.intent.REGISTER identifies the Google's service.
Now, you still have to write the broadcast receiver that'll receive the registration result (either an ID or error). And a receiver to receive the actual C2DM messages.
You left out the most interesting piece of code
Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER");
Is this a service that I must code? or does Android know how to handle this type of Intent.
Note that the intent is in the com.google.android.c2dm domain. The Android C2DM implementation on the device knows how to handle this intent and you will be starting its own service to process it.

Categories