Friday, March 23, 2012

Android c2DM with php

First I would like to describe the need of c2dm.Basically c2dm stands for cloud to device messaging is a service provided by Google for notification of frequent updates.I describe a example which needs for c2dm. Suppose we have a e-commerce site on which we have a android mobile application which synchronize with all website work.and In this mobile application we want that when admin of website add some product or does some update then mobile app get some notification of having update and change their data accordingly.So In android we can do this by two way.
(1)one way is we can run a background thread during application run time,and checked periodically on server here there is any update or not.If it found some update then show some notification and look up for update.but there is a problem in this approach.problem is on which period we will check for update.it will spoil device battery life and busy many resources of device.So this is not feasible in my point of view.If we don't have option then we can go through this also.But fortunately there is an option for this provided by google that is called c2dm.
(2)the second way is c2dm from google lab.I want to discuss the limitation of c2dm first then I will describe in detail.the main limitation is we should check this on device only having os version greater than or equal to android os 2.2(froyo).
Now we should follow certain steps to complete our demo.
(a)Registration for c2dm: The first step is to register a valid gmail id,application package name,contact mail on http://code.google.com/android/c2dm/signup.html then you will recieve a noreply mail for confirmation of this registration.We can increase our message quota also on sending mail to c2dm team.
(b)Role of Third Party Application server: Now we need an application server having ssl certificate that mean a server which support https capability .We can write server side in any technology which your server support.but I have written my server code in php so I will provide the code for server side.this is test.php,suppose this is on site https://www.Loremipsum.com/
/**
*
* @package c2dm
* @version $Id$
* @copyright (c) 2011 lytsing.org
* Description: C2DM implementation PHP code
* refer to: http://stackoverflow.com/questions/4121508/c2dm-implementation-php-code
*
* 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.
*
*/

$ob=new c2dm();
$ob->getAuthToken(your registered gmail username,password of mail);
$ob->handleMultiCient();
class c2dm {
var $authString = "";

/**
* Get Google login auth token
* @see http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html
*/
function getAuthToken($username,$password) {

// Initialize the curl object
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, "https://www.google.com/accounts/ClientLogin");
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);

$data = array(
'Email' => $username ,
'Passwd' => $password ,
'accountType' => 'HOSTED_OR_GOOGLE',
'source' => 'Company-AppName-Version',
'service' => 'ac2dm');

curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);

$response = curl_exec($curl);
curl_close($curl);
echo $response;
// Get the Auth string
preg_match("/Auth=([a-z0-9_\-]+)/i", $response, $matches);
$this->authString = $matches[1];
}
/*
*Multiple Users sending
*/
function handleMultiCient()
{
$fp=fopen("imp.txt","r");
$content=fread($fp,filesize("imp.txt"));
$arr=explode(",",$content);
array_pop($arr);
foreach($arr as $temp)
{
$this->sendMessage($temp,1);
}
}
/**
* Send HTTP POST data form
*/
function sendMessage($deviceRegistrationId, $msgType) {
$headers[] = 'Authorization: GoogleLogin auth='.$this->authString;
$data = array(
'registration_id' =$deviceRegistrationId,
'collapse_key' = 1,
'data.message' = 'Hello, c2dm developed by Ashwani' //Add message here.
);
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, "https://android.apis.google.com/c2dm/send");
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
$response = curl_exec($curl);
curl_close($curl);
return $response;
}
}
?>;
Now create a second page on server for storing registration id of devices in file,we can store this in database also,but I store in file named imp.txt .I put this file name as credential.php.
$registration_id=$_REQUEST["registration_id"].',';
$fp=fopen("imp.txt","a");
fwrite($fp,$registration_id);
fclose($fp);
?>
(C)Android coding for c2dm: Now create the android project having same package name that is used for registration of c2dm.Now please write the code given below in your main activity.

package x.y.z;//your package name(For example I am using x.y.z for whole android coding)

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;

public class Dummyc2dmActivity extends Activity {
/** Called when the activity is first created. */
String s="a@a.a"//you registered gmail id that is used in c2dm registration and php
@Override
public void onCreate(Bundle b)
{
super.onCreate(b);
setContentView(R.layout.main);
}
public void registerIt(View v)
{
Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER");
registrationIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0)); // boilerplate
registrationIntent.putExtra("sender",s);
startService(registrationIntent);
}
}

Now create a receiver,Here is the code

