Android Camera2 API SecurityException - android

I'm trying to use the new Android camera2 api. I started with source from this tutorial : http://jylee-world.blogspot.com/2014/12/a-tutorial-of-androidhardwarecamera2.html . When I try to usb-debug-deploy it to any phone, I get a SecurityException from CameraManager.openCamera(...).
My AndroidManifest looks like this:
<uses-feature android:name="com.android.hardware.camera2.full"/>
<uses-permission android:name="android.permission.CAMERA"/>
This seems to be what every tutorial I've been able to find does. I'm able to get permission for other actions; for example, I can make the camera vibrate just fine. I'm also able to enumerate cameras with CameraManager.getCameraIdLists() just fine, but I'm not sure if that actually requires permission. But I can't openCamera.
Are there some additional permissions I need? Am I doing something wrong?
Thanks for the help!
This is my full stack trace:
SecurityException
java.lang.SecurityException: Lacking privileges to access camera serviceat android.hardware.camera2.utils.CameraBinderDecorator.throwOnError(CameraBinderDecorator.java:108)
at android.hardware.camera2.legacy.CameraDeviceUserShim.connectBinderShim(CameraDeviceUserShim.java:336)
at android.hardware.camera2.CameraManager.openCameraDeviceUserAsync(CameraManager.java:327)
at android.hardware.camera2.CameraManager.openCamera(CameraManager.java:457)
at com.example.quinnfreedman.camera2test.MainActivity$1.onSurfaceTextureAvailable(MainActivity.java:74)
at android.view.TextureView.getHardwareLayer(TextureView.java:368)
at android.view.View.updateDisplayListIfDirty(View.java:15167)
at android.view.View.draw(View.java:15964)
at android.view.ViewGroup.drawChild(ViewGroup.java:3612)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3402)
at android.view.View.updateDisplayListIfDirty(View.java:15185)
at android.view.View.draw(View.java:15964)
at android.view.ViewGroup.drawChild(ViewGroup.java:3612)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3402)
at android.view.View.updateDisplayListIfDirty(View.java:15185)
at android.view.View.draw(View.java:15964)
at android.view.ViewGroup.drawChild(ViewGroup.java:3612)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3402)
at android.view.View.updateDisplayListIfDirty(View.java:15185)
at android.view.View.draw(View.java:15964)
at android.view.ViewGroup.drawChild(ViewGroup.java:3612)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3402)
at android.view.View.draw(View.java:16197)
at com.android.internal.policy.PhoneWindow$DecorView.draw(PhoneWindow.java:2690)
at android.view.View.updateDisplayListIfDirty(View.java:15190)
at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:281)
at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:287)
at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:322)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:2627)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2446)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2079)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1119)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6060)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
at android.view.Choreographer.doCallbacks(Choreographer.java:670)
at android.view.Choreographer.doFrame(Choreographer.java:606)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
at android.os.Handler.handleCallback(Handler.java:746)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5443)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)

In Android M, run time permission check is required for dangerous permission. You can see dangerous permission here.
Check for permission :
// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.CAMERA);
If the app has the permission, the method returns PackageManager.PERMISSION_GRANTED, and the app can proceed with the operation. If the app does not have the permission, the method returns PERMISSION_DENIED, and the app has to explicitly ask the user for permission.
For details: https://developer.android.com/training/permissions/requesting.html#perm-request

Just close your camera device in onSurfaceTextureDestroyed function
onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture){cameraDevice.close();cameraDevice = null;}
Security exception will get fix

When you are running your application on android 6+, you need to give the famous runtime permissions.
https://developer.android.com/training/permissions/requesting.html
The permission you are trying to give is considered a dangerous permissions android.
https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous
Different of other responses about runtime permission, I suggest you to use this https://github.com/Karumi/Dexter
This lib makes the permission handling easily

