I am building an Android Library and have a method getting some information about the device. Our target is to support 2.2 and up but was wondering if there is a way to collect information introduced in later versions (ex device serial in 2.3) and have the application set with version 2.2 to compile.
After searching around I found people using code like:
private static String getHardwareSerial() {
try {
return Build.SERIAL;
} catch (VerifyError e) {
//Android 8 and previous did not have this information
return Build.UNKNOWN;
}
}
However, with this code present, my sample application using our library fails to build when setting the build target to 8. Any suggestions or do we have to live with our clients setting their target to 9 to get this info?
You could do it through reflection:
public static String getHardwareSerial() {
try {
Field serialField = Build.class.getDeclaredField("SERIAL");
return (String)serialField.get(null);
}
catch (NoSuchFieldException nsf) {
}
catch (IllegalAccessException ia) {
}
return Build.UNKNOWN;
}
If the field isn't found (on earlier versions of the OS) it'll throw an exception that will be ignored and then fall through to return Build.UNKNOWN.
Related
I'm trying to use xerial sqlite-jdbc to manage my database in Android with no success.I'm getting an java.lang.NoClassDefFoundError: org.sqlite.SQLiteConnection exception.I've imported this dependency 'org.xerial:sqlite-jdbc:3.18.0' in my gradle.
My code is as follows,
try {
Class.forName("org.sqlite.JDBC");
Connection connection = DriverManager.getConnection("jdbc:sqlite:hs.db");
} catch (ClassNotFoundException eString) {System.err.println("Could not init JDBC driver - driver not found");
} catch (java.sql.SQLException e) {e.printStackTrace();}
Using android.database.sqlite in my project is not option so please don't suggest that as an answer.
Gradle
compile 'org.sqldroid:sqldroid:1.0.3'
JAVA
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
public class MainActivity extends AppCompatActivity {
private Connection connection;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
DriverManager.registerDriver((Driver) Class.forName("org.sqldroid.SQLDroidDriver").newInstance());
} catch (Exception e) {
throw new RuntimeException("Failed to register SQLDroidDriver");
}
String jdbcUrl = "jdbc:sqldroid:" + "/data/data/" + getPackageName() + "/my-database.db";
try {
this.connection = DriverManager.getConnection(jdbcUrl);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
#Override
public void onDestroy() {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
super.onDestroy();
}
}
https://github.com/SQLDroid/SQLDroid
I needed to share some database code between my server DB and my phone DB, so I spent a lot of time writing the common code to use JDBC.
However, what I found out, after already being neck deep in this endeavor, is that there essentially isn't a working JDBC implementation on Android. I would strongly urge you not to go down this path - both Xerial and SQLDroid JDBC drivers have significant problems that I uncovered only during testing.
Problems with Xerial
It just doesn't work on Android :) It doesn't even load - I ran into this issue and couldn't work around it even after trying for hours.
Problems with SQLDroid
See the Open Issues page - some of them were pretty scary to me. The deal breaker for me was the lack of batch support.
See the Known Issues page before you start.
Finally, I ended up creating an implementation of the small subset of the java.sql interfaces and methods that I needed, using the android.database.sqlite classes. This was far less painful than I imagined (it took me just a couple of hours, for the subset of the functionality I needed) and it works great!
Here's my way:
do some config in app/build.gradle, and copy a so file, looks ugly, but really works in my Android 9.0 system.
android {
defaultConfig {
ndk {
abiFilters "armeabi-v7a"
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
dependencies {
implementation 'org.xerial:sqlite-jdbc:3.34.0'
}
then copy this file sqlite-jdbc-3.34.0.jar!\org\sqlite\native\Linux\android-arm\libsqlitejdbc.so to app/libs/armeabi-v7a/, which looks like this: .
and the built apk looks like:
now you can enjoy javax.sql.DataSource of sqlite on android.
update gradle plugin version and gradle version to latest version
File -> Project Structure
project structure image
I know this question asked multiple time, i have googled it too but not found any working answer.
I am developing a android App with sqlite database and want to secure my database.db file from rooted phone, I have applied the check for rooted Device using below code but its not working on some Samsung and Redmi.
public class CheckRooted {
public static boolean isRooted() {
// get from build info
String buildTags = android.os.Build.TAGS;
if (buildTags != null && buildTags.contains("test-keys")) {
return true;
}
// check if /system/app/Superuser.apk is present
try {
File file = new File("/system/app/Superuser.apk");
if (file.exists()) {
return true;
}
} catch (Exception e1) {
// ignore
}
// try executing commands
return canExecuteCommand("su");
}
// executes a command on the system
private static boolean canExecuteCommand(String command) {
boolean executedSuccesfully;
try {
Runtime.getRuntime().exec(command);
executedSuccesfully = true;
} catch (Exception e) {
executedSuccesfully = false;
}
return executedSuccesfully;
}
}
Also suggest me how to protect my database.db file from rooted phones, I can't use any paid services like GreenDao or others.
I'm not very familiar with Android, but this can help you with your database encryption problem.
It is called "SQLCipher".
Check it out. It also has a community edition, which is also allowed to be used with commercial apps.
Further more, if the root checks aren't working on some Samsung and Redmi Devices, make sure they do not use slightly different commands.
Try testing for multiple root commands instead of just one single one.
I learnt a bit about reflection after reading about it in some tpics here. From what I understands, it is used to check the avaibility of a certain class/method/field at runtime. But is it really useful in Android ? Android provide us with the api version at runtime and we can know if a particular class/method or field is available by reading the Android doc (or with error message with Android Studio).
I understand how it can be useful with Java in general, but is there any meaning to use it in Android?
Reflection (in every languages) is very powerful.
In Android most of time reflection is not needed, because you can find Security Exceptions, problems. It depends on what You do.
If you use undocumented classes, libs, you can use it, and it's very useful.
Sometimes, to do particular things, like turn on/off 3g on old device, change device language, you need rooted device to use reflection.
Finally, depends always on what You do.
Sometimes it works , and some times it does't work .
E.T work example :
You can reflect the method to hang off a phone call (there are a lot example codes on Internet so I won't copy the code.).
Doesn't work example:
If you want to switch data connect status , use reflection works on 4.4 but will not work on 5.0 because it's a binder connection, the BN will check Permission the app granted , but this permission only granted to system app . So if your app is a third part app,on 5.0 you can't use reflection to switch data connect status.
Hope that helps
This is a very general question, it really depends on what you're trying to do. Sometimes you have to use reflection, if the APIs are hidden, all depends on your use case, generally you should avoid reflection as it complicates your code more than its needs to be and its potentially unsafe for further versions of android.
In my opinion it's a good to way to do particular things.
For example you can use the methods of PowerProfile class to do a simple power model for your phone.
By the method getAveragePower(POWER_WIFI_SCAN) you can take the average current in mA consumed by the subsystem (in this case: wi-fi during scan).
So to use PowerProfile's methods for get your battery capacity you you could use java reflection in this way:
private Object mPowerProfile_;
private static final String POWER_PROFILE_CLASS = "com.android.internal.os.PowerProfile";
private Double batteryCapacity = Double.valueOf(1);
public Double getBatteryCapacity(Context ctx) {
try {
mPowerProfile_ = Class.forName(POWER_PROFILE_CLASS).getConstructor(Context.class).newInstance(this);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
batteryCapacity = (Double) Class.forName(POWER_PROFILE_CLASS).getMethod("getAveragePower", String.class).invoke(mPowerProfile_, "battery.capacity");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
e.g If user clicks on check for update then action should be..
Check version of my application in market
If my application's current version and market version are not same then I just want to give a dialog of "Upgrade now".
Also wants to know is there any possibilities without WebServices for this aciton? because I have referred some answers with WebServices, instead of it is there any possibilities then please share your knowledge.
Thanks in advance :)
You can't get version of android market app, I wanna explain this... You can get current version of the app with this code:
public int getAppVersion(Context context) {
try {
PackageInfo packageInfo = context.getPackageManager()
.getPackageInfo(context.getPackageName(), 0);
return packageInfo.versionCode;
} catch (PackageManager.NameNotFoundException e) {
// should never happen
throw new RuntimeException("Could not get package name: " + e);
}
}
If you want to know the version that have android market, that it's impossible, you need to think alternative, webservice how you say... In my case, for example, I save the version of Android Market in my server, when I sync my app with my server I obtain the AndroidMarket version then in this moment I can compare versions and apply the necessary code, in your case showDialog.
Not exist alternative that you can get version from you android app market, why? The apps have their code and not it's the same code for all apps...
Tell me if I helped you and good programming!
public String getLatestVersionNumber()
{
String versionNumber = "0.0.0";
try
{
Document doc = Jsoup.connect("<market:urlofparticularapp>").get();
Elements changeLog = doc.select("div.clDesc");
for (Element div : changeLog)
{
String divText = div.text();
if (divText.contains("Version"))
{
return divText.split(" ")[1];
}
}
}
catch (IOException e)
{
e.printStackTrace();
}
return versionNumber;
}
Download Library of Jsoup from http://jsoup.org/download
As I know there is no way to get current version of market Application.
But i am doing this in my application by using of server.
I am putting the Upcoming app version on server and getting the current app version from app By using this code
oldVersion = Splash.this.getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
When app will launch then we will get the version from server and compare with app version for old user. If version will mismatch then give the popup for update for application using this code
Uri uri = Uri.parse("market://details?id=" + MainActivity.this.getPackageName());
Intent goToMarket = new Intent(Intent.ACTION_VIEW, uri);
try {
MainActivity.this.startActivity(goToMarket);
} catch (ActivityNotFoundException e) {
MainActivity.this.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://play.google.com/store/apps/details?id=" + MainActivity.this.getPackageName())));
}
I'm trying to use the SecureRandom workaround that Google posted in my android application:
http://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html
This work around involve writing to (and reading from) /dev/urandom. However, it looks like Samsung has enabled SELinux in such a way that prevents applications from accessing /dev/urandom.
I don't have one of these devices, so it is a little hard for me to test solutions, other than to push out attempts at workarounds on the Android market, but it seems like this is not an error that I can trap with a try catch block. It also appears that File.canRead and canWrite return true. You can see my attempts at workaround in the supportedOnThisDevice method in the following class:
PRNGFixes.java
I'm looking for a reliable way to detect if I am an such a device, and if so, not apply the Google SecureRandom workaround.
This is my way to check if SELinux is in enforce-mode - can be done via any Shell-script, not depending on RootTools:
private static boolean isSELinuxEnforcing() {
try {
CommandCapture command = new CommandCapture(1, "getenforce");
RootTools.getShell(false).add(command).waitForFinish();
boolean isSELinuxEnforcing = command.toString().trim().equalsIgnoreCase("enforcing");
return isSELinuxEnforcing;
} catch (Exception e) {
// handle exception
}
return false;
}
I've heard Samsung is starting to ship devices with the SELinux policy set to Enforce, but I don't know if it's true or not. As far as I know most devices on 4.3 still have it set to permissive.
According to Google, "SELinux reinforcement is invisible to users and developers, and adds robustness to the existing Android security model while maintaining compatibility with existing applications." So you may need to check the system properties or test it through a shell to find out for sure.
If you can get someone to send you their build.prop you may be able to catch it by comparing their ro.build.selinux property via System.getProperty("ro.build.selinux"),
but you'll also want to verify you're able to access it more directly in case it is unreliable or getProperty() for that is broken in future updates.
Root (System user on SELinux) is another option when available, but either way a shell based solution is probably your best bet.
System.getProperty("ro.build.selinux")
Did not work for me on Samsung S4 Android 4.3. So I wrote this
private static final int JELLY_BEAN_MR2 = 18;
public static boolean isSELinuxSupported() {
// Didnt' work
//String selinuxStatus = System.getProperty(PROPERTY_SELINUX_STATUS);
//return selinuxStatus.equals("1") ? true : false;
String selinuxFlag = getSelinuxFlag();
if (!StringUtils.isEmpty(selinuxFlag)) {
return selinuxFlag.equals("1") ? true : false;
} else {
// 4.3 or later ?
if(Build.VERSION.SDK_INT >= JELLY_BEAN_MR2) {
return true;
}
else {
return false;
}
}
}
public static String getSelinuxFlag() {
String selinux = null;
try {
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
selinux = (String) get.invoke(c, "ro.build.selinux");
} catch (Exception ignored) {
}
return selinux;
}
if you have access to the frameworks
import android.os.SELinux;
SELinux.isSELinuxEnforced();
Most devices as as of Jellybean MR2 and onwards will have SELinux enabled on their devices, but if you are working with OEMs or doing platform work this might not necessarily be true.
The method I am using to verify is with the getenforce shell command:
public boolean isSeLinuxEnforcing() {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec("getenforce");
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line);
}
} catch (Exception e) {
Log.e(TAG, "OS does not support getenforce");
// If getenforce is not available to the device, assume the device is not enforcing
e.printStackTrace();
return false;
}
String response = output.toString();
if ("Enforcing".equals(response)) {
return true;
} else if ("Permissive".equals(response)) {
return false;
} else {
Log.e(TAG, "getenforce returned unexpected value, unable to determine selinux!");
// If getenforce is modified on this device, assume the device is not enforcing
return false;
}
}
It appears that most devices are only writing a system property for selinux if they aren't running in enforcing status. You can additionally check the property: ro.boot.selinux to see if the Kernel passed in the permissive parameter on your current build.