package x.y.z;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerPNames;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.SingleClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Vibrator;
import android.widget.Toast;
public class C2DMReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) {
String registration = intent.getStringExtra("registration_id");
if (intent.getStringExtra("error") != null) {
// handle an error for registration
} else if (intent.getStringExtra("unregistered") != null) {
// call service to handle unregistration
} else if (registration != null) {
HttpPost hg=new HttpPost(" https://www.Loremipsum.com/ credential.php?registration_id="+registration);//path of page that you created in php to store credential
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", new EasySSLSocketFactory(), 443));
HttpParams params = new BasicHttpParams();
params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 30);
params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(30));
params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
ClientConnectionManager cm = new SingleClientConnManager(params, schemeRegistry);
HttpClient hc = new DefaultHttpClient(cm, params);
try {
HttpResponse hr=hc.execute(hg);
InputStream is=hr.getEntity().getContent();
BufferedReader br=new BufferedReader(new InputStreamReader(is));
String s="";
while((s=br.readLine())!=null)
{
Toast.makeText(context,"registered",2000).show();
}
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// call service to handle registration
}
} else if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) {
// we are just going to say a few fields already exist here, but this is based
// on what you may be sending from above in your data fields
String message=intent.getExtras().getString("message");
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(5000);
Toast.makeText(context, message, 15000).show();
}
}
}

Now we add two utilities file for ignoring SSLpeerunverified exception in our project,the first file code is given below
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;

import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.scheme.LayeredSocketFactory;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

/**
* This socket factory will create ssl socket that accepts self signed
* certificate
*
* @author olamy
* @version $Id: EasySSLSocketFactory.java 765355 2009-04-15 20:59:07Z evenisse
* $
* @since 1.2.3
*/
public class EasySSLSocketFactory implements LayeredSocketFactory {

private SSLContext sslcontext = null;

private static SSLContext createEasySSLContext() throws IOException {
try {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[] { new EasyX509TrustManager(
null) }, null);
return context;
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}

private SSLContext getSSLContext() throws IOException {
if (this.sslcontext == null) {
this.sslcontext = createEasySSLContext();
}
return this.sslcontext;
}

/**
* @see org.apache.http.conn.scheme.SocketFactory#connectSocket(java.net.Socket,
* java.lang.String, int, java.net.InetAddress, int,
* org.apache.http.params.HttpParams)
*/
public Socket connectSocket(Socket sock, String host, int port,
InetAddress localAddress, int localPort, HttpParams params)
throws IOException, UnknownHostException, ConnectTimeoutException {
int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
int soTimeout = HttpConnectionParams.getSoTimeout(params);

InetSocketAddress remoteAddress = new InetSocketAddress(host, port);
SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket());

if ((localAddress != null) || (localPort > 0)) {
// we need to bind explicitly
if (localPort < 0) {
localPort = 0; // indicates "any"
}
InetSocketAddress isa = new InetSocketAddress(localAddress,
localPort);
sslsock.bind(isa);
}

sslsock.connect(remoteAddress, connTimeout);
sslsock.setSoTimeout(soTimeout);
return sslsock;

}

/**
* @see org.apache.http.conn.scheme.SocketFactory#createSocket()
*/
public Socket createSocket() throws IOException {
return getSSLContext().getSocketFactory().createSocket();
}
/**
* @see org.apache.http.conn.scheme.SocketFactory#isSecure(java.net.Socket)
*/
public boolean isSecure(Socket socket) throws IllegalArgumentException {
return true;
}

/**
* @see org.apache.http.conn.scheme.LayeredSocketFactory#createSocket(java.net.Socket,
* java.lang.String, int, boolean)
*/
public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
throws IOException, UnknownHostException {
//return getSSLContext().getSocketFactory().createSocket();
return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
}
// -------------------------------------------------------------------
// javadoc in org.apache.http.conn.scheme.SocketFactory says :
// Both Object.equals() and Object.hashCode() must be overridden
// for the correct operation of some connection managers
// -------------------------------------------------------------------

public boolean equals(Object obj) {
return ((obj != null) && obj.getClass().equals(
EasySSLSocketFactory.class));
}

public int hashCode() {
return EasySSLSocketFactory.class.hashCode();
}

}


then after the second file code is now:


/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/

import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

/**
* @author olamy
* @version $Id: EasyX509TrustManager.java 765355 2009-04-15 20:59:07Z evenisse $
* @since 1.2.3
*/
public class EasyX509TrustManager
implements X509TrustManager
{

private X509TrustManager standardTrustManager = null;

/**
* Constructor for EasyX509TrustManager.
*/
public EasyX509TrustManager( KeyStore keystore )
throws NoSuchAlgorithmException, KeyStoreException
{
super();
TrustManagerFactory factory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() );
factory.init( keystore );
TrustManager[] trustmanagers = factory.getTrustManagers();
if ( trustmanagers.length == 0 )
{
throw new NoSuchAlgorithmException( "no trust manager found" );
}
this.standardTrustManager = (X509TrustManager) trustmanagers[0];
}

/**
* @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],String authType)
*/
public void checkClientTrusted( X509Certificate[] certificates, String authType )
throws CertificateException
{
standardTrustManager.checkClientTrusted( certificates, authType );
}

/**
* @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],String authType)
*/
public void checkServerTrusted( X509Certificate[] certificates, String authType )
throws CertificateException
{
if ( ( certificates != null ) && ( certificates.length == 1 ) )
{
certificates[0].checkValidity();
}
else
{
standardTrustManager.checkServerTrusted( certificates, authType );
}
}

