Hi all bug reporting for your information. link
Problem details:
The Code - wifiManager.getConnectionInfo().getSSID()
The above code to returns the current SSID, it is returning the current SSID with extra quotations around it.
For eg. the SSID internet is returned as "internet".
This is only seen on Jelly bean 4.2 using device Nexus 7.
This bug is causing errors in our app as we compare the current SSID with the SSID that we are trying to connect too.
The code wifiManager.getScanResults(); however still returns all SSID's without extra quotation marks.
this is not a bug and behavior is correct as per documentation at http://developer.android.com/reference/android/net/wifi/WifiInfo.html#getSSID()
The so-called bug apparently was in pre 4.2 devices, because they didn't return it with "" enclosure.
Aiden's method looks good to me in the current state of confusion left by Android. However, being theoritically correct would just require
if (ssid.startsWith("\"") && ssid.endsWith("\"")){
ssid = ssid.substring(1, ssid.length()-1);
}
This regular expression is quite neat:
String ssid = wi.getSSID().replaceAll("^\"(.*)\"$", "$1");
Just for the notes
Edit °1 (as per question in the comment):
The issue that OP describes is, that on some devices the SSID returned by getSSID() is enclosed in "" whereas it is not on other devices. E.g. on some devices the SSID is "MY_WIFI" and on others it is MY_WIFI - or spoken in Java code: "\"MY_WIFI\"" and "MY_WIFI".
In order to to unify both results I proposed to remove the " at start and end - only there, because " is a legal character inside the SSID. In the regular expression above
^ means from start
$ means at end
\" means " (escaped)
.* means any number of characters
(...) means a capturing group, that can be referred by $1
So the whole expression means: replace "<something>" by <something> where $1 = <something>.
If there is no " at end/start, the regular expression doesn't match and nothing is replaced.
See Java Pattern class for more details.
For the mean time this is how I am getting around it, although its not great it will fix the issue.
public String removeQuotationsInCurrentSSIDForJellyBean(String ssid){
int deviceVersion= Build.VERSION.SDK_INT;
if (deviceVersion >= 17){
if (ssid.startsWith("\"") && ssid.endsWith("\"")){
ssid = ssid.substring(1, ssid.length()-1);
}
}
return ssid;
}
Two very simple variants:
string = string.replaceAll("^\" | \"$", "");
and
string = string.substring(1, string.length() - 1);
Faced the same problem! Used this technique which is backwards compatible:
if (suppliedSSID.equals(connectionInfo.getSSID()) || ("\"" + suppliedSSID + "\"").equals(connectionInfo.getSSID()) { DO SOMETHING }
Related
When using the URIs
String myUri = "https://evil.example.com\\.good.example.org/";
// or
String myUri = "https://evil.example.com\\#good.example.org/";
in Java on Android, the backslash in the host or user information of the authority part of the URI causes a mismatch between how Android’s android.net.Uri and android.webkit.WebView parse the URI with regard to its host.
The Uri class (and cURL) treat evil.example.com\.good.example.org (first example) or even good.example.org (second example) as the URI’s host.
The WebView class (and Firefox and Chrome) treat evil.example.com (both examples) as the URI’s host.
Is this known, expected or correct behavior? Do the two classes simply follow different standards?
Looking at the specification, it seems neither RFC 2396 nor RFC 3986 allows for a backslash in the user information or authority.
Is there any workaround to ensure a consistent behavior here, especially for validation purposes? Does the following patch look reasonable (to be used with WebView and for general correctness)?
Uri myParsedUri = Uri.parse(myUri);
if ((myParsedUri.getHost() == null || !myParsedUri.getHost().contains("\\")) && (myParsedUri.getUserInfo() == null || !myParsedUri.getUserInfo().contains("\\"))) {
// valid URI
}
else {
// invalid URI
}
One possible flaw is that this workaround may not catch all the cases that cause inconsistent hosts to be parsed. Do you know of anything else (apart from a backslash) that causes a mismatch between the two classes?
It's known that Android WebView 4.4 converts some URLs, in the linked issue are some steps described how to prevent that. From your question is not completely clear if your need is based in that issue or something else.
You can mask the backslashes and other signs with there according number in the character-table. In URLs the the number is written in hexademcimal.
Hexadecimal: 5C
Dezimal: 92
Sign: \
The code is the prepended with a % for each sign in the URL, your code looks like this after replacement:
String myUri = "https://evil.example.com%5C%5C.good.example.org/";
// or
String myUri = "https://evil.example.com%5C%5C#good.example.org/";
it might be required still to add a slash to separate domain and path:
String myUri = "https://evil.example.com/%5C%5C.good.example.org/";
// or
String myUri = "https://evil.example.com/%5C%5C#good.example.org/";
Is it possible that the backslashes never shall be used for network-communication at all but serve as escaping for some procedures like regular expressions or for output in JavaScript (Json) or some other steps?
Bonus ;-)
Below is a php-script that prints a table for most UTF-8-signs with the corresponding Numbers in hex and dec. (it still should be wrapped in an html-template including css perhaps):
<?php
$chs = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
$chs2 = $chs;
$chs3 = $chs;
$chs4 = $chs;
foreach ($chs as $ch){
foreach ($chs2 as $ch2){
foreach ($chs3 as $ch3){
foreach ($chs4 as $ch4){
echo '<tr>';
echo '<td>';
echo $ch.$ch2.$ch3.$ch4;
echo '</td>';
echo '<td>';
echo hexdec($ch.$ch2.$ch3.$ch4);
echo '</td>';
echo '<td>';
echo '&#x'.$ch.$ch2.$ch3.$ch4.';';
echo '</td>';
echo '</tr>';
}
}
}
}
?>
Is this known, expected or correct behavior?
IMO, it is not. For both URI and WebView. Because RFC won't allow a backslash, they could have warn it. However it is less important because it does not affect the working at all if the input is as expected.
Do the two classes simply follow different standards?
The URI class and WebView strictly follows the same standards. But due to the fact that they are different implementations, they may behave differently to an unexpected input.
For example, "^(([^:/?#]+):)?((//([^/?#]*))?([^?#]*)(\\?([^#]*))?)?(#(.*))?" this is the regular expression in URI which is used to parse URIs. The URI parsing of WebView is done by native CPP methods. Even though they follow same standards, chances are there for them to give different outcome (At least for unexpected inputs).
Does the following patch look reasonable?
Not really (See the answer of next question).
Do you know of anything else (apart from a backslash) that causes a
mismatch between the two classes?
Because you are so concerned about the consistent behavior, I won't suggest a manual validation. Even the programmers who wrote these classes can't list all of such scenarios.
The solution
If I understand correctly, you need to load URLs which is supplied by untrustable external sources (which attackers can exploit if there is a loop hole), but you need to identify it's host correctly.
In that case, you can parse it using URI class itself and use URI#getHost() to identify the host. But for WebView, instead of passing the original URL string, pass URI#toString().
I am using DynamoDB as back-end database for my mobile app, and the schema etc are identical across Android & iOS. For a particular use-case, I have to perform a Scan, based on two attributes which are not indexed. For iOS Objective C, I am using the following code:
AWSDynamoDBScanExpression *scanExpression = [AWSDynamoDBScanExpression new];
scanExpression.limit = [NSNumber numberWithInt:maxCount];
scanExpression.filterExpression = #"#l = :location AND event = :event";
scanExpression.expressionAttributeNames = #{#"#l":#"location"};
scanExpression.expressionAttributeValues = #{#":location":location,
#":event":EVENT_TASTING};
Both location and event are Strings. EVENT_TASTING is a String constant. This scan keeps returning zero results, even though I have validated that for the provided entries I should be receiving the results. I use the following code in Android Java:
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression();
scanExpression.setLimit(maxCount);
scanExpression.addFilterCondition("location",
new Condition()
.withComparisonOperator(ComparisonOperator.EQ)
.withAttributeValueList(new AttributeValue().withS(location)));
scanExpression.addFilterCondition("event",
new Condition()
.withComparisonOperator(ComparisonOperator.EQ)
.withAttributeValueList(new AttributeValue().withS(Constants.EVENT_TASTING)));
The scan works as expected in Android. What needs to change in iOS to make it work there too? I updated iOS SDK to 2.3.6 but it has not made a difference. This is the only scan operation I am doing in my code.
Is there an error in my scanExpression for iOS? Is there a way I can use the Android-style syntax to make this work on iOS?
Update
I tried the following changes:
AWSDynamoDBScanExpression *scanExpression = [AWSDynamoDBScanExpression new];
AWSDynamoDBAttributeValue *locationVal = [AWSDynamoDBAttributeValue new];
locationVal.S = location;
AWSDynamoDBAttributeValue *eventVal = [AWSDynamoDBAttributeValue new];
eventVal.S = EVENT_TASTING;
scanExpression.limit = [NSNumber numberWithInt:maxCount];
scanExpression.filterExpression = #"#l = :location AND event = :event";
scanExpression.expressionAttributeNames = #{#"#l":#"location"};
scanExpression.expressionAttributeValues = #{#":location":locationVal,
#":event":eventVal};
But now I am getting an error:
The request failed. Error: [Error Domain=com.amazonaws.AWSDynamoDBErrorDomain Code=0 "(null)" UserInfo={message=ExpressionAttributeValues contains invalid value: Supplied AttributeValue is empty, must contain exactly one of the supported datatypes for key :location, __type=com.amazon.coral.validate#ValidationException}]
Thanks to the hint from #YosukeMatsuda, I was able to fix this by calling Scan repeatedly until LastEvaluatedKey is empty. I am posting this as answer because unfortunately Mike's answer is not pointing out the correct issue and is misleading.
Here's how I changed the code in iOS:
// In a different method (for first call):
AWSDynamoDBScanExpression *scanExpression = // See code in original question
// In a new method that can be called recursively:
// DynamoDBObjectMapper scan:class-for-model expression:scanExpression
// continueWithBlock -> if (task.result):
AWSDynamoDBPaginatedOutput *paginatedOutput = task.result;
if (paginatedOutput.items.count != 0)
// Append the paginatedOutput.items to the cumulative array
else
// Replace the cumulative array with paginatedOutput.items
if (paginatedOutput.lastEvaluatedKey.count == 0) {
// Scan is complete - handle results
} else {
// Check if you have sufficient results
// In my case I had asked for 25 results but was getting 39
// So it doesn't seem to obey the scanExpression.limit value
// If more results are needed, continue the scan
[scanExpression setExclusiveStartKey:paginatedOutput.lastEvaluatedKey];
// Call this method recursively
}
If there is a more elegant solution I'd love to hear it. But at least it works now.
There are several differences between the Android code you're using and the ObjectiveC version.
in the Android Version you're using the older Filter Condition API while in the ObjectiveC you're using the more modern Filter Expression API; this doesn't necessarily make the newer one fail but it's just something to point out
in the case of ExpressionAttributeValues, the values for location and event that you're passing in should be of type AWSDynamoDBAttributeValue *, not strings; if you make this change your query will most likely start working.
I hope this answers your question but can't be certain because you only say "this works as expected in Android - how can I make it work in iOS" but you're not telling us what's broken.
I have tried matching Phone numbers with the regular expressions provided by Android in Patterns.Phone,this matches a lot of things that are not phone numbers.I have also tried using:
(?:(?:\+?1\s*(?:[.-]\s*)?)?(?:\(\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\s*\)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\s*(?:[.-]\s*)?)?([2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)?([0-9]{4})(?:\s*(?:#|x\.?|ext\.?|extension)\s*(\d+))?
However,I found that the Test was not successfull for all the inputs.I would like to validate the following inputs using a regular expression:
67450450
+9144-27444444
27444444
27470570
+12142261347
+61406366180
0891 2577456
2577456
+91 9550461668
9550461668
03-1234567
1860 425 3330
Basically any nymber format supported here:WTND
you can use the following code to check phone #:
private boolean validPhone(String phone) {
Pattern pattern = Patterns.PHONE;
return pattern.matcher(phone).matches();
}
if(validPhone("67450450")){
Toast.makeText(this,"The phone number is valid");
}
else
{
Toast.makeText(this,"The phone number is not valid");
}
This isn't clean/efficient, just thrown together to match your sample data:
\b\d{7,10}|\+\d{4}-\d{8}|\+\d{11}|\d{4}\s\d{7}|\+\d{2}\s\d{10}|\d{2}-\d{7}|\d{4}\s\d{3}\s\d{4}\b
I am using the following snippet to log all available (and unavailable) voices currently on phone:
ArrayList<String> availableVoices = intent.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
String availStr = "";
for (String lang : availableVoices)
availStr += (lang + ", ");
Log.i(String.valueOf(availableVoices.size()) + " available langs: ", availStr);
ArrayList<String> unavailableVoices = intent.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_UNAVAILABLE_VOICES);
String unavailStr = "";
for (String lang : unavailableVoices)
unavailStr += (lang + ", ");
Log.w(String.valueOf(unavailableVoices.size()) + " unavailable langs: ", unavailStr);
The logged result is somehwat bewildering, since I know beyond certainty that I have multiple languages installed and I can even hear the TTS speaking in eng-USA, yet the log shows:
1 available langs: eng-GBR,
30 unavailable langs: ara-XXX, ces-CZE, dan-DNK, deu-DEU, ell-GRC,
eng-AUS, eng-GBR, eng-USA, spa-ESP, spa-MEX, fin-FIN, fra-CAN,
fra-FRA, hun-HUN, ita-ITA, jpn-JPN, kor-KOR, nld-NLD, nor-NOR,
pol-POL, por-BRA, por-PRT, rus-RUS, slk-SVK, swe-SWE, tur-TUR,
zho-HKG, zho-CHN, zho-TWN, tha-THA,
Why is this inconsistent behavior? (note that eng-GBR appears in both the available and unavailable lists...)
It turns out that as far as text-to-speech in Android 2.x goes, it's the wild west out there: Every and any installed 3rd-party TTS engine can modify the output of this EXTRA_AVAILABLE_VOICES function however they desire, regardless whether checked/unchecked or selected/unselected as default.
I just tried uninstalling all TTS engines from my phone, leaving only the hard-coded Pico, and the result match exactly what I expected:
6 available voices: deu-DEU, eng-GBR, eng-USA, spa-ESP, fra-FRA,
ita-ITA,
0 unavailable voices:
I don't mind the output of this function dynamically refer to the currently selected (i.e. default) TTS engine, but the fact is that once a 3rd party TTS engine is installed, this function's output doesn't make any sense, because it ignores any settings.
Also note that the name misleading: It's available languages, not voices!
I am posting this answer with the hope that it will help someone save the time & agony of discovering this the hard way.
addressBar = (AutoCompleteTextView) mActivity.findViewById(package.R.id.addressBar);
TouchUtils.tapView(this, addressBar);
sendKeys("1"); //Works
sendKeys("G M A I L"); // Works - Result would be "gmail"
sendKeys("G M A I L . C O M"); // Doesn't work
sendKeys("{.}"); // Doesn't work
sendKeys("gmail") // Doesn't work
sendKeys("G M A I L {.} C O M") //Doesn't work
I am writing android test scripts using "InstrumentationTestCase2". I actually want to sendkeys - "gmail.com" but, unable to send special character "."(Dot)
For '.' (period or dot) you can try the int equivalent values of it.
Like,
sendKeys(56);
From Android-Docs
public static final int KEYCODE_PERIOD
Key code constant: '.' key.
Constant Value: 56 (0x00000038)
"The sequence of keys is a string containing the key names as specified in KeyEvent, without the KEYCODE_ prefix." (sendKeys documentation)
So you can use NUMPAD_DOT in the sendKeys string.
e.g.
sendKeys("G M A I L NUMPAD_DOT C O M");
For Further information see :
(http://developer.android.com/reference/android/test/InstrumentationTestCase.html#sendKeys(java.lang.String))
sendKeys(56); // for special character "." (Dot)
Have you tried the following:
getInstrumentation().sendStringSync("Do.You#Love.IT???");
works like magic and makes life a lot simpler!