I am new in Android, and I´m making an app and I want to focus on the secure of the app, I found this link, in it says "keep sensitive information in RAM for the minimum time possible by setting it to null after use." and later it says "avoid the use of Java’s String class to hold sensitive information. Instead use char arrays or byte arrays. The reason for this is because Strings are immutable"
In my app I have a code similar to this (this code just checks a PIN the users enters and compares it with another one in the internal storage):
public class Class extends Activity implements OnClickListener{
private static final String fileName = "FilePin";
private Button button;
private EditText editText = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_example);
editText = (editText) findViewById(R.id.editText);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(this);
}
#Override
public void onClick(View v){
if (readPin()) {
textView.setText(new char[]{' '}, 0, 0);
Intent intent = new Intent(this, OtherClass.class);
startActivity(intent);
}
}
// this method read the file where the PIN the user create is save in the internal storage
public boolean readPin(){
StringBuilder stringBuilder = null;
StringBuilder inputString;
try {
BufferedReader inputReader = new BufferedReader(new InputStreamReader
(openFileInput(fileName)));
stringBuilder = new StringBuilder();
while ((inputString = new StringBuilder(inputReader.readLine())) != null) {
stringBuilder.append(inputString);
}
inputReader.close();
} catch (Exception e) {
e.printStackTrace();
}
inputString = new StringBuilder("");
assert stringBuilder != null;
boolean comparePin = compare(stringBuilder);
stringBuilder = new StringBuilder("");
return comparePin;
}
// this method compare the PIN saved with the PIN the users enters
private boolean compare(StringBuilder pinSaved){
if (!editText.getText().toString().equals(pinSaved.toString())) {
Toast.makeText(getBaseContext(), "the PIN it´s incorrect"
, Toast.LENGTH_SHORT).show();
pinSaved = new StringBuilder("");
return false;
}
else {
pinSaved = new StringBuilder("");
return true;
}
}
}
For what I read in the previews link I didn´t use String instead I use StringBuilder because StringBuilder are mutable and after I use it a change the value to "stringBuilder = new StringBuilder("");", I didn´t use char[] because I don´t know how to save the editText to a char[] variable or how to save the PIN saved in the file in a char[] variable and I didn´t find examples about how to use char[] in those cases.
My questions are: this case is secure for an android app or is it better to change to char[] variables?, Is StringBuffer class insecure for Android? How can I save the editText value in a char[]? How Can I save a file in a char[] variable?
stringBuilder = new StringBuilder("");
The above is weaker than zeroing out a char[]. The original StringBuilder, along with is internal buffer, will stay in memory. At some point, the garbage collector will mark the memory as free, and later still, something might overwrite it.
Zeroing out a char[] gives you more control over when the secret is overwritten.
Also, StringBuilder comes with logic that automatically copies its internal char[] buffer when it needs more room. You need to be careful to make sure it never does this. A plain char[] is inconvenient because you can't resize it, but it's good in this case, because any attempt to copy it is explicit.
See Java: convert a char[] to a CharSequence for how to get your char[] into a form that you can pass to EditText.setText.
Related
I want to send UTF-8 text that is stored with ElasticSeach to an application via sockets.
I have a ThreadedTCPServer Implemented, here is the class that should handle the reply.
I have implemented basic string based handshaking to share some info like query was sent and that response will be sent.
class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
es = Elasticsearch()
#receive query from the client
query = self.request.recv(1024)
#Cut off the characters that aren't recognized
query=query[2:]
#for testing
query=query.lower().strip().replace(' ','_')
print query
#Send response that query was received
self.request.send("200...OK\n")
res = es.search(index="painters",body={"query": { "match" : {"title" : query}},"size":1 })
if res['hits']['hits']:
response = res['hits']['hits'][0]['_source']['text']
self.request.send("201...RE\n")
print response
response=response.encode('utf-8')
self.request.sendall(response)
On the android side I have two functions one for reading responses and one for reading bytes.
private String getResponse(InputStream is){
String line="";
BufferedReader rd = new BufferedReader(new InputStreamReader(is),8);
try{
line=rd.readLine();
}
catch (Exception e){
Toast.makeText(MainActivity.this, "Stream Exception", Toast.LENGTH_SHORT).show();
}
return line;
}
private String convertStreamToString(InputStream is) {
BufferedInputStream bi = new BufferedInputStream(is);
byte[] b = new byte[1024];
StringBuilder total = new StringBuilder();
try {
while (bi.read(b,0,1024)!=-1)
{
total.append(decodeUTF8(b));
Log.d("TOTAL",decodeUTF8(b));
}
} catch (Exception e) {
e.printStackTrace();
}
return total.toString();
}
And here is the function that should decode the string:
String decodeUTF8(byte[] bytes) {
return new String(bytes, UTF8_CHARSET);
}
Problem is Sometimes not the whole string is shown on the android Side,
and when the whole thing goes through some UTF-8 Characters end up deformed (totally different character than sent)
AsyncTask post execute that starts new Activty:
protected void onPostExecute(String s) {
//super.onPostExecute(s);
if (s.contains("ECONNREFUSED")){
Toast.makeText(MainActivity.this,"Connection Failed",Toast.LENGTH_LONG).show();
return;
}
Intent intent = new Intent(MainActivity.this,ReplyActivity.class);
intent.putExtra(EXTRA_MESSAGE,s);
startActivity(intent);
}
New Intent getting the string:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//get message
Intent intent = getIntent();
String summary = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
Example ouput:
Early life (1928–1949)
Andy Warhol ("né" Andrej Varhola, Jr.) was born on August 6, 1928 in Pittsburgh, Pennsylvania. He was the fourth child of Andrij Warhola (Americanized as Andrew Warhola, Sr., 1889–1942) and Júlia ("née" Zavacká, 1892–1972), w
As you can see even when sending the query from android to python I get some crap that I need to cut off.
here:
#Cut off the characters that aren't recognized
query=query[2:]
repr(response):
<h2>Early life (1928\xe2\x80\x931949)</h2>\nAndy Warhol ("n\xc3\xa9" Andrej Varhola, Jr.) was born on August 6, 1928 in Pittsburgh, Pennsylvania. He was the fourth child of Andrij Warhola (Americanized as Andrew Warhola, Sr., 1889\xe2\x80\x931942) and J\xc3\xbalia ("n\xc3\xa9e" Zavack\xc3\xa1, 1892\xe2\x80\x931972), whose first child was born in their homeland and died before their move to the U.S.
Terminal print:
<h2>Early life (1928–1949)</h2>
Andy Warhol ("né" Andrej Varhola, Jr.) was born on August 6, 1928 in Pittsburgh, Pennsylvania. He was the fourth child of Andrij Warhola (Americanized as Andrew Warhola, Sr., 1889–1942) and Júlia ("née" Zavacká, 1892–1972), whose first child was born in their homeland and died before their move to the U.S.
I'm using a http request lib to fetch xml content. The listener of the lib has the following functions:
void onDataReceived(char[] data)
{
}
void onRequestSucceeded()
{
}
After requesting a url, the lib will receive the data in many pieces, when every piece of data is received, the onDataReceived function will be called, and this piece of data will be passed in as parameter, and I have to concat all the pieces into one string. And after the request is finished, the onRequestSucceeded function will be called and the string is now the full content of the xml.
I'm doing it like this:
//init the result string
String resStr = new String("");
void onDataReceived(char[] data)
{
resStr += new String(data);
}
void onRequestSucceeded()
{
//now the resStr is ready for parse.
}
The problem is, sometimes my android device report OutOfMemoryError when concating the String. So I changed to StringBuffer like this:
//init the result string
StringBuffer resStr = new StringBuffer("");
void onDataReceived(char[] data)
{
resStr.append(data);
}
void onRequestSucceeded()
{
//now the resStr is ready for parse.
}
But resStr.toString() gives me weird content like "#bsdawevas". I suspect there's something wrong about the encoding, But I don't know how to solve it.
Any ideas?
Try resStr.append(new String(data));
use
String str = resStr.toString();
System.out.println(str);
toString() will convert from StringBuffer to String object
I have an Android broadcast receiver (using Eclipse if it matters) that is started by a service that is suppose to check if an online data version is updated. If so, I want to trigger a notification in the notification bar.
Everything seems to work OK, however, the results it gets from SharedPreferences are just not reliable. The broadcast receiver is suppose to get the current value stored in the SharedPreferences and compare it to a value it gets from a web url. If it compares the value and sees an update has been made (i.e. the value in at the url is greater than the stored value) it triggers a notification and also updates the value stored in the SharedPreferences. The broadcast receiver checks this update once per day and this only is intended to notify the users, nothing more.
The code I am using to do this seems to work. But here and there, it pulls a default value from the SharedPreferences and triggers an update notification even though nothing has changed.
I realize that I may be trying to do too much in the onReceive of the broadcast receiver, however I am very confused on how to fire an activity to do the work from the broadcastreceiver.
Does anyone have any code examples of the best way to implement this? I have searched and searched but I cannot seem to find clear steps to do this either way. Here is what I have:
public class AlarmReceiver extends BroadcastReceiver {
public static JSONArray myJArray2;
public static InputStream is = null;
public static final String URL_DatabaseVersion = "http://www.mysite.com/mobile/version.php";
public static NotificationManager nm;
public static Boolean updated = false;
public static int latestVersion = 0;
public static int internalVersion = 2;
public static final String PREFS_NAME = "MyPrefsFile";
#Override
public void onReceive(Context context, Intent intent) {
updated = false;
SharedPreferences settings = context.getSharedPreferences(PREFS_NAME, 0);
internalVersion = settings.getInt("dataversion", 1);
InputStream is = null;
String result = "";
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("",""));
try{
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(URL_DatabaseVersion);
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
is = entity.getContent();
}catch(Exception e){
}
try{
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"iso-8859-1"),8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null)
{
sb.append(line + "\n");
}
is.close();
result=sb.toString();
}catch(Exception e){
}
try{
JSONArray myJArray = new JSONArray(result);
myJArray2 = myJArray;
}catch(JSONException e){
}
int mode = Activity.MODE_PRIVATE;
SharedPreferences mySharedPreferences = context.getSharedPreferences("MyPrefsFile",mode);
SharedPreferences.Editor editor = mySharedPreferences.edit();
editor.putInt ("dataversion", latestVersion);
editor.commit();
if (!Arrays.asList(myJArray2).contains(null))
{
try{
for(int i=0;i<myJArray2.length();i++)
{
JSONObject json_data = myJArray2.getJSONObject(i);
latestVersion = json_data.getInt("title");
if (internalVersion < latestVersion)
{
updated = true;
}
else
{
updated = false;
}
}
}
catch(Exception e)
{}
finally
{}
}
else
{
updated = false;
}
if (updated)
{
nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
CharSequence from = "Database Updated";
CharSequence message = "I: " + internalVersion + " L: " + latestVersion + "Click to open.";
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, new Intent(), 0);
Notification notif = new Notification(R.drawable.icon, "Database Updated", System.currentTimeMillis());
notif.setLatestEventInfo(context, from, message, contentIntent);
nm.notify(1, notif);
}
}
}
Yes, I am probably trying to do way too much in a BroadcastReceiver and that might be causing the issue, however I cannot figure out how to do this any other way. When I log the results of this code, the first time through it works great. Second time through the internalVersion comes up 0. The value it gets from the URL is ALWAYS correct, it is the internalVersion, the one it pulls from SharedPreferences which is wrong many times.
Any thoughts? Sorry so long. Thanks.
Corey
SharedPreference.Editor.commit() is a blocking call. This will take time and could result in likely ANRs.
Use apply() on API 9 and up and thread this off before while storing the expected value locally on older API versions, move this code to a service, and log your exceptions so you can debug appropriately for race conditions like this.
You should write/commit your changes only when you realize, something has changed - that is in the "if (updated)" branch. And you should not use static members but commit the value you read from the server.
Say your Receiver has been started anew (in a new process). In this case static values are starting with their initial values, so latestversion is "0". You commit this value.
Next run you read the "0". Now internalversion is lower than the server version - even if no change has happened.
Try by using:
int mode = Context.MODE_PRIVATE;
instead of
int mode = Activity.MODE_PRIVATE;
It'll start saving values in SharedPreferences.
I want to add the default search app widget to a view inside my app. Just like the homescreen but without any dragging and I only want the workspace to be 4x1 big.
How can I do this? I have looked inside the sources for the launcher but its a lot to understand and remove because I want it as simple as possible...
I know that I can get the Component name of the widget like this:
private ComponentName getSearchWidgetProvider() {
SearchManager searchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
ComponentName searchComponent = null;
searchManager.getSearchableInfo(searchComponent);
if (searchComponent == null) return null;
return getProviderInPackage(searchComponent.getPackageName());
}
private ComponentName getProviderInPackage(String packageName) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders();
if (providers == null) return null;
final int providerCount = providers.size();
for (int i = 0; i < providerCount; i++) {
ComponentName provider = providers.get(i).provider;
if (provider != null && provider.getPackageName().equals(packageName)) {
return provider;
}
}
return null;
}
So how can I add it inside a view (lets say a LinearView) in the easiest way...
Thanks!
Use AppWidgetHost. I actually needed this to store and read the packagename of the widget. See, we both helped out. AppWidgetHost can be implemented to be added to LnearLayout. I woud use RelativeLayout if I was you, since RelativeLayout allows custom areas, per se if you wanted it centered, then you can. Unless you use LinearLayout inside a RelativeLayout.
Here is how it is implemented:
You can edit this and change it to the way you like. The call "appWidgetId", is the int given for each widget. You can store that, then have your app check if the file that is stored exists, read it, convert the text to string, then convert the string to int.
Storing info:
String path="/sdcard/widget.txt";
//You can change the path to whatever you want it to be
File f=new File(path);
try{
FileWriter fw=new FileWriter(f);
fw.write("whatever text");
fw.flush();
fw.close();
}catch(IOException e){
e.printStackTrace();
}
Now to read the file, you must call a StringBuilder, BufferedReader, and String line to get the info.
File f=new File("/sdcard/widget.txt");
//the path must be where you stored the files
StringBuilder sb=new StringBuilder();
try{
BufferedReader br=new BufferedReader(new FileReader(f));
String line;
while((line=br.readLine())!=null){
sb.append(line);
sb.append('\n');
}
br.close();
}catch(IOException e){
e.printStackTrace();
}
Now all you need is get the text into a string:
String widId=sb.toStrng();
And then convert that String to int:
int wid=Integer.parseInt(widId);
Now you impement that ID inside this code:
public void createWidget(Intent data) {
Bundle extras = data.getExtras();
int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(rl.getWidth(), rl.getHeight()/3);
lp.topMargin = numWidgets * (rl.getHeight()/3);
AppWidgetHostView hostView = mAppWidgetHost.createView(getActivity().getApplicationContext(), appWidgetId, appWidgetInfo);
hostView.setAppWidget(widID,appWidgetInfo);
rl.addView(hostView, lp);
hostView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
numWidgets ++;
}
This will add the selected widget to the layout.
new to the community. been up all night trying to flesh out the underlying html reading system that's at the core of my app's functionally. I could really use a fresh pair of eyes on this one.
Problem: While trying to return a string to be displayed on my app's home activity, I've run into an issue where I'm almost certain that the data was taken in correctly, cleaned up into XML via "Html Cleaner" (http://htmlcleaner.sourceforge.net/), and pulled through Jaxen (opensource Xpath) the result should display some text. Problem is of course, dispite my efforts I've yet to figure out exactly why it wont. My code follows below.
As a test I'm trying to pull the word "maps" from the http://www.google.com home page which is inside an tag with the hyperlink "http://maps.google.com/maps?hl=en&tab=wl" (which i'm using to uniquely identify the tag):
public class home extends Activity {
TextView text1;
//** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
text1 = (TextView)findViewById(R.id.text1);
text1.setText(LoadHTMLFromURL("http://www.google.com"));
}
private String LoadHTMLFromURL(String url)
{
try
{
// Load data from URL
InputStream is = (InputStream) new URL(url).getContent(); //generate
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder stringBuilder = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null)
{
stringBuilder.append(line + "");
}
is.close();
String HTMLout = stringBuilder.toString();
// Clean up HTML input.
//Initialize HTML Cleaner.
HtmlCleaner cleaner = new HtmlCleaner();
// This next line Cleans the html and exports it to a Tagnode named "node"
TagNode node = cleaner.clean(HTMLout);
// This is the xpath parsing info
String SearchTerm = "//a[#href='http://maps.google.com/maps?hl=en&tab=wl']";
Object[] info_nodes = node.evaluateXPath(SearchTerm);
TagNode info_node = (TagNode) info_nodes[0];
String info = info_node.getChildren().iterator().next().toString().trim();
return info;
}
catch (Exception e)
{
System.out.println( "Inside: home.LoadHTMLFromURL()" + "Exc="+e);
return null;
}
}
}
I apologize for the clutter, and lack of neatness in the code, still a mid to low range programer in a "learn as you go" stage of my ability. Any advice is appreciated.
side note: I ran a string containing some hand made simple XML to test if it would read the info, and it worked perfectly but not on xml generated from html webpages.
Ok, I believe the issue was my search term. my xpath term was typed wrong.