/**
* @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
*/
public X509Certificate[] getAcceptedIssuers()
{
return this.standardTrustManager.getAcceptedIssuers();
}

}
Now we have androidmanifest.xml file.please if you will find any html tag like br,span,<,> then kindly omit this from file.paste this in your mainfest file.



Now write the code for main.xml that is given below.

please omit html tag specially br tag from this code and copy this into main.xml.
then run this on android device having os more than 2.2 or equal to 2.2.you can run this on emulator having AVD image 2.2 or more.For emulator and device both have already android market installed already,and both have sync with valid gmail id.then run the application on device,and click register button.Now run the main php file from server i.e. https://www.loremipsum.com/test.php and you will see a message and vibration on device and only message on emultor will appear.that's the full demo of c2dm including PHP.In Real world we will not have to send original data,only notification have to send that please cache all the data from server.then fetch the original data it will save the battery of device and optimize the use of device resources .that's all about for this blog from my side.
please comment on this blog and feel free to any query.

Thanks:

Tuesday, March 6, 2012

Synchronization in java

Almost any fresher and intermediate level java developer find the most difficult thing to understand in java is Multithreading,So I want to clarify some more typical part in Multithreading that cover synchronization as per my understanding.So First I will give explanation of Some methods in multithreading,then I will discuss Concurrency problem.Now we will discuss some of the methods:
1.Sleep():this is method that use to sleep the current thread for specified time in millisecond,this is static method in Thread class,if current thread is sleeping and again we call sleep method then it will throw Interrupted Exception,So Basically it is used to remove the current thread from runnable state for specified period of time.But it still hold the lock on object.the syntax is:
try
{
Thread.sleep(5000);
}
catch(Exception e)
{
}

2.wait():T
his is a method available in object class.this method is used to wait the current thread until some another thread notify.after executing this method object lock is released,this is main method that used in Synchronization.Always used wait in synchronized method or block.If we use it outside synchronized then it will return IllegalMonitorStateException because of Race condition,you can get good useful about Race condition on link http://java.sun.com/developer/Books/performance2/chap3.pdf there is different version of wait method available also, that take a time parameter,but it will not guarantee of waiting on that much time.
3.notify(): This method is also available in object Class.This method is used to notify the thread that is waiting in waiting Queue,the notified thread depend on underlying OS.Some OS notify of thread having same priority as running thread,some uses algorithm like Round robin and any thing else.So try to avoid that,Instead of notify call notifyAll() method,this should be also called from synchronized method or block.this method also release the lock
4.notifyAll():This method is also available in object class,this notifies all the thread waiting for notification in waiting Queue irrespect of their underlying platform like in notify.So I insist to use this in place of notify.This should also be used in synchronized block or method,because of same condition like in wait method. this method also release the lock.
5.join(): This method is available in Thread class.if there is two threads like a main thread and thread t1.and t1 is calling join method then the thread (Main Thread)that runs the thread (t1)that is calling join method will wait for
t1 to finish.this is implemented internally in jdk5 and jdk6 in different way,specially for windows.It is not in scope of this blog.For more internal specification see oracle docs.
6.yield():this method remove the thread status from running to idle state,Frankly speaking Don't use this method
in your real world application because you don't know when CPU resume that thread,The only purpose to distract its state,I did not find any useful practical implementation in my programming.So if anyone know the special uses please give comment.
Now on behalf of this knowledge we elaborate a synchronization problem .I will give you a complete code you understand it clearly.
First the Main Class Run.java
public class Run {

/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Item i=new Item();
Producer p=new Producer(i);
Consumer q=new Consumer(i);
//new Thread(p).start();
//new Thread(q).start();
}
}
then the next Producer.java class:
import java.util.Random;
public class Producer implements Runnable {
Item _item;
public Producer(Item i)
{
_item=i;
new Thread(this).start();
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true)
_item.put(new Random().nextInt(_item.s.length));
}
Now Consumer.java:
public class Consumer implements Runnable {
Item temp;
public Consumer(Item i)
{
temp=i;
new Thread(this).start();
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true)
temp.get();
}

}
}
Now the Most important classs Item.java:

public class Item {
String[]s={"Ashwani","Abhishek","Ashu","james","simple","john"};
boolean set=true;
String temp;
public void get()
{
synchronized (this) {
try{
if(!set)
{
System.out.println("Get Value:"+temp);
set=true;
notifyAll();
}
else
//System.out.print("Performance hit in Consumer"+"\n");
wait();
}
catch(Exception e)
{
}
}
}
public void put(int k)
{
synchronized (this) {
try {
if(set)
{
temp=s[k];
System.out.println("Set Value"+temp);
set=false;
notifyAll();
}
else
wait();
// System.out.print("Performance hit in Producer"+"\n");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

then if you run the Run.java you will see the synchronized value.This is all about synchronization in java.Any comment or suggestion is highly appreciated.

Thanks: