java.io.FileNotFoundException for Cloud Iot core Code - android-things

I am presently working on program on Android Things for connecting to Google Cloud IoT Core. I used to sample maven code provided by Google and modified it for Gradle(with all the imports and stuff). After doing every kind of check, whenever I am trying to run the program on a Raspberry Pi3 running Android Things it keeps giving this error
W/System.err: java.io.FileNotFoundException: com/example/adityaprakash/test/rsa_private.pem (No such file or directory)
telling me that the private key file that I am supposed to use for the JWT doesn't exist despite the fact it does and I have given the path for the pem file.Here are my java codes
package com.example.adityaprakash.test;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
Log.i("#########","######");
MqttExample mqtt = new MqttExample();
try {
mqtt.Start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
The MqttExample.java
package com.example.adityaprakash.test;
// [END cloudiotcore_mqtt_imports]
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.joda.time.DateTime;
import java.io.BufferedReader;
import java.io.FileReader;
import java.security.KeyFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import android.util.Base64;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
public class MqttExample {
// [START cloudiotcore_mqtt_createjwt]
/** Create a Cloud IoT Core JWT for the given project id, signed with the given RSA key. */
public static String createJwtRsa(String projectId, String privateKeyFile) throws Exception {
DateTime now = new DateTime();
String strKeyPEM = "";
BufferedReader br = new BufferedReader(new FileReader(privateKeyFile));
String line;
while ((line = br.readLine()) != null) {
strKeyPEM += line + "\n";
}
br.close();
// Create a JWT to authenticate this device. The device will be disconnected after the token
// expires, and will have to reconnect with a new token. The audience field should always be set
// to the GCP project id.
JwtBuilder jwtBuilder =
Jwts.builder()
.setIssuedAt(now.toDate())
.setExpiration(now.plusMinutes(20).toDate())
.setAudience(projectId);
String privateKeyPEM = strKeyPEM;
privateKeyPEM = privateKeyPEM.replace("-----BEGIN PRIVATE KEY-----\n", "");
privateKeyPEM = privateKeyPEM.replace("-----END PRIVATE KEY-----", "");
byte[] encoded = Base64.decode(privateKeyPEM,Base64.DEFAULT);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(encoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
return jwtBuilder.signWith(SignatureAlgorithm.RS256, kf.generatePrivate(spec)).compact();
}
/** Parse arguments, configure MQTT, and publish messages. */
public void Start() throws Exception {
// [START cloudiotcore_mqtt_configuremqtt]
MqttExampleOptions options = MqttExampleOptions.values();
if (options == null) {
// Could not parse.
System.exit(1);
}
// Build the connection string for Google's Cloud IoT Core MQTT server. Only SSL
// connections are accepted. For server authentication, the JVM's root certificates
// are used.
final String mqttServerAddress =
String.format("ssl://%s:%s", options.mqttBridgeHostname, options.mqttBridgePort);
// Create our MQTT client. The mqttClientId is a unique string that identifies this device. For
// Google Cloud IoT Core, it must be in the format below.
final String mqttClientId =
String.format(
"projects/%s/locations/%s/registries/%s/devices/%s",
options.projectId, options.cloudRegion, options.registryId, options.deviceId);
MqttConnectOptions connectOptions = new MqttConnectOptions();
// Note that the the Google Cloud IoT Core only supports MQTT 3.1.1, and Paho requires that we
// explictly set this. If you don't set MQTT version, the server will immediately close its
// connection to your device.
connectOptions.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
// With Google Cloud IoT Core, the username field is ignored, however it must be set for the
// Paho client library to send the password field. The password field is used to transmit a JWT
// to authorize the device.
connectOptions.setUserName("unused");
System.out.println(options.algorithm);
if (options.algorithm.equals("RS256")) {
connectOptions.setPassword(
createJwtRsa(options.projectId, options.privateKeyFile).toCharArray());
}else {
throw new IllegalArgumentException(
"Invalid algorithm " + options.algorithm + ". Should be one of 'RS256' or 'ES256'.");
}
// [END cloudiotcore_mqtt_configuremqtt]
// [START cloudiotcore_mqtt_publish]
// Create a client, and connect to the Google MQTT bridge.
MqttClient client = new MqttClient(mqttServerAddress, mqttClientId, new MemoryPersistence());
try {
client.connect(connectOptions);
// Publish to the events or state topic based on the flag.
String subTopic = options.messageType.equals("event") ? "events" : options.messageType;
// The MQTT topic that this device will publish telemetry data to. The MQTT topic name is
// required to be in the format below. Note that this is not the same as the device registry's
// Cloud Pub/Sub topic.
String mqttTopic = String.format("/devices/%s/%s", options.deviceId, subTopic);
// Publish numMessages messages to the MQTT bridge, at a rate of 1 per second.
for (int i = 1; i <= options.numMessages; ++i) {
String payload = String.format("%s/%s-payload number-%d", options.registryId, options.deviceId, i);
System.out.format(
"Publishing %s message %d/%d: '%s'\n",
options.messageType, i, options.numMessages, payload);
// Publish "payload" to the MQTT topic. qos=1 means at least once delivery. Cloud IoT Core
// also supports qos=0 for at most once delivery.
MqttMessage message = new MqttMessage(payload.getBytes());
message.setQos(1);
client.publish(mqttTopic, message);
if (options.messageType.equals("event")) {
// Send telemetry events every second
Thread.sleep(1000);
}
else {
// Note: Update Device state less frequently than with telemetry events
Thread.sleep(5000);
}
}
} finally {
// Disconnect the client and finish the run.
client.disconnect();
}
System.out.println("Finished loop successfully. Goodbye!");
// [END cloudiotcore_mqtt_publish]
}
}
and the MqttExampleOptions.java code:
package com.example.adityaprakash.test;
public class MqttExampleOptions {
String projectId;
String registryId;
String deviceId;
String privateKeyFile;
String algorithm;
String cloudRegion;
int numMessages;
String mqttBridgeHostname;
short mqttBridgePort;
String messageType;
/** Construct an MqttExampleOptions class. */
public static MqttExampleOptions values() {
try {
MqttExampleOptions res = new MqttExampleOptions();
res.projectId = "_";
res.registryId = "_";
res.deviceId = "_";
res.privateKeyFile = "com/example/adityaprakash/test/rsa_private.pem";
res.algorithm = "RS256";
res.cloudRegion = "asia-east1";
res.numMessages = 100;
res.mqttBridgeHostname = "mqtt.googleapis.com";
res.mqttBridgePort = 8883;
res.messageType = "event";
return res;
} catch (Exception e) {
System.err.println(e.getMessage());
return null;
}
}
}
Please can anyone give a solution to this problem.
P.S. I know the code looks totally crappy.I don't have experience with Android programming,so please let it go.

The example you are following is not designed for Android.
res.privateKeyFile = "com/example/adityaprakash/test/rsa_private.pem";
Will not relate to the same directory on the Android file system.
I wrote up an AndroidThings explanation of how to talk to Cloud IoT Core here: http://blog.blundellapps.co.uk/tut-google-cloud-iot-core-mqtt-on-android/
You can setup communication like this (with your pem file going into the /raw directory)
// Setup the communication with your Google IoT Core details
communicator = new IotCoreCommunicator.Builder()
.withContext(this)
.withCloudRegion("your-region") // ex: europe-west1
.withProjectId("your-project-id") // ex: supercoolproject23236
.withRegistryId("your-registry-id") // ex: my-devices
.withDeviceId("a-device-id") // ex: my-test-raspberry-pi
.withPrivateKeyRawFileId(R.raw.rsa_private)
.build();
Source code is here: https://github.com/blundell/CloudIoTCoreMQTTExample
Note that the above is good enough for a secure environment or for testing that the end to end works. However if you wanted to release a production IoT device, you would look at embedding the PEM into the ROM and using private file storage access. https://developer.android.com/training/articles/keystore.html
An example of this can be found here: https://github.com/androidthings/sensorhub-cloud-iot
Specifically this class:
https://github.com/androidthings/sensorhub-cloud-iot/blob/e50bde0100fa81818ebbadb54561b3b68ccb64b8/app/src/main/java/com/example/androidthings/sensorhub/cloud/cloudiot/MqttAuthentication.java
You can then generate and use the PEM on the device:
public Certificate getCertificate() {
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
certificate = ks.getCertificate("Cloud IoT Authentication");
if (certificate == null) {
Log.w(TAG, "No IoT Auth Certificate found, generating new cert");
generateAuthenticationKey();
certificate = ks.getCertificate(keyAlias);
}
Log.i(TAG, "loaded certificate: " + keyAlias);
}
and
private void generateAuthenticationKey() throws GeneralSecurityException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
kpg.initialize(new KeyGenParameterSpec.Builder("Cloud IoT Authentication",KeyProperties.PURPOSE_SIGN)
.setKeySize(2048)
.setCertificateSubject(new X500Principal("CN=unused"))
.setDigests(KeyProperties.DIGEST_SHA256)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
.build());
kpg.generateKeyPair();
}

I'm pretty sure you're not doing the file I/O correctly. Your file, "com/example/adityaprakash/test/rsa_private.pem", doesn't correspond to an actual filepath on the device. The location of files on the device may be different than in your project. You will have to determine where on the device your file actually is.

On AndroidThings, it is easier to provide the authentication credentials in an Android Resource. See my fork of the WeatherStation sample to see how this works.
First, copy the private key file (e.g. rsa_private_pkcs8) to app/src/main/res/raw/privatekey.txt
Next, you can load the key used to calculate your JWT as:
Context mContext;
int resIdPk = getResources().getIdentifier("privatekey", "raw", getPackageName());
...
InputStream privateKey = mContext.getResources().openRawResource(resIdPk);
byte[] keyBytes = inputStreamToBytes(privateKey);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("EC");
One final note, it appears that you're referencing a file that is not in pkcs8 format, which will cause issues with Java. Make sure to use a key that is packaged in PKCS8 when opening credentials on Android (Java).

Related

Connection to AWS IoT with M2MQTT from .net core

I managed to manually create the AWS IoT config, downloaded the certs and create a console app that could subscribe to a topic. Im now trying to automate the thing creation, which results in the certificate keys being provided by AWS as strings. Im not sure how to use these. I have the root ca downloaded already, which I assume I use for all things.
My file based cert subscriber looks like this:
Console.WriteLine("AWS IOT Dotnet core message listener starting");
string iotendpoint = "blahblah-ats.iot.ap-southeast-2.amazonaws.com";
int BrokerPort = 8883;
string Topic = "topic_1/";
var CaCert = X509Certificate.CreateFromCertFile(#"root-CA.crt");
var ClientCert = new X509Certificate2(#"device.pfx", "password");
var IotClient = new MqttClient(iotendpoint, BrokerPort, true, CaCert, ClientCert, MqttSslProtocols.TLSv1_2);
try
{
IotClient.Connect(Guid.NewGuid().ToString());
Console.WriteLine("Connected to AWS IOT");
IotClient.MqttMsgPublishReceived += Client_MqttMsgPublishReceived;
IotClient.MqttMsgSubscribed += Client_MqttMsgSubscribed;
IotClient.Subscribe(new string[] { Topic }, new byte[] { MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE });
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return;
}
To load the cert from file, I tried this:
var keyText = File.ReadAllText("keys.json");
var keys = JsonConvert.DeserializeObject<Keys>(keyText);
var bytes = Encoding.ASCII.GetBytes(keys.PrivateKey.ToCharArray());
var ClientCert = new X509Certificate2(bytes);
with:
class Keys {
public string PublicKey {get;set;}
public string PrivateKey {get;set;}
}
and the keys from AWS in a json file :
{
"PrivateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA4mh2PQ581XN9BmoCvDjlaktm/6gQgqGBItZThcQVMTjveU8H\npjOU2E/9lq7vmdO+96NuuMr9MKtFD+ZWtVExLjMq9hH0MvIvosVt9+6Ggcwz7Kdr\nigprfBMVORV0rgcK+nsd2DmBNrs339fqbTn5UAIFFBpqkNReW7LMl9h6g8hu4aYQ\nJTohDwSmgmNJKlzMJGtVfPggqt+bBi3lUf9NEOEz...
-----END RSA PRIVATE KEY-----\n",
"PublicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4mh2PQ581XN9BmoCvDjl\naktm/6gQgqGBItZThcQVMTjveU8HpjOU2E/9lq7vmdO+96NuuMr9MKtFD+ZWtVEx\nLjMq9hH0MvIvosVt9+6Ggcwz7K...
-----END PUBLIC KEY-----\n"
}
Im getting an error loading the cert:
An unhandled exception of type 'Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException' occurred in System.Security.Cryptography.X509Certificates.dll: 'Cannot find the requested object.'
Can anyone see anything obviously wrong here? I dont understand certificates...
Update:
Using the PEM text produced by the AWS SDK is more correct, but I still get an error connecting - M2MQTT says there is a cert problem, it has no private key. Does it need it?
var pemText = File.ReadAllText("thing.crt");
var bytes = Encoding.ASCII.GetBytes(pemText);
var ClientCert = new X509Certificate2(bytes);
Final hacked together solution looks like this:
var keyText = File.ReadAllText("keys.json"); // saved from AWS SDK when creating IoT Cert.
var keys = JsonConvert.DeserializeObject<Keys>(keyText);
var rsa = RsaHelper.PrivateKeyFromPem(keys.PrivateKey);
var pemText = File.ReadAllText("thing.crt");
var bytes = Encoding.ASCII.GetBytes(pemText);
var ClientCert = new X509Certificate2(bytes);
ClientCert = ClientCert.CopyWithPrivateKey(rsa);
ClientCert = new X509Certificate2(ClientCert.Export(X509ContentType.Pfx,"12345678"), "12345678");
RSAHelper from https://github.com/dejanstojanovic/dotnetcore-token-authentication/blob/asymmetric_rsahelper/Sample.Core.Common/Helpers/RsaHelper.cs
Last trick to Export and Import the PFX from https://github.com/aspnet/KestrelHttpServer/issues/2960 to solve error: "No credentials are available in the security package"
Sidebar - why do we (as an industry) always take something conceptually simple and make it so fricken complicated? :)

Device to device push notifications via Firebase Cloud Messaging [duplicate]

Is there any way to send Upstream notification message through FCM from one android device to another devices connected with Firebase database.
I know that XMPP server can then receive the upstream messages and send the notifications to the other devices.To receive messages sent with the upstream API i need to implement an XMPP server but there is any other way???
Is there any way to send Upstream notification message through FCM
from one android device to another devices connected with Firebase
database?
Currently it's NOT possible to send messages directly from one device to another.
(or at least it's not possible without introducing a HUGE security vulnerability: more details below)
Full details:
Sending messages to a user device is a pretty serious action!
based on the payload a message can result in spam, phishing, execution of internal methods.
You want this operation to be allowed only be trusted entities, this is why the FCM send API requires the SERVER-API-KEY in the authentication header.
Adding the SERVER-API-KEY in your app code (or communicating it to the app in some other way) IS NOT SAFE. This because apk can be extracted, decompiled, inspected, executed on emulators, executed under debugging and so on.
The best way to implement this today: is to have some sort of server between the two devices:
[DeviceA] -- please send message to B --> [SERVER] -- fcmSendAPI --> [DeviceB]
The server can be as simple as a PHP page, or a more complex XMPP implementation.
An example in Node.js can be found here:
Sending notifications between devices with Firebase Database and Cloud Messaging
Finally, after 2 months of trying to maintain reliable server script myself, I suddenly found OneSignal. It's completely free, supports device-to-device push messages on iOS, Android, WP and browsers.
Hope, I won't get flag for promotion spam, but it's currently the only (and easiest) way to be completely "backendless".
Also, it's completely secure way. Nobody can send push unless he knows special OS user id, which you can store in Firebase Database protected by rules.
UPD: It's not a replacement for Firebase. It has only push service and nothing else
UPD2: Firebase now has Functions, and examples of it usage has sending FCM. You now don't need any other server or service. Read more in official samples https://github.com/firebase/functions-samples
After lots of try finally i got one solution and its work perfectly
Step 1 :Include two library.
compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.google.firebase:firebase-messaging:9.2.0'
Step 2 : In your MainActivity or from where you want to send notifications.
OkHttpClient mClient = new OkHttpClient();
String refreshedToken = "";//add your user refresh tokens who are logged in with firebase.
JSONArray jsonArray = new JSONArray();
jsonArray.put(refreshedToken);
Step 3: Create one async task which sends notifications to all devices.
public void sendMessage(final JSONArray recipients, final String title, final String body, final String icon, final String message) {
new AsyncTask<String, String, String>() {
#Override
protected String doInBackground(String... params) {
try {
JSONObject root = new JSONObject();
JSONObject notification = new JSONObject();
notification.put("body", body);
notification.put("title", title);
notification.put("icon", icon);
JSONObject data = new JSONObject();
data.put("message", message);
root.put("notification", notification);
root.put("data", data);
root.put("registration_ids", recipients);
String result = postToFCM(root.toString());
Log.d("Main Activity", "Result: " + result);
return result;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String result) {
try {
JSONObject resultJson = new JSONObject(result);
int success, failure;
success = resultJson.getInt("success");
failure = resultJson.getInt("failure");
Toast.makeText(MainActivity.this, "Message Success: " + success + "Message Failed: " + failure, Toast.LENGTH_LONG).show();
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(MainActivity.this, "Message Failed, Unknown error occurred.", Toast.LENGTH_LONG).show();
}
}
}.execute();
}
String postToFCM(String bodyString) throws IOException {
public static final String FCM_MESSAGE_URL = "https://fcm.googleapis.com/fcm/send";
final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
RequestBody body = RequestBody.create(JSON, bodyString);
Request request = new Request.Builder()
.url(Url.FCM_MESSAGE_URL)
.post(body)
.addHeader("Authorization", "key=" + "your server key")
.build();
Response response = mClient.newCall(request).execute();
return response.body().string();
}
Step 4 : Call in onclick of your button
btnSend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
sendMessage(jsonArray,"Hello","How r u","Http:\\google.com","My Name is Vishal");
}
});

Push Notifications on iOS with Firebase [duplicate]

After searching the docs I could not find any info on how to send device to device messages using FCM without the use of an external server.
For example, if I was creating a chat application I would need to send push notifications to users about unread messages since they won't be online all the time and I can't have a persistent service in the background that would always be connected to the real time database because that would be too resource heavy.
So how would I send a push notification to a user "A" when a certain user "B" sends him/her a chat message? Do I need an external server for this or can it be done with just Firebase servers?
UPDATE: It is now possible to use firebase cloud functions as the server for handling push notifications. Check out their documentation here
============
According to the docs you must implement a server for handling push notifications in device to device communication.
Before you can write client apps that use Firebase Cloud Messaging, you must have an app server that meets the following criteria:
...
You'll need to decide which FCM connection server protocol(s) you want to use to enable your app server to interact with FCM connection servers. Note that if you want to use upstream messaging from your client applications, you must use XMPP. For a more detailed discussion of this, see Choosing an FCM Connection Server Protocol.
If you only need to send basic notifications to your users from the server. You can use their serverless solution, Firebase Notifications.
See a comparison here between FCM and Firebase Notifications:
https://firebase.google.com/support/faq/#messaging-difference
Making a HTTP POST request with the link https://fcm.googleapis.com/fcm/send with required header and data helped me. In the below code snippet
Constants.LEGACY_SERVER_KEY is a local class variable, you can find this at your Firebase Project Settings->Cloud Messaging->Legacy Server key. You need to pass device registration token i.e. regToken in below code snippet referenced HERE.
At last you need okhttp library dependency in order to get this snippet work.
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
private void sendNotification(final String regToken) {
new AsyncTask<Void,Void,Void>(){
#Override
protected Void doInBackground(Void... params) {
try {
OkHttpClient client = new OkHttpClient();
JSONObject json=new JSONObject();
JSONObject dataJson=new JSONObject();
dataJson.put("body","Hi this is sent from device to device");
dataJson.put("title","dummy title");
json.put("notification",dataJson);
json.put("to",regToken);
RequestBody body = RequestBody.create(JSON, json.toString());
Request request = new Request.Builder()
.header("Authorization","key="+Constants.LEGACY_SERVER_KEY)
.url("https://fcm.googleapis.com/fcm/send")
.post(body)
.build();
Response response = client.newCall(request).execute();
String finalResponse = response.body().string();
}catch (Exception e){
//Log.d(TAG,e+"");
}
return null;
}
}.execute();
}
further if you want to send message to a particular topic, replace regToken in json like this
json.put("to","/topics/foo-bar")
and don't forget to add INTERNET permission in your AndroidManifest.xml.
IMPORTANT : - Using above code means your server key resides in the client application. That is dangerous as someone can dig into your application and get the server key to send malicious notifications to your users.
You can do it using Volly Jsonobject request....
follow this Steps first:
1 copy legacy server key and store it as Legacy_SERVER_KEY
Legacy Server key
you can see in picture how to get
2 You need Volley dependency
compile 'com.mcxiaoke.volley:library:1.0.19'
Code for send Push:-
private void sendFCMPush() {
String Legacy_SERVER_KEY = YOUR_Legacy_SERVER_KEY;
String msg = "this is test message,.,,.,.";
String title = "my title";
String token = FCM_RECEIVER_TOKEN;
JSONObject obj = null;
JSONObject objData = null;
JSONObject dataobjData = null;
try {
obj = new JSONObject();
objData = new JSONObject();
objData.put("body", msg);
objData.put("title", title);
objData.put("sound", "default");
objData.put("icon", "icon_name"); // icon_name image must be there in drawable
objData.put("tag", token);
objData.put("priority", "high");
dataobjData = new JSONObject();
dataobjData.put("text", msg);
dataobjData.put("title", title);
obj.put("to", token);
//obj.put("priority", "high");
obj.put("notification", objData);
obj.put("data", dataobjData);
Log.e("!_#rj#_##_PASS:>", obj.toString());
} catch (JSONException e) {
e.printStackTrace();
}
JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.POST, Constants.FCM_PUSH_URL, obj,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.e("!_##_SUCESS", response + "");
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("!_##_Errors--", error + "");
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Authorization", "key=" + Legacy_SERVER_KEY);
params.put("Content-Type", "application/json");
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(this);
int socketTimeout = 1000 * 60;// 60 seconds
RetryPolicy policy = new DefaultRetryPolicy(socketTimeout, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
jsObjRequest.setRetryPolicy(policy);
requestQueue.add(jsObjRequest);
}
Just Call sendFCMPush();
1) subscribe an identical topic name, for example:
ClientA.subcribe("to/topic_users_channel")
ClientB.subcribe("to/topic_users_channel")
2) send messages inside the application
GoogleFirebase : How-to send topic messages
Yes, it's possible to do it without any server. You can create a device group client side and then you exchange messages in the group. However there are limitations:
You have to use the same Google account on the devices
You can't send high priority messages
Reference: Firebase doc See the section "Managing device groups on Android client apps"
Google Cloud Functions make it now possible send push notifications from device-to-device without an app server.
I have made cloud function which is trigger when new message is added in database
It is node.js code
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin'); admin.initializeApp();
exports.sendNotification = functions.database.ref('/conversations/{chatLocation}/{messageLocation}')
.onCreate((snapshot, context) => {
// Grab the current value of what was written to the Realtime Database.
const original = snapshot.val();
const toIDUser = original.toID;
const isGroupChat = original.isGroupChat;
if (isGroupChat) {
const tokenss = admin.database().ref(`/users/${toIDUser}/tokens`).once('value').then(function(snapshot) {
// Handle Promise
const tokenOfGroup = snapshot.val()
// get tokens from the database at particular location get values
const valuess = Object.keys(tokenOfGroup).map(k => tokenOfGroup[k]);
//console.log(' ____________ddd((999999ddd_________________ ' + valuess );
const payload = {
notification: {
title: original.senderName + " :- ",
body: original.content
}
};
return admin.messaging().sendToDevice(valuess, payload);
}, function(error) {
console.error(error);
});
return ;
} else {
// get token from the database at particular location
const tokenss = admin.database().ref(`/users/${toIDUser}/credentials`).once('value').then(function(snapshot) {
// Handle Promise
// The Promise was "fulfilled" (it succeeded).
const credentials = snapshot.val()
// console.log('snapshot ......snapshot.val().name****^^^^^^^^^^^^kensPromise****** :- ', credentials.name);
//console.log('snapshot.....****snapshot.val().token****^^^^^^^^^^^^kensPromise****** :- ', credentials.token);
const deviceToken = credentials.token;
const payload = {
notification: {
title: original.senderName + " :- ",
body: original.content
}
};
return admin.messaging().sendToDevice(deviceToken, payload);
}, function(error) {
console.error(error);
});
}
return ;
});
Google Cloud Functions make it now possible send push notifications from device-to-device without an app server.
From the relevant page on Google Cloud Functions:
Developers can use Cloud Functions to keep users engaged and up to
date with relevant information about an app. Consider, for example, an
app that allows users to follow one another's activities in the app.
In such an app, a function triggered by Realtime Database writes to
store new followers could create Firebase Cloud Messaging (FCM)
notifications to let the appropriate users know that they have gained
new followers.
Example:
The function triggers on writes to the Realtime Database path where followers are stored.
The function composes a message to send via FCM.
FCM sends the notification message to the user's device.
Here is a demo project for sending device-to-device push notifications with Firebase and Google Cloud Functions.
You can use firebase realtime database to do so. You can create data structure for storing chats and add observers for the conversation threads for both users. It still does device - server - device architecture, but in this case there is no additional server on the developers' part. This uses the firebase servers. You can check out a tutorial here (ignore the UI part, although, that is also a good starting point for chat UI frameworks).
Firebase Realtime Chat
If you have fcm(gcm) token of the device to whom you want to send notification. It's just a post request to send the notification.
https://github.com/prashanthd/google-services/blob/master/android/gcm/gcmsender/src/main/java/gcm/play/android/samples/com/gcmsender/GcmSender.java
In my case I use retrofit with this class Message:
public class Message {
private String to;
private String collapseKey;
private Notification notification;
private Data data;
public Message(String to, String collapseKey, Notification notification, Data data) {
this.to = to;
this.collapseKey = collapseKey;
this.notification = notification;
this.data = data;
}
}
Data
public class Data {
private String body;
private String title;
private String key1;
private String key2;
public Data(String body, String title, String key1, String key2) {
this.body = body;
this.title = title;
this.key1 = key1;
this.key2 = key2;
}
}
Notification
public class Notification {
private String body;
private String title;
public Notification(String body, String title) {
this.body = body;
this.title = title;
}
}
this the call
private void sentToNotification() {
String to = "YOUR_TOKEN";
String collapseKey = "";
Notification notification = new Notification("Hello bro", "title23");
Data data = new Data("Hello2", "title2", "key1", "key2");
Message notificationTask = new Message(to, collapseKey, notification, data);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://fcm.googleapis.com/")//url of FCM message server
.addConverterFactory(GsonConverterFactory.create())//use for convert JSON file into object
.build();
ServiceAPI api = new retrofit.create(ServiceAPI.class);
Call<Message> call = api .sendMessage("key=YOUR_KEY", notificationTask);
call.enqueue(new Callback<Message>() {
#Override
public void onResponse(Call<Message> call, retrofit2.Response<Message> response) {
Log.d("TAG", response.body().toString());
}
#Override
public void onFailure(Call<Message> call, Throwable t) {
Log.e("TAG", t.getMessage());
}
});
}
our ServiceAPi
public interface ServiceAPI {
#POST("/fcm/send")
Call<Message> sendMessage(#Header("Authorization") String token, #Body Message message);
}
You can use Retrofit. Subscribe devices to topic news. Send notification from one device to other.
public void onClick(View view) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", "key=legacy server key from FB console"); // <-- this is the important line
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
httpClient.addInterceptor(logging);
OkHttpClient client = httpClient.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://fcm.googleapis.com")//url of FCM message server
.client(client)
.addConverterFactory(GsonConverterFactory.create())//use for convert JSON file into object
.build();
// prepare call in Retrofit 2.0
FirebaseAPI firebaseAPI = retrofit.create(FirebaseAPI.class);
//for messaging server
NotifyData notifydata = new NotifyData("Notification title","Notification body");
Call<Message> call2 = firebaseAPI.sendMessage(new Message("topic or deviceID", notifydata));
call2.enqueue(new Callback<Message>() {
#Override
public void onResponse(Call<Message> call, Response<Message> response) {
Log.d("Response ", "onResponse");
t1.setText("Notification sent");
}
#Override
public void onFailure(Call<Message> call, Throwable t) {
Log.d("Response ", "onFailure");
t1.setText("Notification failure");
}
});
}
POJOs
public class Message {
String to;
NotifyData notification;
public Message(String to, NotifyData notification) {
this.to = to;
this.notification = notification;
}
}
and
public class NotifyData {
String title;
String body;
public NotifyData(String title, String body ) {
this.title = title;
this.body = body;
}
}
and FirebaseAPI
public interface FirebaseAPI {
#POST("/fcm/send")
Call<Message> sendMessage(#Body Message message);
}
Here is walk around how to get notifications without second server apart from the Firebase one. So we use Firebase only, without additional server.
At the mobile app code, we create its own notifications function by Android libraries like here, not using Firebase libraries like here, without Firebase Cloud messaging.
Here is an example with Kotlin:
private fun notification() {
createNotificationChannel()
val intent = Intent(this, LoginActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
val notificationBuilder = NotificationCompat.Builder(this, "yuh_channel_id")
.setSmallIcon(R.drawable.ic_send)
.setContentText("yuh")
.setContentText("yuh")
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(0, notificationBuilder.build())
with(NotificationManagerCompat.from(this)) {
// notificationId is a unique int for each notification that you must define
notify(0, notificationBuilder.build())
}
}
private fun createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = "yuh_channel"
val descriptionText = "yuh_description"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val CHANNEL_ID = "yuh_channel_id"
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
// Register the channel with the system
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
In the Firebase database, create collection "pending notifications". Documents should contain user name (to send notification to) and source name (where should user go upon tapping the notification).
In the app code, implement option for adding new records to the Pending Notifications collection. E. g. if user A sends message to user B, then the document with the id of user B (who will be notified) is created in the collection.
In the app code, set up background (when the app is not visible to the user) service. Like here. In the background service, set up a listener for changes in the "Notifications Pending" collection. When the new record with the user id comes to the collection, call the notification function created in the paragrath 1 supra and delete the consequent record from the collection.
So I had an idea here. See: If the FCM, as well as the GCM, has a endpoit to http request where we can send a post json with our message data, including the token (s) of devices that we want this message to be delivered.
So why not send a post to Firebase server with this notification to be delivered to user B? you understand ?
So, you send the message and chat with a call post to ensure delivery of the notification if the user is with your app in the background. I am also in need of it soon, I will test later. What do you say about?
Simplest way :
void sendFCMPush(String msg,String token) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", "key="+Const.FIREBASE_LEGACY_SERVER_KEY); // <-- this is the important line
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
httpClient.addInterceptor(logging);
OkHttpClient client = httpClient.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://fcm.googleapis.com/")//url of FCM message server
.client(client)
.addConverterFactory(GsonConverterFactory.create())//use for convert JSON file into object
.build();
// prepare call in Retrofit 2.0
FirebaseAPI firebaseAPI = retrofit.create(FirebaseAPI.class);
//for messaging server
NotifyData notifydata = new NotifyData("Chatting", msg);
Call<Message> call2 = firebaseAPI.sendMessage(new Message(token, notifydata));
call2.enqueue(new Callback<Message>() {
#Override
public void onResponse(Call<Message> call, retrofit2.Response<Message> response) {
Log.e("## SUCCES #E$#", response.body().toString());
}
#Override
public void onFailure(Call<Message> call, Throwable t) {
Log.e("E$ FAILURE E$#", t.getMessage());
}
});
}
Create Class to make Object:
public class Message {
String to;
NotifyData data;
public Message(String to, NotifyData data) {
this.to = to;
this.data = data;
}
}
Create Class to make Object:
public class Notification {
String title;
String message;
enter code here`enter code here`
public Notification(String title, String message) {
this.title = title;
this.message = message;
}
}

Delphi Send Device to Device message using firebase [duplicate]

Is there any way to send Upstream notification message through FCM from one android device to another devices connected with Firebase database.
I know that XMPP server can then receive the upstream messages and send the notifications to the other devices.To receive messages sent with the upstream API i need to implement an XMPP server but there is any other way???
Is there any way to send Upstream notification message through FCM
from one android device to another devices connected with Firebase
database?
Currently it's NOT possible to send messages directly from one device to another.
(or at least it's not possible without introducing a HUGE security vulnerability: more details below)
Full details:
Sending messages to a user device is a pretty serious action!
based on the payload a message can result in spam, phishing, execution of internal methods.
You want this operation to be allowed only be trusted entities, this is why the FCM send API requires the SERVER-API-KEY in the authentication header.
Adding the SERVER-API-KEY in your app code (or communicating it to the app in some other way) IS NOT SAFE. This because apk can be extracted, decompiled, inspected, executed on emulators, executed under debugging and so on.
The best way to implement this today: is to have some sort of server between the two devices:
[DeviceA] -- please send message to B --> [SERVER] -- fcmSendAPI --> [DeviceB]
The server can be as simple as a PHP page, or a more complex XMPP implementation.
An example in Node.js can be found here:
Sending notifications between devices with Firebase Database and Cloud Messaging
Finally, after 2 months of trying to maintain reliable server script myself, I suddenly found OneSignal. It's completely free, supports device-to-device push messages on iOS, Android, WP and browsers.
Hope, I won't get flag for promotion spam, but it's currently the only (and easiest) way to be completely "backendless".
Also, it's completely secure way. Nobody can send push unless he knows special OS user id, which you can store in Firebase Database protected by rules.
UPD: It's not a replacement for Firebase. It has only push service and nothing else
UPD2: Firebase now has Functions, and examples of it usage has sending FCM. You now don't need any other server or service. Read more in official samples https://github.com/firebase/functions-samples
After lots of try finally i got one solution and its work perfectly
Step 1 :Include two library.
compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.google.firebase:firebase-messaging:9.2.0'
Step 2 : In your MainActivity or from where you want to send notifications.
OkHttpClient mClient = new OkHttpClient();
String refreshedToken = "";//add your user refresh tokens who are logged in with firebase.
JSONArray jsonArray = new JSONArray();
jsonArray.put(refreshedToken);
Step 3: Create one async task which sends notifications to all devices.
public void sendMessage(final JSONArray recipients, final String title, final String body, final String icon, final String message) {
new AsyncTask<String, String, String>() {
#Override
protected String doInBackground(String... params) {
try {
JSONObject root = new JSONObject();
JSONObject notification = new JSONObject();
notification.put("body", body);
notification.put("title", title);
notification.put("icon", icon);
JSONObject data = new JSONObject();
data.put("message", message);
root.put("notification", notification);
root.put("data", data);
root.put("registration_ids", recipients);
String result = postToFCM(root.toString());
Log.d("Main Activity", "Result: " + result);
return result;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String result) {
try {
JSONObject resultJson = new JSONObject(result);
int success, failure;
success = resultJson.getInt("success");
failure = resultJson.getInt("failure");
Toast.makeText(MainActivity.this, "Message Success: " + success + "Message Failed: " + failure, Toast.LENGTH_LONG).show();
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(MainActivity.this, "Message Failed, Unknown error occurred.", Toast.LENGTH_LONG).show();
}
}
}.execute();
}
String postToFCM(String bodyString) throws IOException {
public static final String FCM_MESSAGE_URL = "https://fcm.googleapis.com/fcm/send";
final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
RequestBody body = RequestBody.create(JSON, bodyString);
Request request = new Request.Builder()
.url(Url.FCM_MESSAGE_URL)
.post(body)
.addHeader("Authorization", "key=" + "your server key")
.build();
Response response = mClient.newCall(request).execute();
return response.body().string();
}
Step 4 : Call in onclick of your button
btnSend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
sendMessage(jsonArray,"Hello","How r u","Http:\\google.com","My Name is Vishal");
}
});