Struggled with this off and on for weeks, thinking several times I'd solved the problem. In the end, none of the "fixes" I'd read about here worked. Then, after putting in ~100 Log.v statements in my Java, I realized it was a threading issue that might, or might not, kick off this error depending on events on the camera. Basically, I think, the main program was running on the main thread, but there was an extra thread kicked off by the following statement:
//this code seems to be the culprit ... commenting it out solve my problem
private void showToast(final String text) {
final Activity activity = MyStupidProgram.this;
if (activity != null) {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(activity, text, Toast.LENGTH_SHORT).show();
}
});
}
}
So while there's nothing in this statement calling the camera, perhaps because of thread safety, Android 5.x and 6.x were throwing security errors when I called showToast('some crap');
Commenting that out and just using a Toast.makeText('blah blah'); statement, I was able to get rid of the security error.
Additionally, I added this to the code on the page's onCreate(); statement, to catch any issues on the main thread:
Thread.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(
Thread paramThread,
Throwable paramThrowable
) {
//Do your own error handling here
if (exceptionHandler != null)
exceptionHandler.uncaughtException(
paramThread,
paramThrowable
); //Delegates to Android's error handling
else
System.exit(2); //Prevents the service/app from freezing
}
});

Related

What permission do we need to use instead of WRITE_EXTERNAL_STORAGE in Android 13 app on Android 13 device?

What permission do we need to use instead of WRITE_EXTERNAL_STORAGE in Android 13 app on Android 13 device? I have read through the documentation but not able to get what permission we need to use. The function which needs this is a photo picker which access the images, before it used to ask for permission on Android 12, but once the app was upgraded to Android 13 and used on an Android 13 app, the photo picker stopped working. It uses the permission WRITE_EXTERNAL_STORAGE which is not required anymore. But what needs to be used instead of this?
The function is from a library - react-native-image-crop-picker
#ReactMethod
public void openPicker(final ReadableMap options, final Promise promise) {
final Activity activity = getCurrentActivity();
if (activity == null) {
promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
return;
}
setConfiguration(options);
resultCollector.setup(promise, multiple);
initiatePicker(activity);
permissionsCheck(activity, promise, Collections.singletonList(Manifest.permission.WRITE_EXTERNAL_STORAGE), new Callable<Void>() {
#Override
public Void call() {
return null;
}
});
}
Use react-native-permissions for managing the permissions.
The docs are quite simple, kindly go through this.
Some permission changes were required for this in the react-native-image-cropper library - https://github.com/ivpusic/react-native-image-crop-picker/pull/1852

How to check if user has given camera or location permissions (android) UNITY

I really struggle with this since a while :( as I need an solution that works within UNITY3D.
I need to check if the user has given the permission to access the Android device camera (and location on a second level).
Normally the app start by asking for this permissions at launch, but if the user denies the access for the camera I need to know and check that later.
Otherwise the user could hit the camera UI button I made and try to access the camera via webcamtexture... and that leads into a crash of the app.
Since Android API 23 you cannot ignore or already grant permissions by changing the android manifest like I tried after reading several posts about that.
Thank's to everyone who has an idea to solve this.
Check this library: https://github.com/sanukin39/UniAndroidPermission
In that library I got these methods to check and request Permission.
public static void requestPermission(String permissionStr){
if(!hasPermission(permissionStr)) {
UnityPlayer.currentActivity.requestPermissions(new String[]{permissionStr}, 0);
}
}
public static boolean hasPermission(String permissionStr) {
if(Build.VERSION.SDK_INT < 23) {
return true;
}
Context context = UnityPlayer.currentActivity.getApplicationContext();
return context.checkCallingOrSelfPermission(permissionStr) == PackageManager.PERMISSION_GRANTED;
}
Hope it helps:)

React-Native (Android) : ArrayIndexOutOfBoundsException thrown when requesting for permissions

