In my app, i generate and save private keys. They are encrypted using AES as described here (http://nelenkov.blogspot.com/2012/05/storing-application-secrets-in-androids.html). I've seen another entry on smiliar subject (http://nelenkov.blogspot.com/2012/05/storing-application-secrets-in-androids.html), but this solution gives no guarantee to work on all devices.
Everything is fine except one thing. Everytime usertime wants to get access to one of the encrypted files (or create new one) he needs to enter pin/password (which is very frustrating). I'm looking for a way to somehow avoid this, so as long as user don't leave aplication his password is stored.
The only solution i found is to load everything after user enters password and keep it in memory, but I don't think it's good one.
Cache your keys for a certain period of type (say 10 mins) and clear them afterwards. You can set alarms using AlarmService for this. There was a library project that does this for you, but I haven't used it personally:
https://github.com/guardianproject/cacheword
Why not create a custom Application subclass, and store the password there?
So something like:
public class App extends Application {
private static String sPassword;
public String getPassword() {
return sPassword;
}
}
Then use it:
String password = ((App) context.getApplication()).getPassword();
if(password != null && !password.isEmpty() {
// Decrypt files
} else {
// Prompt user for password, and save it to the Application
}
You'll also have to specify your custom Application in your manifest:
<application android:name="com.my.awesome.App" ....
Related
I'm writing an android application which will have two types of users.One is admin that s gonna make adding and removing needed stuff and user that is gonna just read only.And I dont know a word about how can i make authentication between them.I need some source and advice.So if I can hear some advice.I really become so happy :)
Thanks in advance.
There would be several approaches to this. One rather quick that gives some flexibility would be to:
1) Create an:
interface ApplicationRights {
boolean writeX()
boolean canSeeButtonY()
}
Then you have a static variable of ApplicationRights interface somewhere accessible from anywhere in the application. (there would be many options for this, this is just a simple way)
To complement this you have two classes:
class AdminRights implements ApplicationRights {
...
}
and
class UserRights implements ApplicationRights {
...
}
Then in that static variable, as soon as you know if it's a User or Admin you
create and put either the UserRights or AdminRights in there. Doing so will allow you to customise this behaviour on a more finegrained level.
And whenever you add a new piece of code that will differ from a User and Admin you can easily add another boolean method in the interface to decide upon the rights. (This would also easily allow you to create more roles than users and admins)
Then to decide and save this to repeat it you can use for example SharedPreferences for knowing which user to create.
My problem is all the texts comes from webservices. So, at the beginning, I call the webservice and I have to set the texts in strings.xml. I know writing in strings.xml it's impossible but we had the same problem on iOs and a solution has been found here http://iosapplove.com/archive/2013/01/localizable-strings-how-to-load-translations-dynamically-and-use-it-inside-your-iphone-app/.
So my question is : Is there a similar way on android ? (I think no)
It exists alternative way like using database or sharedPreferences but these solutions won't be very effective. Moreover, the application will contain many languages.
So my second question : What is the best way to do this ?
As you already know, the strings.xml files will not exist in the APK. This means that there is no way to change what getResources().getString(R.string.mystring) will return.
You say that "it's a request from customer". My suggestion is that you tell the customer "this is a bad idea". Because it really is.
If the customer persists I guess your only option is to download the texts in whatever language it is that you need and store it in your apps external file storage. The suggestion to use a properties file is good. Load it as an InputStream and get strings from it.
Have you considered what will happen if the app is in flight mode or does not have a network available when started? Will you provide default values?
Also, with this solution you will not be able to set any texts in TextViews or similar in XML since the system won't have access to your downloaded translations in the properties files.
If you still go ahead with this I'd also suggest that you wrap the Properties file into some other class and fall back to whatever texts you have in your strings.xml if a text doesn't exist in the properties file or if it wasn't downloaded for some reason:
class TextRetriever {
// the downloaded texts go here
private Properties properties;
private Context context;
public String getString(String key) {
String s = properties.getProperty(key);
// not found in properties file
if(s == null) {
// fall back to value in strings.xml
s = context.getResources().getIdentifier(key, "string", context.getPackageName())
}
return s;
}
}
I am writing a client (android) /server (java) application. Server returns true or "false and error code" for every request made by client. I added every error message (string) to strings.xml in client side.
This is how i keep error codes;
public class ErrorCodes {
public static final int WRONG_USERNAME = 1;
public static final int WRONG_PASSWORD = 2;
public static final int WRONG_EMAIL= 3;
...
}
The problem is, everytime server sends an error code, i have to handle it like that (roughly);
int code = conn.receive();
if(code == ErrorCodes.WRONG_USERNAME)
print(getResources().getString(R.string.WRONG_USERNAME_ERROR));
else if(code == ErrorCodes.WRONG_PASSWORD)
print(getResources().getString(R.string.WRONG_PASSWORD_ERROR));
...
There are two approaches in my mind to ease this process but both not elegant.
Approach 1: Making ErrorCodes values equal to R.string values manually. Also with this approach i don't need to keep ErrorCodes class in client side too.
Problem: I have to update both ErrorCodes and strings.xml everytime i add a new error message.
Approach 2: Use generated R.java file in server, no need to update anything. This is the best i can think of.
Problem(s): There will be a lot of unnecessary stuff comes with R.java. I have to use it like that in server side: R.string.xyz. And if server is not coded with java, whole approach is useless.
If i use one of these, i can do this in client side;
int code = conn.receive();
print(getResources().getString(code));
I think there is a better way i can not think of. Because both way sounds painfully. How to handle this kind of situation?
I would suggest to keep your approach.
It will leads you to write some code for handling all the cases but at the end, for me, is the most robust solution because:
Your server won't be Android dependent. The logic behing your server will be valid for Android only. In case tomorrow you will develop one client for another platform it would be much better to have a platform-indipendent protocol.
Every time your edit your app, adding new resources and so on, your R file is generated again and R.string.something could have a different value than the previous build. So, in the situation you are rolling out an app update your users could experience strange errors because in the versione 1 of your app the resource 12345 could point to R.string.something and in the version 2 could point to R.string.something_else. But maybe for this point there is a method for keep a static R.string value.
The following solution seems acceptable to me:
Your server returns some code for your errors, for example:
1 for WRONG_USERNAME
2 for WRONG_PASSWORD
3 for WRONG_EMAIL
you could declare the string is this way:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="error_1">Wrong username!</string>
<string name="error_2">Wrong password!</string>
<string name="error_3">Wrong email!</string>
</resources>
And then you load the string resource by name:
int stringId = getResources().getIdentifier("error_"+errorCode,"string",getPackageName());
I use the same method you described in approach 1
If you only want to print errors, think about using json to send data from serwer, e.g.:
if(logged_on == true){
server.send("{\"status\":0}");
}else{
server.send("{\"status\":1,\"error\":"+ErrorString);
}
Then on the client side check the status field, and if equals 1 print string from the error field.
The best solution is obviously to keep error description strings on server, but that could introduce additional complexity for localization. You would need to send device's current interface language to the server to get correct error description
Otherwise I would use a short textual error identifiers on server side (e.g. "no_data_found", "db_timeout", etc) - something which makes sense and makes it easier to debug and modify string table for the android app.
On the client side, I would add corresponding error description strings, with identifiers following some standard scheme. For example: "error_no_data_found", "error_db_timeout".
Then, to retrieve error description string, you could use a reflection:
String getErrorString(String stringName)
{
try
{
Field f_a =R.string.class.getDeclaredField(stringName);
int a = f_a.getInt(null);
return getString(a);
}
catch (NoSuchFieldException x)
{
return "No error description for "+stringName;
}
catch (IllegalAccessException x)
{
return "";
}
}
...
String errorDescription = getErrorString("error_"+errorCodeReceivedFromServer);
Also, you can write a script to check if all of the server error identifiers have corresponding entries in strings.xml file.
i have a query regarding location files. i want to download localization files from a server (string.xml, string_ar.xml , etc) on the launch of the android application instead of declaring previously. is there any way to achieve this task..
Thanks in advance
If you want to know that the app is running for the first time, you could save a boolean value in the preferences. Something like first_run.
If you download localization files you will not be able to use the default localization support.
(Not sure why you wouldn't just place them in the apk)
What you could do is getting the localization and hitting the server for the correct translations.
You could create your own class that takes care of getting strings and if they do not exist, fail over the android one.
Something like:
public static String getString(Context ctx, int key) {
String ret = getStringFromDB(key);
if ( ret == null ) {
ret = ctx.getString(key);
}
return ret;
}
I've been struggling with this for a while. Basically, I want to have two applications (which will always be installed together) share preferences, with one of them being just a service which runs in the background and needs to use the preferences (should own the preferences but only really needs to read them) and the other app being a front-end UI app which needs to be able to write to the preferences file owned by the other app. The service will be doing things in the background (which may be determined by the preferences) and the UI will allow the user to edit the preferences and view some information from the service. However, they will be different packages/apps.
I tried following this tutorial which gave me a pretty good idea of how to have preferences in one app which can be read by another. Essentially, I create a new context through myContext = createPackageContext("com.example.package",Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); and then call myContext.getSharedPreferences("pref_name", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); However, I can't write to the preferences successfully from the outside app - (SharedPreferences.Editor).commit() returns false and I get a warning in logcat about being unable to edit pref_name.xml.bak.
How can I successfully set up my applications so both of them can read and write to the same preferences file (which is stored in the data folder of one of them)?
It is better to set private mode for the file. App needs to be signed with same set of certificates to share this file.
Set sharedUserId in both apps to be same.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.hello"
android:versionCode="1"
android:versionName="1.0"
android:sharedUserId="com.example">
....
Get Context from other package:
mContext = context.createPackageContext(
"com.example.otherapp",
Context.MODE_PRIVATE);
mPrefs = mContext.getSharedPreferences("sameFileNameHere", Activity.MODE_PRIVATE);
Get items as usual from SharedPreference. You can access it now.
First, I should note that this is not officially supported, although there may be a supported way to do this (i.e. it would NOT be this method) added to Android in the future (source for both claims: see second paragraph of this link).
Again, this is unsupported and is very possibly unstable. I primarily did this as an experiment to see if it was possible; take extreme caution if you are planning to actually incorporate this method into an application.
However, it appears to be possible to share preferences between applications if a few requirements are met. First, if you want App B to be able to access App A's preferences the package name of App B must be a child of App A's package name (e.g. App A: com.example.pkg App B: com.example.pkg.stuff). Additionally, they can't be wanting to access the file at the same time (I assume the same rules apply as for accessing them between activities, if you want to ensure atomic access you'll have to use additional safeguards such as .wait() and .notify(), but I won't go into that here).
Note: all of this works on the emulator on 2.2 and 2.3.3- I haven't extensively tested across devices or android versions.
Things to do in the app which is going to own the preferences (App A from above):
1.) Declare the SharedPreferences file
This is fairly simple. Simply declare a couple variables for your sharedpreferences file and the editor in your class and instantiate them in your onCreate method. You can put a string in the preferences now that you will use to make sure the other app can read it properly.
public class stuff extends Activity {
SharedPreferences mPrefs = null;
SharedPreferences.Editor mEd= null;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mPrefs = (getApplicationContext()).getSharedPreferences("svcprefs", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
mEd = mPrefs.edit();
mEd.putString("test", "original send from prefs owner");
mEd.commit();
2.) Set up the backup file
The getSharedPreferences method appears to check for a .bak file to load the preferences from. This is why it says in the documentation that it will not work across multiple processes; to minimize I/O, it loads the prefs ONCE when you grab them and only backs them up when you close your application/activity. However, if you call this from an outside application you will get a warning about not having the right file permissions for the folder (which is the first app's data folder). To fix this we are going to create the .bak file ourselves and make it publicly readable/writable. The way I chose to do this was to define three variables in my overall class.
final String[] copyToBackup = { "dd", "if=/data/data/com.example.pkg/shared_prefs/prefs.xml", "of=/data/data/com.example.pkg/shared_prefs/prefs.xml.bak", "bs=1024" };
final String[] mainFixPerm = {"chmod", "666", "/data/data/com.example.pkg/shared_prefs/prefs.xml"};
final String[] bakFixPerm = {"chmod", "666", "/data/data/com.example.pkg/shared_prefs/prefs.xml.bak"};
and make a function in my main class which would take these as arguments and execute them
public void execCommand(String[] arg0){
try {
final Process pr = Runtime.getRuntime().exec(arg0);
final int retval = pr.waitFor();
if ( retval != 0 ) {
System.err.println("Error:" + retval);
}
}
catch (Exception e) {}
}
It's not terribly pretty or good but it works.
Now, in your onCreate method (right after editor.commit())you will call this function with each of the three strings.
execCommand(copyToBackup);
execCommand(mainFixPerm);
execCommand(bakFixPerm);
This will copy the file and make both the main .xml and the .xml.bak files accessible to outside programs. You should also call these three methods in your onDestroy() to make sure the database is backed up properly when your app exits, and additionally call them right before you call getSharedPreferences elsewhere in your application (as otherwise it will load the .bak file which is likely out of date if another process has been editing the main .xml file). That's all you need to do in this application though, though. You can call getSharedPreferences elsewhere in this activity and it will grab all the data from the .xml file, allowing you to then call the getdatatype("key") methods and retrieve it.
Things to do in the accessing file(s) (App B from above)
1.) Write to the file
This is even simpler. I made a button on this activity and set up code in it's onClick method which would save something to the shared preferences file. Remember that App B's package must be a child of App A's package. We will be creating a context based on App A's context and then calling getSharedPreferences on that context.
prefsbutton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Context myContext = null;
try {
// App A's context
myContext = createPackageContext("com.example.pkg", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
} catch (NameNotFoundException e) {e.printStackTrace();}
testPrefs = myContext.getSharedPreferences("svcprefs", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
testEd = testPrefs.edit();
String valueFromPrefs = testPrefs.getString("test", "read failure");
TextView test1 = (TextView)findViewById(R.id.tvprefs);
test1.setText(valueFromPrefs);
testEd.putString("test2", "testback");
boolean edit_success = testEd.commit();
This grabs the string I set in the other application and displays it (or an error message) in a textview in this application. Additionally it sets a new string in the preferences file and commits the changes. After this runs, if your other application calls getSharedPreferences it will retrieve the file including the changes from this app.