Error "401 Unauthorized" to upload video in my youtube account with Simple Access API (API Key)

I'm trying to upload video in my youtube account with Simple Access API (API Key).
I'm getting this exception.
You chose src/resources/video.avi to upload.
Initiation Started
Initiation Completed
Exception: 401 Unauthorized
com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:143)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:115)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:421)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:340)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:458)
at com.google.api.services.samples.YoutubeSample.queryGoogleYouTube(YoutubeSample.java:126)
at com.google.api.services.samples.YoutubeSample.main(YoutubeSample.java:152)
What is wrong?
Documentation to use Simple Access API (API Key) with youtube-API is very poor.
Could anyone help me?
My Simple Access API:
Simple API Access
Use API keys to identify your project when you do not need to access user data.
Key for server apps (with IP locking)
API key: AIzaSxxxxxxxxxxxxxxxxxxxxxxx-bVE8jRlzXY
IPs: Any IP allowed
Activated on: Feb 11, 2014 7:50 PM
Activated by: myaccount#gmail.com – you
This is my code.
package com.google.api.services.samples;
import com.google.api.client.googleapis.media.MediaHttpUploader;
import com.google.api.client.googleapis.media.MediaHttpUploaderProgressListener;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.InputStreamContent;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.YouTubeRequestInitializer;
import com.google.api.services.youtube.model.Video;
import com.google.api.services.youtube.model.VideoSnippet;
import com.google.api.services.youtube.model.VideoStatus;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
public class YoutubeSample {
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
private static YouTube youtube;
private static String VIDEO_FILE_FORMAT = "video/*";
private static final String APPLICATION_NAME = "YoutubeTest";
//Simple API Access
//Use API keys to identify your project when you do not need to access user data.
//Key for server apps (with IP locking)
private static final String API_KEY = "AIzaSxxxxxxxxxxxxxxxxxx-bVE8jRlzXY"; // This has changed.
private static File getVideoFromUser() throws IOException {
return new File("src/resources/video.avi");
}
private static void queryGoogleYouTube() throws Exception {
ClientCredentials.errorIfNotSpecified();
try {
//In this piece of code that is my difficulty
youtube = new YouTube.Builder(HTTP_TRANSPORT, JSON_FACTORY,
new HttpRequestInitializer() {
public void initialize(HttpRequest request)
throws IOException {
}
})
.setApplicationName(APPLICATION_NAME)
.setYouTubeRequestInitializer(new YouTubeRequestInitializer(API_KEY)).build();
File videoFile = getVideoFromUser();
System.out.println("You chose " + videoFile + " to upload.");
Video videoObjectDefiningMetadata = new Video();
VideoStatus status = new VideoStatus();
status.setPrivacyStatus("public");
videoObjectDefiningMetadata.setStatus(status);
VideoSnippet snippet = new VideoSnippet();
Calendar cal = Calendar.getInstance();
snippet.setTitle("Test Upload via Java on " + cal.getTime());
snippet.setDescription("Video uploaded via YouTube Data API V3 using the Java library "
+ "on " + cal.getTime());
// Set your keywords.
List<String> tags = new ArrayList<String>();
tags.add("test");
tags.add("example");
tags.add("java");
tags.add("YouTube Data API V3");
tags.add("erase me");
snippet.setTags(tags);
videoObjectDefiningMetadata.setSnippet(snippet);
InputStreamContent mediaContent = new InputStreamContent(
VIDEO_FILE_FORMAT, new BufferedInputStream(
new FileInputStream(videoFile)));
mediaContent.setLength(videoFile.length());
YouTube.Videos.Insert videoInsert = youtube.videos().insert(
"snippet,statistics,status", videoObjectDefiningMetadata,
mediaContent);
MediaHttpUploader uploader = videoInsert.getMediaHttpUploader();
uploader.setDirectUploadEnabled(false);
MediaHttpUploaderProgressListener progressListener = new MediaHttpUploaderProgressListener() {
public void progressChanged(MediaHttpUploader uploader)
throws IOException {
switch (uploader.getUploadState()) {
case INITIATION_STARTED:
System.out.println("Initiation Started");
break;
case INITIATION_COMPLETE:
System.out.println("Initiation Completed");
break;
case MEDIA_IN_PROGRESS:
System.out.println("Upload in progress");
System.out.println("Upload percentage: "
+ uploader.getProgress());
break;
case MEDIA_COMPLETE:
System.out.println("Upload Completed!");
break;
case NOT_STARTED:
System.out.println("Upload Not Started!");
break;
}
}
};
uploader.setProgressListener(progressListener);
// Execute upload.
Video returnedVideo = videoInsert.execute();
// Print out returned results.
System.out
.println("\n================== Returned Video ==================\n");
System.out.println(" - Id: " + returnedVideo.getId());
System.out.println(" - Title: "
+ returnedVideo.getSnippet().getTitle());
System.out.println(" - Tags: "
+ returnedVideo.getSnippet().getTags());
System.out.println(" - Privacy Status: "
+ returnedVideo.getStatus().getPrivacyStatus());
System.out.println(" - Video Count: "
+ returnedVideo.getStatistics().getViewCount());
} catch (Exception e) {
System.err.println("Exception: " + e.getMessage());
e.printStackTrace();
} catch (Throwable t) {
System.err.println("Throwable: " + t.getMessage());
t.printStackTrace();
}
}
public static void main(String[] args) {
try {
queryGoogleYouTube();
} catch (Exception e) {
e.printStackTrace();
}
}
}
As you can see in the snippet you posted, the simple access API key is designed to identify your project (i.e. for the sake of quotas) when you do not need to access user data. Uploading a video into an account is a form of accessing user data, so you'll need to use oauth2, request the scope 'https://www.googleapis.com/auth/youtube.upload,' and then perform the upload with the access token.
Once you have that oauth2 flow running and your user grants access to the scope, it seems that the rest of your code looks right (but I'm not a Java expert so don't hold me to that!)

Resources