Recently I have encountered a very strange issue while working with a React-Native app. Here's my environement:
Rect-Native 0.42.3
Samsung Galaxy Note 5
Android 6.0
As you may know, the Android permission model has changed since Marshmallow, but some days ago, I got his exception:
java.lang.RuntimeException:
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4156)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4250)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1839)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7229)
at java.lang.reflect.Method.invoke(Native Method:0)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
Caused by: java.lang.ArrayIndexOutOfBoundsException:
at com.facebook.react.modules.permissions.PermissionsModule$1.invoke(PermissionsModule.java:119)
at com.facebook.react.modules.permissions.PermissionsModule.onRequestPermissionsResult(PermissionsModule.java:207)
at com.facebook.react.ReactActivityDelegate$1.invoke(ReactActivityDelegate.java:211)
at com.facebook.react.ReactActivityDelegate.onResume(ReactActivityDelegate.java:131)
at com.facebook.react.ReactActivity.onResume(ReactActivity.java:66)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1286)
at android.app.Activity.performResume(Activity.java:6987)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4145)
First, I thought I was missing a check in the code, as explained here but this is not the case as otherwise I would have a message like this instead:
java.lang.SecurityException: Permission Denial: opening provider com.android.providers.contacts.ContactsProvider2 from ProcessRecord{fac49ea 4674:com.gospacesmobile/u0a65} (pid=4674, uid=10065) requires android.permission.READ_CONTACTS or android.permission.WRITE_CONTACTS
I looked at React-Native's code and found this:
// com.facebook.react.modules.permissions.PermissionsModule.java
int[] results = (int[]) args[0];
if (results[0] == PackageManager.PERMISSION_GRANTED) { // <- the exception happens when results is empty
promise.resolve(GRANTED);
} else {
PermissionAwareActivity activity = (PermissionAwareActivity) args[1];
if (activity.shouldShowRequestPermissionRationale(permission)) {
promise.resolve(DENIED);
} else {
promise.resolve(NEVER_ASK_AGAIN);
}
}
According the documentation, the grantResults variable may be empty whenever the permission request has been canceled:
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
Unfortunately, I do not have the client code (as the crash report in the google console dev does not mention it). In any case, shouldn't React-Native check for empty grantResults? Is this a bug? If yes, how could this be reproduced? I don't see any way this could be done, except by canceling the promise responsible for ensuring if a permission has been enabled or not (which is impossible considering react-native permissions API).
Any thought is very welcome. Thank you in advance.
So I finally found the problem. Whenever the permission request dialog appear, if the user minimizes the app, and then goes back to it by clicking on the app's icon in from the home menu, the results is empty which makes the app crashing.
This seems to be quiet a simple issue, and I've decided to monkey patch React-Native in order to prevent an unstable upgrade (Now I check if the results array is empty or not, and if it is, then deny the request). I still don't know if this is a common issue or something that comes from my app.

Wait until permisison request result received in Android

I've seen SO question Can you request permissions synchronously in Android Marshmallow (API 23)'s runtime permissions model?. The answer is no.
Hence, I added a code as below (simplified version):
public class MyActivity ... {
private boolean hasGotPermissionRequestResult = false;
#Override
public void onCreate(...) {
if (ContextCompat.checkSelfPermission(...) == PackageManager.PERMISSION_DENIED) {
ActivityCompat.requestPermission(...);
while (!hasGotPermissionRequestResult) {}
}
}
#Override
public void onRequestPermissionResult(...) {
// whether granted or not
hasGotPermissionRequestResult = true;
}
}
However, I am not sure whether my approach is nice, safe and efficient.
Well, what you trying to achieve simply ain't possible, however there are several ways to overcome this:
Only trigger the method you wanna call when the permission is granted.
If you'd like to make the User only uses your app because that particular permission is so important that your app will not function without it, then use an educated screen to tell the users why you would want to use this permission in an intro screen kinda way.
I've created a library just for this kind of scenario where the library simplify the Permissions for you, it can be also used as a stand Alone Activity that has an Intro to your permission. you could check it out in Github PermissionHelper Github

Permission denial: writing to settings requires android.permission.WRITE_SETTINGS

