I am developing an application using api 14 (android 4.0).
in manifest:
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="14" />
I want to get an unique id from each device (or create one) that could be the same even after reboot the device. But it is important that the id be different even for 2 same devices. How can i do that?
You can use device's IMEI number as unique Id.
You want to call android.telephony.TelephonyManager.getDeviceId().
This will return whatever string uniquely identifies the device (IMEI on GSM, MEID for CDMA).
You'll need the following permission in your AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
You can generate different device token using GCM....
And this device token will remain same even if you will uninstall and again installed the application or after factory setting.. you have to follow some steps......
Create a new project at Google Developers Console .
At this step, for simplicity, you just need to take note of 2 values: Project Number, which will be used as SENDER_ID in the client project; and API server key (created at Credentials), which will be used as API_KEY in the server project.
Create a new simple Android project for server side (with basic source code as my answer in the following links).
Create a new simple Android project for client side (with basic source code as my answer in the following links, I customized from the original source at Google Cloud Messaging - GitHub).
Run the client app, you will get the registration token (means that your device has successfully registered). Then, paste (hard-code) this token at CLIENT_REGISTRATION_TOKEN variable in server app (or write code to send this token to server app).
You can read more at the following questions, one of them you have read before with one of your previous questions:
How to implement a GCM Hello World for Android using Android Studio
Adding Google Cloud Messagin (GCM) for Android - Registration process
Try this one String android_id = Settings.Secure.getString(getApplicationContext().getContentResolver(),
Settings.Secure.ANDROID_ID);
Here android_id is the unique Id for each device.
Try this code
String UniqueDeviceId = AndroidDeviceIdentifier.getUniqueDeviceIdentifier(context);
Add this class too.
final class AndroidDeviceIdentifier {
private AndroidDeviceIdentifier() {
// hidden constructor of singleton
}
/**
* Returns a stable identifier for the current device.
*
* #param ctx The application's Context
* #return The unique device identifier
* #throws IllegalStateException If the device's identifier could not be determined
*/
public static String getUniqueDeviceIdentifier(#NonNull final Context ctx) throws IllegalStateException {
try {
return getDeviceUUID(ctx);
} catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
throw new IllegalStateException("Could not determine device identifier", e);
}
}
private static String getDeviceUUID(Context ctx) throws UnsupportedEncodingException, NoSuchAlgorithmException {
byte[] hash = makeHash(getMac(ctx), getSerialNumber(ctx));
return createUUIDFromHash(hash);
}
private static String createUUIDFromHash(byte[] hash) {
return UUID.nameUUIDFromBytes(hash).toString().toLowerCase(); // Server side wants lower cased UUIDs
}
private static byte[] makeHash(final String mac, final String serialNumber) throws UnsupportedEncodingException, NoSuchAlgorithmException {
MessageDigest sha;
sha = MessageDigest.getInstance("SHA-256");
sha.reset();
sha.update(mac.getBytes("UTF-8"));
sha.update(serialNumber.getBytes("UTF-8"));
return sha.digest();
}
private static String getSerialNumber(Context context) {
String serialNumber = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
if (serialNumber == null) {
serialNumber = "0000000000000000";
}
return serialNumber;
}
private static String getMac(Context context) {
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
String mac = wifiManager.getConnectionInfo().getMacAddress();
if (mac == null) {
mac = "000000000000";
}
return mac;
}
You will get a unique device id. Ping me if u have any questions
Try out this code below ,this device id is constant and even if you uninstall the app and reinstall this id remains constant and you can use it to retrieve the user data from database as well.
final String android_id = Settings.Secure.getString(getApplicationContext().getContentResolver(),
Settings.Secure.ANDROID_ID);
Just paste this code in your main activity or any function and can store the ID generated in the shared Preference for later use.
Related
I'm building an app that uses Google Cloud Speech.
I have a Google Service account key in my app, and I use it to call the API.
It works well when used by one user, but does not work when multiple users use it at the same time.
For example, only one user is available or all are unavailable.
The rights of the service account key are project owner.
I think it's a service account key issue...
How do I fix it?
private class AccessTokenTask extends AsyncTask<Void, Void, AccessToken> {
#Override
protected AccessToken doInBackground(Void... voids) {
final SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
String tokenValue = prefs.getString(PREF_ACCESS_TOKEN_VALUE, null);
long expirationTime = prefs.getLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, -1);
// Check if the current token is still valid for a while
if (tokenValue != null && expirationTime > 0) {
if (expirationTime > System.currentTimeMillis() + ACCESS_TOKEN_EXPIRATION_TOLERANCE) {
return new AccessToken(tokenValue, new Date(expirationTime));
}
}
final InputStream stream = mContext.getResources().openRawResource(R.raw.credential);
try {
final GoogleCredentials credentials = GoogleCredentials.fromStream(stream).createScoped(SCOPE);
final AccessToken token = credentials.refreshAccessToken();
prefs.edit()
.putString(PREF_ACCESS_TOKEN_VALUE, token.getTokenValue())
.putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, token.getExpirationTime().getTime())
.apply();
return token;
} catch (IOException e) {
Log.e(TAG, "Failed to obtain access token.", e);
}
return null;
}
#Override
protected void onPostExecute(AccessToken accessToken) {
mAccessTokenTask = null;
final ManagedChannel channel = new OkHttpChannelProvider()
.builderForAddress(GOOGLE_API_HOSTNAME, GOOGLE_API_PORT)
.nameResolverFactory(new DnsNameResolverProvider())
.intercept(new GoogleCredentialsInterceptor(new GoogleCredentials(accessToken)
.createScoped(SCOPE)))
.build();
mApi = SpeechGrpc.newStub(channel);
// Schedule access token refresh before it expires
if (mHandler != null) {
mHandler.postDelayed(mFetchAccessTokenRunnable,
Math.max(accessToken.getExpirationTime().getTime() - System.currentTimeMillis() - ACCESS_TOKEN_FETCH_MARGIN, ACCESS_TOKEN_EXPIRATION_TOLERANCE));
}
}
}
This code is the code that calls 'credential.json' file on Android and gets 'Access token'.
The server for this app is python and communicates via http.
https://github.com/GoogleCloudPlatform/android-docs-samples/tree/master/speech/Speech
The description in the link above tells you to delegate the authentication to the server.
I want to write that part with python code.
What should I do?
In the link you provided in the description, they suggest you to read first the basic authentication concepts document. In your case, use a service account for the Android application.
I understand that you have already been able to provide end user credentials to a Google Cloud Platform API, as for example Cloud Speech API.
If you want to authenticate multiple users to your application you should use instead Firebase authentication. The link contains a brief explanation and a tutorial.
There are several Python client libraries for GCP that you can use, depending on what operations do you want to perform on the server. And regarding Python authentication on the server side, this documentation shows how the authentication for Google Cloud Storage works (have this example in mind as a reference).
First of all I'd like to say that it is a project for a course in my university and at the same time my first app for Android which is more complicated that a calculator, so I understand that I could have done some unforgivable mistakes, but my priority is that the code should work. It can be insecure and not considering some cases, but as long as those cases won't appear, it will do.
My app is intended to be running on Android and first of all there should appear login screen which takes login and password, makes the hash of the password and contacts a database on a web server to compare hashes. I was told to use a free database db4free.net.
I created a class Serwer, which would be responsible exclusively for contacting the database. As far as I understood from tutorials and stackoverflow questions and answers, the connection should consist of:
Loading the driver,
Registering it in the DriverManager class,
Using getConnection method to open the connection, passing the credentials,
Preparing and executing SQL query,
Fetching a result set.
I also learned that I should download a mysql-connector-java-5.1.38-bin.jar file. As some threads on stackoverflow suggested, I copied it into main directory of the project (I have to copy the workspace and take to professor's computer when I finish), added it to Libraries tab of properties as an external library. Now when I run the project on my smartphone, I get a java.lang.ClassNotFoundException: Didn't find class "com.mysql.jdbc.Driver" error. I also tried to check the library in Order and Export tab - then it even doesn't compile, returning Conversion to Dalvik format failed with error 1.
I've tried many scenarios in other stackoverflow threads, such as cleaning the project in many configurations, changing the order of build path, etc. I suspect that I've made a simple, stupid mistake that I do not see and I hope you will recognize it.
Here is my Serwer class:
package com.planer.serwer;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Driver;
import com.planer.MainActivity;
import com.example.planer.R;
import com.planer.pracownik.Pracownik;
public class Serwer {
private Connection conn = null;
private static Driver driver;
private static int status;
private MainActivity parentActivity;
private final String user = parentActivity.getResources().getString(R.string.db_login);
private final String pass = parentActivity.getResources().getString(R.string.db_pass);
private final String url= "jdbc:mysql://db4free.net:3306/kalendarzplaner";
public static final int STATUS_GOOD = 0;
public static final int STATUS_NO_CONNECTION = 1;
public static final int STATUS_NOT_AUTHENTICATED = 2;
public static final int STATUS_SQL_EXCEPTION = 4;
public static final int STATUS_NO_DRIVER = 8;
public Serwer(MainActivity parentActivity){
status = STATUS_NO_CONNECTION;
try {
driver = new com.mysql.jdbc.Driver();
} catch (Exception ex) {
status |= STATUS_NO_DRIVER;
} catch (NoClassDefFoundError e){
status |= STATUS_NO_DRIVER;
}
this.parentActivity = parentActivity;
}
public Pracownik authorize(String login, String passhash){
Pracownik pracownik = new Pracownik("","",false,status);
status |= this.polacz();
if(status != Serwer.STATUS_GOOD) {
pracownik.status |= status;
return pracownik;
}
Statement statement = null;
ResultSet resultSet = null;
String query = "select passhash, imie_nazwisko, czy_kierownik from auth where login='" + login + "';";
try {
statement = conn.prepareStatement(query);
resultSet = statement.executeQuery(query);
resultSet.first();
if(resultSet.getString("passhash").toString().compareTo(passhash)!= 0){
status |= Serwer.STATUS_NOT_AUTHENTICATED;
pracownik.status |= status;
return pracownik;
}
pracownik.login = login;
pracownik.imie_nazwisko = resultSet.getString("imie_nazwisko");
pracownik.czy_kierownik = resultSet.getBoolean("czy_kierownik");
} catch (SQLException ex) {
pracownik.status |= Pracownik.STATUS_SQL_EXCEPTION;
}
return pracownik;
}
public int polacz() {
int done = STATUS_NO_CONNECTION;
if((status & STATUS_NO_DRIVER) != 0)
return done;
// Connection
try {
DriverManager.registerDriver(driver);
conn = DriverManager.getConnection(url, user, pass);
done = Serwer.STATUS_GOOD;
} catch (java.sql.SQLException ex) {
done |= Serwer.STATUS_SQL_EXCEPTION;
System.out.println("SQLException: " + ex.getMessage());
}
return done;
}
}
As I said, the status of the result of authorise method is 9, which is expected when the driver is not loaded. I also append my workspace contents.
First, I want to start by suggesting that you tried out Android Studio. It's the new more modern IDE developed specifically for the purpose of Android Development.
Secondly, contacting a database on Android is a lot different than for example contacting a DB from Java/C# in an Desktop application.
To contact an online MySQL Database you need a RESTful service (written in PHP for example) that gets the data from the database and sends it over to the application. The service is like a communication point between the App and the Database. The service usually sends data to the application in a human-unfriendly format like JSON, so your app needs to parse that and then display it.
I want to restrict my API endpoints access only to my android app, but without google_account/password.
I've the choice of those methods : https://developers.google.com/accounts/docs/OAuth2
For test, I succeeded to authenticate my android app to my API with this method: https://cloud.google.com/appengine/docs/python/endpoints/consume_android
==> This method allow you to authenticate your app with combo:
Login/password (Google account)
SHA1 and package name of your android APP
So if someone get my code (Decompiling apk) and modify my android code, they can't access to my API because SHA1 fingerprint of my app will change. (I tested it, and it works =) )
This method works fine, but I don't want Google login/password for authentication..
So I tried this method: https://developers.google.com/accounts/docs/OAuth2ServiceAccount
I successfully authenticate my android app, BUT, if my android code is modified by someone else(So the SHA1 changed), my android app can still connect to my API !! So if someone get my package and decompile it, he'll changed freely code and successfully access to my API..
Here is my API Code:
#ApiMethod( name = "ListCampagnes", httpMethod = ApiMethod.HttpMethod.GET, path="list", clientIds = {CONSTANTES.ANDROID_CLIENT_ID, CONSTANTES.WEB_CLIENT_ID, CONSTANTES.SERVICE_CLIENT_ID, com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID}, audiences = {CONSTANTES.ANDROID_AUDIENCE})
public Collection<Campagne> getCampagnes(#Named("NumPortable")String NumPortable, User user) throws UnauthorizedException {
if (user == null) throw new UnauthorizedException("User is Not Valid");
return CampagneCRUD.getInstance().findCampagne(NumPortable);
}
Here is my android code:
GoogleCredential credentialToAppengine;
try {
String p12Password = "notasecret";
KeyStore keystore = KeyStore.getInstance("PKCS12");
InputStream keyFileStream = getAssets().open("59ce5a08e110.p12");
keystore.load(keyFileStream, p12Password.toCharArray());
PrivateKey key = (PrivateKey)keystore.getKey("privatekey", p12Password.toCharArray());
credentialToAppengine = new GoogleCredential.Builder().setTransport(AndroidHttp.newCompatibleTransport()).setJsonFactory(new JacksonFactory()).setServiceAccountId("301991144702-3v9ikfp4lsmokee1utkucj35847eddvg#developer.gserviceaccount.com").setServiceAccountPrivateKey(key).setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/userinfo.email")).build();
} catch (GeneralSecurityException e) {
e.printStackTrace();
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
Do I try an other method for authenticate my android App ? Or did I missing something in my API code ?
Thanks a looot in advance,
Authenticate Android End point without Google User Account is just impossible ! I tried every ways but still doesn't works !
So here is my way to resolv this problem, without any user interaction (Maybe not the right but that works, and you've got strong authentication (SHA1 + Google Account)):
HERE IS MY ANDROID CODE
Get and Build Valid Credential
//Get all accounts from my Android Phone
String validGoogleAccount = null;
Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
Account[] accounts = AccountManager.get(context).getAccounts();
for (Account account : accounts) {
if (emailPattern.matcher(account.name).matches()) {
//Just store mail if countain gmail.com
if (account.name.toString().contains("gmail.com")&&account.type.toString().contains("com.google")){
validGoogleAccount=account.name.toString();
}
}
}
//Build Credential with valid google account
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(this,"server:client_id:301991144702-5qkqclsogd0b4fnkhrja7hppshrvp4kh.apps.googleusercontent.com");
credential.setSelectedAccountName(validGoogleAccount);
Use this credential for secure calls
Campagneendpoint.Builder endpointBuilder = new Campagneendpoint.Builder(AndroidHttp.newCompatibleTransport(), new JacksonFactory(), credential);
HERE IS MY API BACKEND CODE:
API Annotation
#Api(
scopes=CONSTANTES.EMAIL_SCOPE,
clientIds = {CONSTANTES.ANDROID_CLIENT_ID,
CONSTANTES.WEB_CLIENT_ID,
com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID},
audiences = {CONSTANTES.ANDROID_AUDIENCE},
name = "campagneendpoint",
version = "v1"
)
Method code:
public Collection<Campagne> getCampagnes(#Named("NumPortable")String NumPortable, User user) throws UnauthorizedException {
if (user == null) throw new UnauthorizedException("User is Not Valid");
return CampagneCRUD.getInstance().findCampagne(NumPortable);
}
For the moment, it only works on Android (I don't know how we gonna do on IOS..)..
Hope It will help you !
I have android native application & web service. Our application is University application. Generate long live token for session issue.So we planned to go for MDM, i searched on Google, , I got some solution
Open Mobster (Open Source Mobile Enterprise Backend)
Apache USerGrid_ (Mobile Backend as a service)
OpenMeap - not support native
Problem is I could not find out , whether this MDM are provide device identification facility.If any one knows about it please tell me. Device identification may be any unique id or mac address
or If i am going to developed our own middle layer , how we can get mac address from request header. without passing to service method , from the request should take.
is there any way to take sort-out this issue?
Please help me
Here is how OpenMEAP grabs the Mac address and passes it in the header.
package com.openmeap.thinclient;
import java.io.ByteArrayInputStream;
import com.openmeap.http.HttpRequestExecuter;
import com.openmeap.http.HttpResponse;
import com.openmeap.util.Utils;
public class FirstRunCheck implements Runnable {
private SLICConfig config;
private String macAddress;
private HttpRequestExecuter executer;
public FirstRunCheck(SLICConfig config, String macAddress, HttpRequestExecuter executer) {
this.config = config;
this.macAddress = macAddress;
this.executer = executer;
}
public void run() {
if( config.isDevelopmentMode().equals(Boolean.TRUE) ) {
return;
}
if( config.getNotFirstRun()==null ) {
config.setNotFirstRun(Boolean.TRUE);
try {
String macWithSalt = macAddress+".OPENMEAP#$!#3__234";
String hashValue = Utils.hashInputStream("sha1", new ByteArrayInputStream(macWithSalt.getBytes("UTF-8")));
HttpResponse response = executer.get("http://usage.openmeap.com/tracker.gif?hash="+hashValue);
Utils.consumeInputStream(response.getResponseBody());
} catch( Exception ioe ) {
return;
}
}
}
}
I am using the Amazon Web Service to send push notifications directly to a device. After I install the app I get the device id, that I need to manually add to the Amazon SNS. I would like to know if there is anyway to register the device id directly with the amazon server the moment the user starts the application.
I have read this, but found it difficult to understand. Does anyone have any previous experience of how to do this?
EDIT 2 (What I have done so far)
I've followed the instructions from this link
I download the snspobilepush.zip file as instructed and extract and import the project into eclipse. I add the GCM project number, add the jar files and run the application. I get my device registration Id.
I open the Amazon SNS, add my device id and I publish a message. I receive the message on my mobile phone. Works great so far.
MY PROBLEM
I would be having a lot of potential users for my application. So adding every device id manually to the SNS makes no sense. I need the Amazon SNS to directly register my device id when I start the app. Is there any option for me to do that? I couldn't find any definitive answer in the docs.
This link tells me to Use the "AWS Token Vending Service". However, I could not find any example of how to do that.
Using the AmazonSNSClient documented here:
http://docs.aws.amazon.com/AWSAndroidSDK/latest/javadoc/
it should be possible to register using code similar to this:
AWSCredentials awsCredentials = new BasicAWSCredentials("XXXXXX", "XXXXXXXXXXXXXXX");
String platformApplicationArn = "arn:aws:sns:us-east-1:123456789:app/GCM/myappname";
AmazonSNSClient pushClient = new AmazonSNSClient(awsCredentials);
String customPushData = "my custom data";
CreatePlatformEndpointRequest platformEndpointRequest = new CreatePlatformEndpointRequest();
platformEndpointRequest.setCustomUserData(customPushData);
platformEndpointRequest.setToken(pushNotificationRegId);
platformEndpointRequest.setPlatformApplicationArn(platformApplicationArn);
CreatePlatformEndpointResult result = pushClient.createPlatformEndpoint(platformEndpointRequest);
Log.w(TAG, "Amazon Push reg result: " + result);
It was not liking my ARN, but that was a stupid typo that Reid pointed out and is now fixed above.
There is Android AWS SDK available to use. Check out the documentation link: http://docs.aws.amazon.com/AWSAndroidSDK/latest/javadoc/
Also, more information available: http://aws.amazon.com/sdkforandroid/
This is working for Firebase and Cognito. An AsyncTask is necessary to avoid running on the Main Thread.
private class RegisterIdForAWS extends AsyncTask<String, Void, Void> {
private Exception exception;
protected Void doInBackground(String... urls) {
try {
String pushNotificationRegId = FirebaseInstanceId.getInstance().getToken();
if (pushNotificationRegId != null) {
CognitoCachingCredentialsProvider provider = new CognitoCachingCredentialsProvider(
getApplicationContext(),
"us-west-2:aaaaaaaaa-1234-1234-1234-0bbbbbbbbbbbb",
Regions.US_WEST_2);
String platformApplicationArn = "arn:aws:sns:us-west-2:123456789:app/GCM/appname";
AmazonSNSClient pushClient = new AmazonSNSClient(provider);
pushClient.setRegion(Region.getRegion(Regions.US_WEST_2));
String customPushData = "";
CreatePlatformEndpointRequest platformEndpointRequest = new CreatePlatformEndpointRequest();
platformEndpointRequest.setCustomUserData(customPushData);
platformEndpointRequest.setToken(pushNotificationRegId);
platformEndpointRequest.setPlatformApplicationArn(platformApplicationArn);
CreatePlatformEndpointResult result = pushClient.createPlatformEndpoint(platformEndpointRequest);
Log.w(TAG, "Amazon Push reg result: " + result);
}
} catch (Exception e) {
this.exception = e;
}
return null;
}
protected void onPostExecute(String text) {
Log.w(TAG, "Amazon Push reg Finished");
}
}