I have the registration id and auth token for c2dm. And then I pass store these values in db. and using php, i could send one message to c2dm server. But my problem is I dont know how to receive the message in the application. I am not sure whether my way of getting the message is correct or not. Anyway i will give it below.
I have one activity which registers to the c2dm using registration intent. and one receiver to receive the reg_id and notification message. it is registering with c2dm and not to receive message.
manifest
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION"></action>
<category android:name="my.android.c2dm"></category>
</intent-filter>
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE"></action>
<category android:name="my.android.c2dm"></category>
</intent-filter>
</receiver>
</application>
C2dmRegistration.class (activity)
Intent objRegIntnet=new Intent("com.google.android.c2dm.intent.REGISTER");
objRegIntnet.putExtra("app",PendingIntent.getBroadcast(this,0,new Intent(),0));
objRegIntnet.putExtra("sender","mymail#gmail.com");
startService(objRegIntnet);
c2dmReceiver
public class c2dmReceiver extends BroadcastReceiver
{
private static String KEY = "c2dmPref";
private static String REGISTRATION_KEY = "registrationKey";
private Context context;
#Override
public void onReceive(Context context, Intent intent)
{
this.context = context;
if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION"))
{
handleRegistration(context, intent);
}
else if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE"))
{
handleMessage(context, intent);
}
}
private void handleRegistration(Context context, Intent intent)
{
//handles registeration
}
private void handleMessage(Context context, Intent intent)
{
String title= intent.getStringExtra("title");
String message= intent.getStringExtra("msg");
Toast.makeText(context,"title : "+title+"\n message : "+message,1).show();
//Do whatever you want with the message
}
please tell what is the mistake i have done...
UPDATE
Hi all, the same code is woring for me today. The mistake i have done is with php code. instaed of passing the values as POST, i sent it was as GET. When I changed it to POST, the toast message is showing. but yet some problems are there.
The title, and msg values are null here.
my php code is :
function sendMessageToPhone($authCode, $deviceRegistrationId, $msgType, $messageText)
{
//$messageText="have a nice day";
//$msgtype="important";
$headers = array('Authorization: GoogleLogin auth=' . $authCode);
$data = array(
'registration_id' => $deviceRegistrationId,
'collapse_key' => $msgType,
'data.message' => $messageText
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://android.apis.google.com/c2dm/send");
if ($headers)
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
Actually i am not sure what type of values should use for collapse_key, and data.message variables.
Please help me...
Thank you...
Finally I found the way of giving collapse_key and and data..
collapse_key should be a string which is a name for a group of messages or a a parthicular type of messages. If we send more than one message with same collapse_key, the latest message will be sent to the device from c2dm server.
Example : $collapse_key = "important";
And the data. is the important thing. This will contain the message that we want to send.
Ex: if we want to send a message "Have a nice day", then i should give a key name to it.
data.="Have a nice day";
here 'wishes' is the key. And in receiver, i should retreive the message with the same key name.
private void handleMessage(Context context, Intent intent)
{
String mywish= intent.getStringExtra("wishes");
Toast.makeText(context,"my wishes : "+mywish,1).show();
}
Sorry to all..
This is my code I have use it for receiving notification form C2DM server, it also shows the notification on notification bar. It is running find you can compare your code with my code and correct the error if any .
I hope this is help.
/**
* Base class for C2D message receiver. Includes constants for the strings used
* in the protocol.
*/
public abstract class C2DMBaseReceiver extends IntentService {
private static final String C2DM_RETRY = "com.google.android.c2dm.intent.RETRY";
public static final String REGISTRATION_CALLBACK_INTENT = "com.google.android.c2dm.intent.REGISTRATION";
private static final String C2DM_INTENT = "com.google.android.c2dm.intent.RECEIVE";
// Logging tag
private static final String TAG = "C2DM";
// Extras in the registration callback intents.
public static final String EXTRA_UNREGISTERED = "unregistered";
public static final String EXTRA_ERROR = "error";
public static final String EXTRA_REGISTRATION_ID = "registration_id";
public static final String ERR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE";
public static final String ERR_ACCOUNT_MISSING = "ACCOUNT_MISSING";
public static final String ERR_AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED";
public static final String ERR_TOO_MANY_REGISTRATIONS = "TOO_MANY_REGISTRATIONS";
public static final String ERR_INVALID_PARAMETERS = "INVALID_PARAMETERS";
public static final String ERR_INVALID_SENDER = "INVALID_SENDER";
public static final String ERR_PHONE_REGISTRATION_ERROR = "PHONE_REGISTRATION_ERROR";
// wakelock
private static final String WAKELOCK_KEY = "C2DM_LIB";
private static PowerManager.WakeLock mWakeLock;
private final String senderId;
/**
* The C2DMReceiver class must create a no-arg constructor and pass the
* sender id to be used for registration.
*/
public C2DMBaseReceiver(String senderId) {
// senderId is used as base name for threads, etc.
super(senderId);
this.senderId = senderId;
}
/**
* Called when a cloud message has been received.
*/
protected abstract void onMessage(Context context, Intent intent);
/**
* Called on registration error. Override to provide better error messages.
*
* This is called in the context of a Service - no dialog or UI.
*/
public abstract void onError(Context context, String errorId);
/**
* Called when a registration token has been received.
*/
public void onRegistrered(Context context, String registrationId)
throws IOException {
}
/**
* Called when the device has been unregistered.
*/
public void onUnregistered(Context context) {
}
#Override
public final void onHandleIntent(Intent intent) {
try {
Context context = getApplicationContext();
if (intent.getAction().equals(REGISTRATION_CALLBACK_INTENT)) {
handleRegistration(context, intent);
} else if (intent.getAction().equals(C2DM_INTENT)) {
//**C2DM Start
Bundle extras = intent.getExtras();
String pushNo =extras != null ? extras.getString("pushNo"):"";
String scoreId =extras != null ? extras.getString("scoreId"):"";
String notfId =extras != null ? extras.getString("notfId"):"";
String fromId =extras != null ? extras.getString("fromId"):"";
String toId =extras != null ? extras.getString("toId"):"";
String matchId =extras != null ? extras.getString("matchId"):"";
String msg =extras != null ? extras.getString("msg"):"";
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
int icon = R.drawable.icon;
CharSequence tickerText = "Notification Receive";
long when = System.currentTimeMillis();
Notification notification = new Notification(icon, tickerText, when);
Context context1 = context;
//*****************
final int CUSTOM_VIEW_ID = 1;
RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.custom_notification_layout);
contentView.setImageViewResource(R.id.image, R.drawable.icon);
contentView.setTextViewText(R.id.text, "Racquetime \n"+msg);
notification.contentView = contentView;
notification.defaults=Notification.FLAG_ONLY_ALERT_ONCE+Notification.FLAG_AUTO_CANCEL;
Intent notificationIntent;
if(GUIStatics.boolLoginStatus)
{
notificationIntent = new Intent(this,ShowAllNotificationActiviry.class);
}
else{
notificationIntent = new Intent(this, HomeActivity.class);
}
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_SOUND;
notification.flags |= Notification.FLAG_SHOW_LIGHTS;
notificationIntent.putExtra("Tag", "C2DMBaseReceiver");
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.contentIntent = contentIntent;
mNotificationManager.notify(CUSTOM_VIEW_ID, notification);
//**C2DM End
// onMessage(context, intent);
} else if (intent.getAction().equals(C2DM_RETRY)) {
C2DMessaging.register(context, senderId);
}
} finally {
// Release the power lock, so phone can get back to sleep.
// The lock is reference counted by default, so multiple
// messages are ok.
// If the onMessage() needs to spawn a thread or do something else,
// it should use it's own lock.
mWakeLock.release();
}
}
/**
* Called from the broadcast receiver. Will process the received intent,
* call handleMessage(), registered(), etc. in background threads, with a
* wake lock, while keeping the service alive.
*/
static void runIntentInService(Context context, Intent intent) {
if (mWakeLock == null) {
// This is called from BroadcastReceiver, there is no init.
PowerManager pm = (PowerManager) context
.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
WAKELOCK_KEY);
}
mWakeLock.acquire();
// Use a naming convention, similar with how permissions and intents are
// used. Alternatives are introspection or an ugly use of statics.
String receiver ="com.commonsware.android.c2dm.C2DMReceiver";
intent.setClassName(context, receiver);
context.startService(intent);
}
private void handleRegistration(final Context context, Intent intent) {
final String registrationId = intent
.getStringExtra(EXTRA_REGISTRATION_ID);
String error = intent.getStringExtra(EXTRA_ERROR);
String removed = intent.getStringExtra(EXTRA_UNREGISTERED);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "dmControl: registrationId = " + registrationId
+ ", error = " + error + ", removed = " + removed);
}
if (removed != null) {
// Remember we are unregistered
C2DMessaging.clearRegistrationId(context);
onUnregistered(context);
return;
} else if (error != null) {
// we are not registered, can try again
C2DMessaging.clearRegistrationId(context);
// Registration failed
Log.e(TAG, "Registration error " + error);
onError(context, error);
if ("SERVICE_NOT_AVAILABLE".equals(error)) {
long backoffTimeMs = C2DMessaging.getBackoff(context);
Log.d(TAG, "Scheduling registration retry, backoff = "
+ backoffTimeMs);
Intent retryIntent = new Intent(C2DM_RETRY);
PendingIntent retryPIntent = PendingIntent
.getBroadcast(context, 0 /* requestCode */, retryIntent,
0 /* flags */);
AlarmManager am = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.ELAPSED_REALTIME, backoffTimeMs,
retryPIntent);
// Next retry should wait longer.
backoffTimeMs *= 2;
C2DMessaging.setBackoff(context, backoffTimeMs);
}
} else {
try {
onRegistrered(context, registrationId);
C2DMessaging.setRegistrationId(context, registrationId);
GUIStatics.registrationID=registrationId;
} catch (IOException ex) {
Log.e(TAG, "Registration error " + ex.getMessage());
}
}
}
My messages only made it through when i started using:
Bundle extras = intent.getExtras();
String message = (String)extras.get("message");
Log.d("Tag", "msg:" + message);
Related
I've already posted a similar question here, but I've realized that my issue could have more to do with x509 certificate rather than JWS in general.
Here's the thing, I'm pretty new to JWS, and Apple now transmits them as part of their server-to-server communication. I'm trying to understand how to fully guarantee the validity of the JWS, because from what I understand, the signature verification only implies that the entire JWS wasn't tampered with. I don't know how to actually verify that this payload is indeed coming from a trusted source (aka Apple).
Here's what I got so far (PHP):
//1. explode jws and decode what's needed
$components = explode('.', $jws);
$headerJson = json_decode(base64_decode($components[0]),true);
$signature = base64Url_decode($components[2]);
//2. extract all certificates from 'x5c' header
foreach ($headerJson['x5c'] as $x5c){
$c = '-----BEGIN CERTIFICATE-----'.PHP_EOL;
$c .= chunk_split($x5c,64,PHP_EOL);
$c .= '-----END CERTIFICATE-----'.PHP_EOL;
$certificates[] = openssl_x509_read($c);
}
//3. verify validity of certificate chain (each one is signed by the next, except root cert)
for($i = 0; $i < count($certificates); $i++){
if ($i == count($certificates) - 1){
if (openssl_x509_verify($certificates[$i], $certificates[$i]) != 1){
throw new Exception("Invalid Root Certificate");
}
}
else{
if (openssl_x509_verify($certificates[$i], $certificates[$i+1]) != 1){
throw new Exception("Invalid Certificate");
}
}
}
//4. get public_key from first certificate
$public_key = openssl_pkey_get_public($certificates[0]);
//5. verify entire token, including signature (using the Firebase library)
$parsed_token = (array) \Firebase\JWT\JWT::decode($jws, $public_key, ['ES256']);
//helper function: a simple base64 url decoder
function base64Url_decode($data){
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}
Is there a specific field to check against inside the certificates (one that couldn't be spoofed) to verify the identity/source of the JWS?
Thanks!
Thanks to #IMSoP for pointing me in the right direction.
When a JWS contains a chain of certificates (x5c header), we're supposed to have a copy of these valid certificates (or at least the root, since it validates the rest of the chain). In my case, I could find them on Apple's website.
Once you download them, it's as easy as:
openssl_x509_verify($jws_root_cert, $downloaded_apple_root_cert);
verify jws in Java code
import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSONPath;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.ringle.framework.json.JSONUtils;
import com.ringle.framework.response.ServiceResponseStatusEnum;
import java.io.ByteArrayInputStream;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.util.List;
import lombok.Data;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
public interface ApplePayNotificationV2DTO {
#Setter
#Slf4j
class NotificationV2 {
private static Certificate appleRootCa;
private String signedPayload;
public NotificationV2() throws CertificateException {
if (ObjectUtil.isNull(appleRootCa)) {
appleRootCa = CertificateFactory.getInstance("X.509").generateCertificate(
ResourceUtil.getStream("applepay/AppleRootCA-G3.cer")
);
}
}
public ResponseBodyV2DecodedPayload getResponse() {
if (!verify()) {
throw ServiceResponseStatusEnum.BAD_REQUEST.buildException();
}
var decodedJWT = JWT.decode(signedPayload);
var payload = new String(Base64.decode(decodedJWT.getPayload()));
log.info("Apple Pay Notification Payload={}", payload);
return JSONUtils.fromJsonNonNull(
payload,
ResponseBodyV2DecodedPayload.class
);
}
/**
* 验证JWT
*/
private boolean verify() {
var valid = true;
try {
// 拿到 header
var decodedJWT = JWT.decode(signedPayload);
var header = new String(Base64.decode(decodedJWT.getHeader()));
// 获取证书链
var caChain = JSONUtils.<List<String>>fromJsonNonNull(
JSONPath.extract(header, "$.x5c").toString(),
List.class
);
// 获取公钥并验证根证书
var publicKey = getPublicKeyByX5c(caChain);
// 验证 token
var algorithm = Algorithm.ECDSA256((ECPublicKey) publicKey, null);
algorithm.verify(decodedJWT);
} catch (Exception e) {
valid = false;
log.error("Apple Pay JWS Verify Fail", e);
}
return valid;
}
/**
* 获取公钥
*
* #param x5c
*
* #return PublicKey
*/
private PublicKey getPublicKeyByX5c(List<String> x5c) throws CertificateException {
var factory = CertificateFactory.getInstance("X.509");
var jwtSignCa = (X509Certificate) factory.generateCertificate(
new ByteArrayInputStream(Base64.decode(x5c.get(0)))
);
var jwtRootCa = (X509Certificate) factory.generateCertificate(
new ByteArrayInputStream(Base64.decode(x5c.get(2)))
);
// 验证证书是否是苹果颁发的
try {
appleRootCa.verify(jwtRootCa.getPublicKey());
} catch (Exception e) {
throw new RuntimeException(e);
}
return jwtSignCa.getPublicKey();
}
}
#Data
#Slf4j
class ResponseBodyV2DecodedPayload {
private String notificationType;
private String subtype;
private String notificationUUID;
private Object data;
private String version;
private Long signedDate;
/**
* 获取交易详情
*
* #return
*/
public Transaction getTransaction() {
var signedTransactionInfo = JSONPath.extract(
JSONUtils.toJson(data),
"$.signedTransactionInfo"
);
if (ObjectUtil.isNull(signedTransactionInfo)) {
log.warn("Get ApplePay Transaction Fail : transaction is null. responseBody={}", this);
throw ServiceResponseStatusEnum.BAD_REQUEST.buildException();
}
var decodedJWT = JWT.decode(signedTransactionInfo.toString());
return JSONUtils.fromJsonNonNull(decodedJWT.getPayload(), Transaction.class);
}
}
#Data
class Transaction {
private String environment;
private String transactionId;
private Long revocationDate;
}
}
I am using the following php code to send a push notification to Android application using Azure Notification Hub.
$hub = new NotificationHub("Endpoint=sb://ServiceName.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SharedAccessKey", "ServiceNameNotification");
$message = '{"data":{"message":"This is a sample notification","title":"Sample Notification","action":"Test","action_id":"60"}}';
$notification = new AzureNotification("gcm", $message);
$hub->sendNotification($notification, null);
But unfortunately most of the times the notification is not getting delivered, although I am getting a 201 as the response. Whenever it does get delivered, the results are unpredictable, sometimes the contents of old notifications end up getting shown.
The following code is used to show the notifications in Android app (Xamarin.Android) :
using System;
using Android.App;
using Android.Content;
using Gcm.Client;
using Microsoft.WindowsAzure.MobileServices;
using Newtonsoft.Json.Linq;
using ServiceName.Helpers;
[assembly: Permission(Name = "#PACKAGE_NAME#.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "#PACKAGE_NAME#.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")]
//GET_ACCOUNTS is needed only for Android versions 4.0.3 and below
[assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")]
[assembly: UsesPermission(Name = "android.permission.INTERNET")]
[assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
namespace ServiceName.Droid
{
[BroadcastReceiver(Permission = Gcm.Client.Constants.PERMISSION_GCM_INTENTS)]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE },
Categories = new string[] { "#PACKAGE_NAME#" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK },
Categories = new string[] { "#PACKAGE_NAME#" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY },
Categories = new string[] { "#PACKAGE_NAME#" })]
public class ServiceNameBroadcastReceiver : GcmBroadcastReceiverBase<PushHandlerService>
{
public static string[] senderIDs = new string[] { Constants.SenderID };
public const string TAG = "MyBroadcastReceiver-GCM";
}
// The ServiceAttribute must be applied to the class.
[Service]
public class PushHandlerService : GcmServiceBase
{
public static string RegistrationID { get; private set; }
public PushHandlerService() : base(ServiceNameBroadcastReceiver.senderIDs) { }
protected override void OnMessage(Context context, Intent intent)
{
string message = string.Empty;
string title = string.Empty;
string action = string.Empty;
string action_id = string.Empty;
if (intent.Extras.ContainsKey("title"))
{
title = intent.Extras.Get("title").ToString();
}
if (intent.Extras.ContainsKey("message"))
{
message = intent.Extras.Get("message").ToString();
}
if (intent.Extras.ContainsKey("action"))
{
action = intent.Extras.Get("action").ToString();
}
if (intent.Extras.ContainsKey("action_id"))
{
action_id = intent.Extras.Get("action_id").ToString();
}
// Extract the push notification message from the intent.
if (!string.IsNullOrWhiteSpace(message) || !string.IsNullOrWhiteSpace(title))
{
// Create a notification manager to send the notification.
var notificationManager =
GetSystemService(Context.NotificationService) as NotificationManager;
// Create a new intent to show the notification in the UI.
PendingIntent contentIntent =
PendingIntent.GetActivity(context, 0,
new Intent(this, typeof(MainActivity)), 0);
// Create the notification using the builder.
var builder = new Notification.Builder(context);
builder.SetAutoCancel(false);
if (!string.IsNullOrWhiteSpace(title))
{
builder.SetContentTitle(title);
}
else
{
builder.SetContentTitle("Notification from ServiceName");
}
if (!string.IsNullOrWhiteSpace(message))
{
builder.SetContentText(message);
}
else
{
builder.SetContentText("Hello ServiceName User");
}
builder.SetSmallIcon(Resource.Drawable.ic_stat_icon);
builder.SetContentIntent(contentIntent);
var notification = builder.Build();
// Display the notification in the Notifications Area.
notificationManager.Notify(1, notification);
}
}
protected override void OnError(Context context, string errorId)
{
System.Diagnostics.Debug.WriteLine(
string.Format("Error occurred in the notification: {0}.", errorId));
}
protected override async void OnRegistered(Context context, string registrationId)
{
System.Diagnostics.Debug.WriteLine("The device has been registered with GCM.", "Success!");
// Get the MobileServiceClient from the current activity instance.
MobileServiceClient client = MainActivity.CurrentActivity.CurrentClient;
var push = client.GetPush();
// Define a message body for GCM.
const string templateBodyGCM = "{\"data\":{\"message\":\"$(messageParam)\", \"title\": \"$(titleParam)\", \"action\":\"$(actionParam)\",\"action_id\":\"$(action_idParam)\"}}";
// Define the template registration as JSON.
JObject templates = new JObject();
templates["genericMessage"] = new JObject
{
{"body", templateBodyGCM }
};
try
{
// Make sure we run the registration on the same thread as the activity,
// to avoid threading errors.
MainActivity.CurrentActivity.RunOnUiThread(
// Register the template with Notification Hubs.
async () => {
try
{
await push.RegisterAsync(registrationId, templates);
System.Diagnostics.Debug.WriteLine(
string.Format("Push Installation Id " + push.InstallationId.ToString()));
var res = await MiscServices.RegisterDevice(Settings.UserID, Settings.AccessToken, push.InstallationId.ToString(), "gcm");
if ((bool)res.data)
{
System.Diagnostics.Debug.WriteLine("Registered InstallationId in Server");
}
else
{
System.Diagnostics.Debug.WriteLine("Cannot register with Server" + " " + res.status.StatusCode);
}
}
catch (Exception e)
{
}
});
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(
string.Format("Error with Azure push registration: {0}", ex.Message));
}
}
protected override void OnUnRegistered(Context context, string registrationId)
{
System.Diagnostics.Debug.WriteLine("Unregistered with Azure push registration");
}
}
}
Even test notifications fail to get delivered at times, but mostly test notifications work fine. We can't really figure out where the issue is occurring, any help is much appreciated.
As you successfully sent messages to Azure NH, and got 201 responses. When you send a notification via Notification Hubs, initially it just gets queued up for NH to do processing to figure out all its targets and then eventually NH sends it to the PNS.
This means that when you are using REST API or any of the client SDK, the successful return of your send call only means that the message has been successfully queued up with Notification Hub.
It doesn’t give an insight into what happened when NH eventually got to send the message to PNS. If your notification is not arriving at the client device, there is a possibility that when NH tried to deliver the message to PNS, there was an error e.g. the payload size exceeded the maximum allowed by the PNS or the credentials configured in NH are invalid etc.
To get an insight into the PNS errors, we have introduced a property called EnableTestSend feature. This property is automatically enabled when you send test messages from the portal or Visual Studio client and therefore allows you to see detailed debugging information.
Or you can try to call via RESTful APIs for troubleshooting:
https://mynamespace.servicebus.windows.net/mynotificationhub/messages?api-version=2013-10&test
Please refer to Azure Notification Hubs - Diagnosis guidelines for more info.
I have website that has a database inside, I want to retrieve some really secure information from that database in the format of JSON to my android application.
For example when I login to my website through my android application, the website transfer JSON securely to me.
I want to make sure no one else could decode my JSON, for example if some hacker could login with user credentials into my website, he could not access this secure information.
I have read some articles about JWT and I got really confused.
I really appreciate you to lead me through the correct way.
The problem with the json was something like this: when I add some code inside my php code to hide it from outside (only legitimate user can access that, I did it with session code) the result of json is going to be null when I connect with android I do not have this problem when I did in in windows, maybe it is something wrong with my android code when I want to pars json, but whenever I delete the session part it worked perfectley.
Here is my android code
public class ParsingJSON {
public static String[] ids;
public static String[] names;
public static String[] passwords;
public static String[] fullnames;
public static String[] emails;
public static String[] otps;
public static final String JSON_ARRAY = "result";
public static final String KEY_ID = "id";
public static final String KEY_NAME = "username";
public static final String KEY_PASSWORD = "password";
public static final String KEY_FULLNAME= "fullname";
public static final String KEY_EMAIL= "email";
public static final String KEY_OTP = "otp";
private JSONArray users = null;
private String json;
public ParsingJSON(String json){
this.json = json;
}
protected String[] ParsingJSON(){
JSONObject jsonObject=null;
try {
jsonObject = new JSONObject(json);
users = jsonObject.getJSONArray(JSON_ARRAY);
Log.v("sharareh:", String.valueOf(users));
ids = new String[users.length()];
names = new String[users.length()];
passwords = new String[users.length()];
fullnames = new String[users.length()];
emails = new String[users.length()];
otps = new String[users.length()];
for(int i=0;i<users.length();i++){
Log.v("sharareh1:", "sharareh");
JSONObject jo = users.getJSONObject(i);
ids[i] = jo.getString(KEY_ID);
names[i] = jo.getString(KEY_NAME);
passwords[i] = jo.getString(KEY_PASSWORD);
fullnames[i] = jo.getString(KEY_FULLNAME);
emails[i] = jo.getString(KEY_EMAIL);
otps[i] = jo.getString(KEY_OTP);
}
} catch (JSONException e) {
e.printStackTrace();
}
Log.v("OTPJSON:", otps[0]);
return otps;
}
Here is my php code:
<?php
define('HOST', 'x');
define('USER', 'y');
define('PASS', 'z');
define('DB', 'y');
require_once 'totp.class.php';
session_start();
include_once 'include/class.user.php';
$user = new User();
$var_value = $_SESSION['varname'];
echo $var_value;
$user = $var_value;
$con = mysqli_connect(HOST,USER,PASS,DB);
$sql = "select * from users WHERE uemail='$user' ";
$res = mysqli_query($con,$sql);
$result = array();
while($row = mysqli_fetch_array($res)){
array_push($result,
array('id'=>$row[0],
'username'=>$row[1],
'password'=>$row[2],
'fullname'=>$row[3],
'email'=>$row[4],
'otp'=>$row[5]
));
}
echo json_encode(array("result"=>$result));
mysqli_close($con);?>
I already created an android app which uses a web service to send and retrieve JSON data.
When I make a request while the device is online it works fine but when the device goes offline the app is stuck and prints Null Pointer Exception error.
Is there a way to listen to the internet connection?
You can check if it is connected to internet through this.
private boolean isNetworkAvailable() {
ConnectivityManager manager = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
boolean isAvailable = false;
if(networkInfo != null && networkInfo.isConnected()) {
isAvailable = true;
}
return isAvailable;
}
From your question and comments, looks like you have a problem if the connection is lost during the request/response process. So in order to listen for this change you need to crate a BroadcastReceiver (NetworkStateReceiver) to listen to network state change, something like this:
NetworkStateReceiver.java
package your.package.name;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import java.util.ArrayList;
import java.util.List;
public class NetworkStateReceiver extends BroadcastReceiver {
// Listeners list
protected List<NetworkStateReceiverListener> listeners;
// Connection flag
protected Boolean connected;
/**
* Public constructor
*/
public NetworkStateReceiver() {
listeners = new ArrayList<NetworkStateReceiverListener>();
connected = null;
}
/**
*
* #param context Context - Application context
* #param intent Intent - Manages application actions on network state changes
*/
public void onReceive(Context context, Intent intent) {
if(intent == null || intent.getExtras() == null) return;
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = manager.getActiveNetworkInfo();
if((ni != null) && ni.isConnected()) {
connected = true;
} else {
connected = false;
}
mNotifyStateToAll();
}
/**
* Notify the state to all needed methods
*/
private void mNotifyStateToAll() {
for(NetworkStateReceiverListener listener : listeners)
mNotifyState(listener);
}
/**
* Notify the network state
* #param listener NetworkStateReceiverListener - receives network state change
*/
private void mNotifyState(NetworkStateReceiverListener listener) {
if(connected == null || listener == null) return;
if(connected == true) {
listener.networkAvailable();
} else {
listener.networkUnavailable();
}
}
/**
* Add listener once it is needed
* #param l NetworkStateReceiverListener - receives network state change
*/
public void addListener(NetworkStateReceiverListener l) {
listeners.add(l);
mNotifyState(l);
}
/**
* Remove the listener once it is not needed anymore
* #param l NetworkStateReceiverListener - receives network state change
*/
public void removeListener(NetworkStateReceiverListener l) {
listeners.remove(l);
}
/**
* Set interface to communicate with Main methods
*/
public interface NetworkStateReceiverListener {
public void networkAvailable();
public void networkUnavailable();
}
}
Your activity needs to implement this:
public class MyActivity extends Activity implements NetworkStateReceiver.NetworkStateReceiverListener {
// Receiver that detects network state changes
private NetworkStateReceiver networkStateReceiver;
private boolean mNetworkAvailable;
...
// What ever the code you want or need
...
/**
* Call back for NetworkStateReceiver to set the network state to available
*/
#Override
public void networkAvailable() {
Log.d(TAG, "I'm in, baby! Dance, dance revolution!");
sNetworkAvailable = true;
// Network available again do things here
}
/**
* Call back for NetworkStateReceiver to set the network state to unavailable
*/
#Override
public void networkUnavailable() {
Log.d(TAG, "I'm dancing with myself, noone can see me.");
sNetworkAvailable = false;
// Network broke, warn the user, or do alternative action
}
/**
* Need to register the receiver
*/
#Override
public void onResume() {
super.onResume();
// Register the network state receiver to listen to network state change
if (networkStateReceiver == null) {
networkStateReceiver = new NetworkStateReceiver();
networkStateReceiver.addListener(this);
this.registerReceiver(networkStateReceiver, new IntentFilter(android.net.ConnectivityManager.CONNECTIVITY_ACTION));
}
}
/**
* Unregister the receiver as you do not need it anymore
*/
#Override
public void onDestroy() {
super.onDestroy();
// Remove network state receiver and listener as we don't need them at this point
if (networkStateReceiver != null) {
networkStateReceiver.removeListener(this);
this.unregisterReceiver(networkStateReceiver);
networkStateReceiver = null;
}
}
...
// What ever the code you want or need
...
}
before request the web service call this method
private boolean isOnline() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo NInfo = cm.getActiveNetworkInfo();
if (NInfo != null && NInfo.isConnectedOrConnecting())
return true;
else
return false;
}
if(isOnline()){
// request the service
// but make sure that U have surrounded the calling web-service by try and catch
try{
// here make your request when the connection go offline the app will catch the error and ignore the process
}catch (Exception e) {
}
add this permission
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
In your manifest add permission
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Java
public boolean networkstatus() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
if(networkstatus())
{
// do your process
}
else
{
//alert message for no internet connection
}
I am writing a Native Android App in which i am using PHP MYSQL to get data from server
In this [Appointment List] i am allowing user to Reschedule an Appointment, but whenever i do tap on item getting blank form, in short not getting data for that particular appointment which i have clicked in a List.
Question
How to show data in a form using AppointmentID ?
Below i am showing all required code written by me [Client & Server Side both]
UpcomingActivity.java:
#Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
int menuItemIndex = item.getItemId();
String[] menuItems = Cmd;
String CmdName = menuItems[menuItemIndex];
// Check Event Command
if ("Cancel".equals(CmdName))
{
Toast.makeText(UpcomingActivity.this,"Selected Cancel",Toast.LENGTH_LONG).show();
}
else if ("Reschedule".equals(CmdName))
{
Toast.makeText(UpcomingActivity.this,"Selected Update",Toast.LENGTH_LONG).show();
String sAppointmentID = MyArrList.get(info.position).get("UserID").toString();
Log.d(tag, "sAppointmentID :: " + sAppointmentID);
Intent newActivity = new Intent(UpcomingActivity.this, UpdateActivity.class);
newActivity.putExtra("UserID", sAppointmentID);
startActivity(newActivity);
}
return true;
}
UpdateActivity.java:
public void showInfo()
{
final TextView tAppointmentID = (TextView)findViewById(R.id.txtUsername);
final TextView tType = (TextView)findViewById(R.id.txtName);
final TextView tDate = (TextView)findViewById(R.id.txtEmail);
final TextView tTime = (TextView)findViewById(R.id.txtTel);
Button btnSave = (Button) findViewById(R.id.btnSave);
Button btnCancel = (Button) findViewById(R.id.btnCancel);
String url = "http://10.0.2.2/appointments/getByMemberID.php";
Intent intent= getIntent();
final String AppointmentID = intent.getStringExtra("AppointmentID");
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("sAppointmentID", AppointmentID));
String resultServer = getHttpPost(url,params);
String strAppointmentID = "";
String strType = "";
String strDate = "";
String strTime = "";
JSONObject c;
try {
c = new JSONObject(resultServer);
strAppointmentID = c.getString("UserID");
Log.d(TAG, "String strAppointmentID" + strAppointmentID);
strType = c.getString("Type");
Log.d(TAG, "String strType" + strType);
strDate = c.getString("Date");
Log.d(TAG, "String strDate" + strDate);
strTime = c.getString("Time");
Log.d(TAG, "String strTime" + strTime);
if(!strAppointmentID.equals(""))
{
tAppointmentID.setText(strAppointmentID);
tType.setText(strType);
tDate.setText(strDate);
tTime.setText(strTime);
}
else
{
tAppointmentID.setText("-");
tType.setText("-");
tDate.setText("-");
tTime.setText("-");
btnSave.setEnabled(false);
btnCancel.requestFocus();
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
first of all make sure that you have valid AppointmentID and UserIdand
in UpcomingActivity.java at method onContextItemSelected you are not providing AppointmentID instead you are only providing UserID to the Intend
List but in Updateactivity at method showData you are requesting intent.getStringExtra("AppointmentID") which is invaild.
so your update your UpcomingActivity.java should look like this
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
int menuItemIndex = item.getItemId();
String[] menuItems = Cmd;
String CmdName = menuItems[menuItemIndex];
// Check Event Command
if ("Cancel".equals(CmdName))
{
Toast.makeText(UpcomingActivity.this,"Selected Cancel",Toast.LENGTH_LONG).show();
}
else if ("Reschedule".equals(CmdName))
{
Toast.makeText(UpcomingActivity.this,"Selected Update",Toast.LENGTH_LONG).show();
String sAppointmentID = MyArrList.get(info.position).get("UserID").toString();
Log.d(tag, "sAppointmentID :: " + sAppointmentID);
Intent newActivity = new Intent(UpcomingActivity.this, UpdateActivity.class);
newActivity.putExtra("UserID", sAppointmentID);
newActivity.putExtra("AppointmentID", MyArrList.get(info.position).get("AppointmentID").toString());// <== here
startActivity(newActivity);
}
return true;
}
I noticed a couple of issues (might be typos, but I prefer to ask anyway):
You are calling a getByMemberID.php, but you have not included its source code in the question - you do have such service, right? :)
In the onContextItemSelected you are starting the UpdateActivity and pass UserID as extra. But in the showInfo method of that activity you are trying to get AppointmentID from the intent's extras, so it is not processing any data.
1) Try weather your PHP code is working fine or not...
You can do that by running directly on server and passing UserId and appoinmentId as a parameter by ?
For eg.... www.abc.com?sUserId=123?sAppointmentID=456
See weather it's showing you proper output.
2) you are calling getByMemberID.php but you have specified code of it but you are not passing "AppointmentID" for that also check with php code weather you are retriving it ??
3) On call of updateData.php in UpdateActivity.java: you are not providing "AppointmentID" but you are retriving it in php code it will get null value by default which will be an error ofcourse