For a very limited number of users, my app is crashing with the following:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.cloud3squared.meteogram.pro/com.cloud3squared.meteogram.MeteogramWidgetConfigureActivity}:
java.lang.SecurityException: Permission denial: writing to settings requires android.permission.WRITE_SETTINGS
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2693)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2758)
at android.app.ActivityThread.access$900(ActivityThread.java:177)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1448)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5942)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1388)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1183)
Caused by: java.lang.SecurityException: Permission denial: writing to settings requires android.permission.WRITE_SETTINGS
at android.os.Parcel.readException(Parcel.java:1540)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:185)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:137)
at android.content.ContentProviderProxy.call(ContentProviderNative.java:643)
at android.provider.Settings$NameValueCache.putStringForUser(Settings.java:1204)
at android.provider.Settings$System.putStringForUser(Settings.java:1505)
at android.provider.Settings$System.putIntForUser(Settings.java:1616)
at android.provider.Settings$System.putInt(Settings.java:1607)
at android.app.WallpaperManager$Globals.getDefaultWallpaperLocked(WallpaperManager.java:483)
at android.app.WallpaperManager$Globals.peekWallpaperBitmap(WallpaperManager.java:303)
at android.app.WallpaperManager.getDrawable(WallpaperManager.java:550)
at com.cloud3squared.meteogram.ax.c(Unknown Source)
at com.cloud3squared.meteogram.MeteogramWidgetConfigureActivity.a(Unknown Source)
at com.cloud3squared.meteogram.MeteogramWidgetConfigureActivity.c(Unknown Source)
at com.cloud3squared.meteogram.MeteogramWidgetConfigureActivity.onCreate(Unknown Source)
at android.app.Activity.performCreate(Activity.java:6288)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
... 10 more
Whilst a solution to the issue is given in the crash report (just add android.permission.WRITE_SETTINGS to the Manifest), it troubles me that I need to, because my app doesn't knowingly write to settings, and the vast majority of users don't have any issue without the permission.
It is particularly troubling because the docs say that:
There are a couple of permissions that don't behave like normal and
dangerous permissions. SYSTEM_ALERT_WINDOW and WRITE_SETTINGS are
particularly sensitive, so most apps should not use them. If an app
needs one of these permissions, it must declare the permission in the
manifest, and send an intent requesting the user's authorization. The
system responds to the intent by showing a detailed management screen
to the user.
I want to do everything I can to avoid having this permission in my app, given that it's apparently "dangerous", and having to request this permission at runtime is enough to scare off even non-tin-hatted users. I really don't need to write to settings... I write to SharedPreferences and that's about it.
So is there any way of avoiding this problem without requiring the permission?
EDIT to add code as per comment below (this function is called from my Activity):
static String getAverageWallpaperColour(Context context) {
final WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
final Drawable wallpaperDrawable = wallpaperManager.getDrawable();
Bitmap bmp = ((BitmapDrawable)wallpaperDrawable).getBitmap();
int color = getAverageColour(bmp); // details of function not relevant... just reads the bmp to work out an average colour
return colorToHexString(color); // details of function not relevant... just converts into color to String in particular format
}
I have now determined that the problem lies in the code snippet I added to the Question in an edit:
static String getAverageWallpaperColour(Context context) {
final WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
final Drawable wallpaperDrawable = wallpaperManager.getDrawable();
Bitmap bmp = ((BitmapDrawable)wallpaperDrawable).getBitmap();
int color = getAverageColour(bmp); // details of function not relevant... just reads the bmp to work out an average colour
return colorToHexString(color); // details of function not relevant... just converts into color to String in particular format
}
Basically, a user for whom the app was crashing has told me that they have a live wallpaper active on their device, rather than a conventional static wallpaper.
So, for some reason I still don't understand, either in trying to get the drawable, or get a bitmap from the drawable, this somehow leads to the Settings$System.putInt and Settings$System.putIntForUser and Settings$System.putStringForUser log entries, which in turn leads to the SecurityException: Permission denial error (attempting to write to Settings without permission).
This post is along similar lines, reporting that the error seemed to be associated with a situation in which the "system wallpaper was null". Quite why there isn't just a NullPointerException when I try to do something unspeakable with null, and instead there is a dire permissions error after apparently attempting to tamper with the Settings, I don't know.

Categories

Resources