Does anyone know how the missing permissions behave and when is it shown in logcat?
I tried removing the INTERNET permission intentionally to trigger this exception, but it's not being triggered at all during httpsURLConnection.connect() below -- what happens is that it goes straight to the finally block.
Initially I thought it's because the permission was granted before and the app/test device remembers it, so I uninstalled the app then reinstalled it but the same thing happens.
Does anyone know what triggered this behavior? Thanks!
Edit: I have another app (Sunshine app from the Udacity course) where I copied this code from, and that one shows the permission error.
Excerpt from my class -- expecting a Permission denied (missing INTERNET permission?) in the httpsURLConnection.connect() line
public class MovieDBAPI extends AsyncTask<String, Object, List<Movie>> {
final String TAG = getClass().getSimpleName();
protected List<Movie> doInBackground(String... params) {
BufferedReader bufferedReader = null;
HttpsURLConnection httpsURLConnection = null;
StringBuffer stringBuffer = null;
try {
//create a URL
URL url = new URL(buildURL(params[0]));
Log.v(TAG, url.toString());
httpsURLConnection = (HttpsURLConnection) url.openConnection();
httpsURLConnection.setRequestMethod("GET");
httpsURLConnection.connect();
//get string input
InputStream inputStream = httpsURLConnection.getInputStream();
if (inputStream == null) {
//no input stream, nothing to do
return null;
}
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line;
stringBuffer = new StringBuffer();
while((line = bufferedReader.readLine()) != null) {
// Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
// But it does make debugging a *lot* easier if you print out the completed
// buffer for debugging.
stringBuffer.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//if stringBuffer is not null, then prepare the result
if (stringBuffer != null) {
return getMovieDataFromJSON(stringBuffer.toString());
}
if (httpsURLConnection != null) {
httpsURLConnection.disconnect();
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
}
For Android M and above, check Android docs..
You don't need to permission access for normal permissions
https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous
https://developer.android.com/guide/topics/security/normal-permissions.html
For Marshmallow and above, you would need to request permissions at runtime. Mentioning them in AndroidManifest.xml isn't necessary,
Please refer to the following link,
https://developer.android.com/training/permissions/requesting.html
As for marshmallow and above android has a new run time permission system. You have ask for at run time . Add the following snippet into your class which will help you to ask for run time permission .
private int INTERNET_PERMISSION_CODE = 23;
//We are calling this method to check the permission status
private boolean isInternetnAllowed() {
//Getting the permission status
int result = ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET);
//If permission is granted returning true
if (result == PackageManager.PERMISSION_GRANTED)
return true;
//If permission is not granted returning false
return false;
}
//Requesting permission
private void requestInternetPermission(){
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.INTERNET)){
//If the user has denied the permission previously your code will come to this block
//Here you can explain why you need this permission
//Explain here why you need this permission
}
//And finally ask for the permission
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.INTERNET}, INTERNET_PERMISSION_CODE);
}
//This method will be called when the user will tap on allow or deny
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
//Checking the request code of our request
if(requestCode == INTERNET_PERMISSION_CODE){
//If permission is granted
if(grantResults.length >0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
//Displaying a toast
Toast.makeText(this,"Permission granted for internet",Toast.LENGTH_LONG).show();
}else{
//Displaying another toast if permission is not granted
Toast.makeText(this,"Oops you just denied the permission",Toast.LENGTH_LONG).show();
}
}
}
Then what ever you are doing in doInBackground method
do it like this way
if(isInternetnAllowed()){
//do your doInbackground stuff
}else {
requestInternetPermission();
}
Hope it helps
Related
Following is the code where activity is erroring out :
the error happens at line outputStream = new FileOutputStream(file);
public class MainActivity extends AppCompatActivity {
public void layoutToJPG(View v) {
View screen = v.getRootView();
// or I've also tried: View screen = new View(this);
screen.setDrawingCacheEnabled(true);
Bitmap b = screen.getDrawingCache();
Log.d("bitmap", b.toString());
File path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MOVIES);
File file = new File(path, "/sample.jpg");
FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
b.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
Toast.makeText(getApplicationContext(), "Saved to Gallery.", Toast.LENGTH_SHORT).show();
try {
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Error :
W/libc: Unable to set property "qemu.gles" to "1": connection failed; errno=13 (Permission denied)
W/System.err: java.io.FileNotFoundException: /storage/emulated/0/Movies/sample.jpg: open failed: EACCES (Permission denied)
at libcore.io.IoBridge.open(IoBridge.java:496)
at java.io.FileOutputStream.<init>(FileOutputStream.java:235)
The above is the error I am getting. I am running it on android emulator Pixel XL API 29
Does it require some special permissions to save file on the emulator.
As.
Android's permission system is one of the biggest security concern all
along since those permissions are asked for at install time. Once
installed, the application will be able to access all of things
granted without any user's acknowledgement what exactly application
does with the permission.
Android 6.0 Marshmallow introduces one of the largest changes to the
permissions model with the addition of runtime permissions, a new
permission model that replaces the existing install time permissions
model when you target API 23 and the app is running on an Android 6.0+
device
Try this code.
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
if (!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
1);
}
}
Add this in Menifest:
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
You have to add permission. And, ask user to accept permission.
Manifest :
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Request permission :
There's a library which you can use to ask for permission
Dexter.withContext(this)
.withPermission(Manifest.permission.CAMERA)
.withListener(new PermissionListener() {
#Override public void onPermissionGranted(PermissionGrantedResponse response) {/* ... */}
#Override public void onPermissionDenied(PermissionDeniedResponse response) {/* ... */}
#Override public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) {/* ... */}
}).check();
Over than API level 23(maybe, I actually forgot) you have to request user to grant permission. That's why you have to add above source code.
I'm trying to write a large multidimensional array into an ascii file in order to debug my program. (My phone has Android 8.0.0)
I added this line to my AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
The function is called within a subclass, so no context is available here:
public void writeAsAscii(float [][][][] labelProbArray, int Nx, int Ny, int Nz ) {
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
Log.i("State","External storage is writable");
} else{
Log.i("State","Error: External storage NOT WRITABLE");
}
File path = Environment.getExternalStorageDirectory();
File file = new File(path, "MyFile.txt");
try {
FileWriter writer = new FileWriter(file, true);
for (int i = 0; i < Nx; i++) {
for (int j = 0; j < Ny; j++) {
for (int k = 0; k < Nz; k++){
writer.write(String.valueOf(labelProbArray[0][i][j][k])+" ");
}
writer.write("\n"); // write new line
}
}
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
I'm following the advice given in related posts, but the writing fails. In debug mode I can see that path is set do /storage/emulated/0/MyFile.txt , but I cannot find the file anywhere on my phone that I use for debugging. So the file is probably never created. The try block is failing and the catch block reports:
java.io.FileNotFoundException: /storage/emulated/0/MyFile.txt (Permission denied)
I'm not sure what is going wrong. Is the file not created? Do I need to add more Permissions?
A short clear toy example how to write custom ascii files anywhere in the code would be nice, as it is crucial for debugging large arrays.
WRITE_EXTERNAL_STORAGE is a dangerous permission so you need to request this permission programmatically before trying to create a file in the file system.
Use this code to request permission from your activity onCreate():
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 12)
And after that check if it is granted or not:
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
when (requestCode) {
12 -> {
// If request is cancelled, the result arrays are empty.
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
// permission was granted
} else {
// permission denied
}
return
}
// Add other 'when' lines to check for other
// permissions this app might request.
else -> {
// Ignore all other requests.
}
}
}
More about permmission: https://developer.android.com/guide/topics/permissions/overview
I swear I've seen this done in other apps, but I can't think of any examples:
I would like to check if my app has been granted root or not (not request). If not, I would like to explain what's about to happen BEFORE requesting root (triggering the super user dialogue box).
Is there any way to do this? The only thing I can think of is to save a preference indicating that they've read the explanation before I check, but that's not ideal. Suggestions?
Try this piece of code:
/**
* Checks if the device is rooted.
*
* #return <code>true</code> if the device is rooted, <code>false</code> otherwise.
*/
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("/system/xbin/which su")
|| canExecuteCommand("/system/bin/which su") || canExecuteCommand("which 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;
}
Having some issues with a custom class that extends AsyncTask. My app is Targeting Android 4.0.3 and the below code works fine for 30+ people testing it. However there are two users that are seeing the app crash when I call new AsyncRequest like below.
I've got a working logger that is recording to a text file on the users storage and doesn't record the entry that is in the AsyncRequest constructor. So I have to assume that the crash is happening before the constructor is called.
One of the two devices that are experiencing this crash is running Android 4.0.4 apparently. Not sure what the other device is running. Unfortunately I dont' have access to the two devices so can't see a logcat output.
Any input as to why the object creation is causing a crash would be greatly appreciated.
String url = "www.google.com";
new AsyncRequest(callback, context).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url);
And here is the full AsyncRequest class
public class AsyncRequest extends AsyncTask<String, String, String>{
HttpURLConnection connection;
InputStream inStream;
IApiCallback callback;
Context context_;
public AsyncRequest(IApiCallback callback, Context context) {
// Log entry added for testing. Never gets called.
FileLogger.getFileLogger(context).ReportInfo("Enter AsyncRequest Constructor");
this.callback = callback;
context_ = context;
}
#Override
protected String doInBackground(String... uri) {
try {
URL url = new URL(uri[0] + "?format=json");
FileLogger.getFileLogger(context_).ReportInfo("Async Request: Sending HTTP GET to " + url);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.addRequestProperty("Accept-Encoding", "gzip");
connection.addRequestProperty("Cache-Control", "no-cache");
connection.connect();
String encoding = connection.getContentEncoding();
// Determine if the stream is compressed and uncompress it if needed.
if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
inStream = new GZIPInputStream(connection.getInputStream());
} else {
inStream = connection.getInputStream();
}
if (inStream != null) {
// process response
BufferedReader br = new BufferedReader(new InputStreamReader(inStream));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
return sb.toString();
}
} catch (SocketTimeoutException e) {
FileLogger.getFileLogger(context_).ReportException("Async Request: SocketTimeoutException", e);
Log.i("AsyncRequest", "Socket Timeout occured");
} catch (MalformedURLException e) {
FileLogger.getFileLogger(context_).ReportException("Async Request: MalformedUrlException", e);
} catch (IOException e) {
FileLogger.getFileLogger(context_).ReportException("Async Request: IOException", e);
Log.i("doInBackground:","IOException");
if (e != null && e.getMessage() != null) {
Log.i("doInBackground:",e.getMessage());
}
} catch (Exception e) {
FileLogger.getFileLogger(context_).ReportException("Async Request: Exception", e);
} finally {
if (connection != null)
connection.disconnect();
}
return null;
}
#Override
protected void onPostExecute(String result) {
if (result != null)
FileLogger.getFileLogger(context_).ReportInfo("Async Request: Response is valid");
else
FileLogger.getFileLogger(context_).ReportInfo("Async Request: Invalid response");
callback.Execute(result);
}
}
EDIT: As per comments below.
Here is the full method that I call my custom AsyncTask from. All the logging messages I have up to creating the AsyncTask are showing in the log. None of the exceptions are.
The logging displays the url value just before creating my AsyncRequest and the URL is not malformed at all. It's what I'm expecting.
public void GetServerInfoAsync(IApiCallback callback, Context context) throws IllegalArgumentException, Exception {
if (callback == null)
throw new IllegalArgumentException("callback");
if (context == null)
throw new IllegalArgumentException("context");
try {
FileLogger.getFileLogger(context).ReportInfo("Build URL");
String url = GetApiUrl("System/Info");
FileLogger.getFileLogger(context).ReportInfo("Finished building URL");
if (url != null) {
FileLogger.getFileLogger(context).ReportInfo("GetServerInfoAsync: url is " + url);
new AsyncRequest(callback, context).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url);
} else {
FileLogger.getFileLogger(context).ReportError("GetServerInfoAsync: url is null");
}
} catch (IllegalArgumentException iae) {
FileLogger.getFileLogger(context).ReportException("GetServerInfoAsync: IllegalArgumentException", iae);
throw iae;
} catch (Exception e) {
FileLogger.getFileLogger(context).ReportException("GetServerInfoAsync: Exception", e);
throw e;
}
}
First of all, just keep in mind that executeOnExecutor() is not available prior to Api 11. You have already said the issue is with a 4.0.4 device, but just keep this in mind.
Here are the steps I would take in order to troubleshoot what the problem is. It seems as if you have already done a few of these with all those ReportInfo() statements.
First, I assume your call to GetServerInfoAsync is within a try...catch, correct? I am checking because of your use of Throw. Also, you have already added logging to check for errors with the url. Since the errors occur before you actually use it, the error cannot be with the url, or any internet permissions.
You call the AsyncTask generation with references to callback and context. You have added logging via ReportInfo() which references context, and those work, yes? Therefore, context is not your issue. However, you never check what callback is. You throw an error if it is null, but you never do anything with it before you call AsyncRequest. Try a ReportInfo(callback.toString()) to see what it is.
If all else fails, it would seem to be an error with threading. Why not try using just AsyncTask, instead of executeOnExecutor(). Do you really need more than 1 background thread?
Sorry for not getting back to this sooner. There were numerous issues here.
First off... Thanks to Wolfram's suggestion I was able to catch the exception and diagnose that the issue was that my FileLogger (and another class) was a static reference and these two tablets couldn't find the reference at runtime. So I ended up removing Logging from my async methods.
Secondly, after making the above changes there was another issue which was that a looper had to be called from the main thread. It turned out that these two tablets weren't calling my async task from the main thread. So I had to enclose the async call in a new Handler using the MainLooper.
TL:DR; version ;)
my app should run without user interaction (autostart etc works)
it should update itself (via apk) without any user interaction
rooted devices are possible
.
problem:
querying a newer apk from a server works
when starting the apk with a (view?) intent, the "install app" prompt pops and needs a user confirmation
How do I solve this without any user interaction?
http://code.google.com/p/auto-update-apk-client/
This seems to be a solution, but there must be better approach.
I already found this: Install Application programmatically on Android
but that doesn't solve my problem.
Solved it! :D
It just works in rooted devices but works perfectly.
Using the unix cmd "pm" (packageManager) allows you to install apks from sdcard, when executing it as root.
Hope this could help some people in the future.
public static void installNewApk()
{
try
{
Runtime.getRuntime().exec(new String[] {"su", "-c", "pm install -r /mnt/internal/Download/fp.apk"});
}
catch (IOException e)
{
System.out.println(e.toString());
System.out.println("no root");
}
}
Required permissions:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
My suggestion is to use plugin mechanism instad of updating the app. You can dynamically load classes from the Web and run them inside your app without any user interaction. There is a lot of resources spread across the Internet:
How to load a Java class dynamically on android/dalvik?
http://android-developers.blogspot.com/2011/07/custom-class-loading-in-dalvik.html
If su -c doesn't work, try su 0 (only rooted devices can do su!)
The full answer looks like this:
private void installNewApk()
{
String path = mContext.getFilesDir().getAbsolutePath() + "/" + LOCAL_FILENAME;
mQuickLog.logD("Install at: " + path);
ProcessUtils.runProcessNoException(mQuickLog, "su", "0", "pm", "install", "-r", path);
}
With this class defined:
public class ProcessUtils {
Process process;
int errCode;
public ProcessUtils(String ...command) throws IOException, InterruptedException{
ProcessBuilder pb = new ProcessBuilder(command);
this.process = pb.start();
this.errCode = this.process.waitFor();
}
public int getErrCode() {
return errCode;
}
public String getOutput() throws IOException {
InputStream inputStream = process.getInputStream();
InputStream errStream = process.getErrorStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
String line;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line + System.getProperty("line.separator"));
}
br = new BufferedReader(new InputStreamReader(errStream));
while ((line = br.readLine()) != null) {
sb.append(line + System.getProperty("line.separator"));
}
return sb.toString();
}
public static String runProcess(String ...command) throws IOException, InterruptedException {
ProcessUtils p = new ProcessUtils(command);
if (p.getErrCode() != 0) {
// err
}
return p.getOutput();
}
public static void runProcessNoException(String ...command) {
try {
runProcess(command);
} catch (InterruptedException | IOException e) {
// err
}
}
}