source.rar source.rar - 5 months ago 122
Android Question

Android M Camera Intent + permission bug?

I'm trying to get my app ready for the new Android M permissions changes and found some weird behaviour. My app uses the Camera intent mechanism to allow the user to get a picture form the camera. But in another activity needs to make use of the camera itself with Camera permission (because of a library dependency card.io that requires this).

However with M in the activity that only needs a camera intent when I try to launch the Camera intent I see the following crash (this does not happen if I remove the Camera permission from the Manifest),

> 09-25 21:57:55.260 774-8053/? I/ActivityManager: START u0
> {act=android.media.action.IMAGE_CAPTURE flg=0x3000003
> pkg=com.google.android.GoogleCamera
> cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity
> (has clip) (has extras)} from uid 10098 on display 0 09-25
> 21:57:55.261 774-8053/? W/ActivityManager: Permission Denial: starting
> Intent { act=android.media.action.IMAGE_CAPTURE flg=0x3000003
> pkg=com.google.android.GoogleCamera
> cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity
> (has clip) (has extras) } from null (pid=-1, uid=10098) with revoked
> permission android.permission.CAMERA 09-25 21:57:55.263 32657-32657/?
> E/ResolverActivity: Unable to launch as uid 10098 package
> com.example.me.mycamerselectapp, while running in android:ui 09-25
> 21:57:55.263 32657-32657/? E/ResolverActivity:
> java.lang.SecurityException: Permission Denial: starting Intent {
> act=android.media.action.IMAGE_CAPTURE flg=0x3000003
> pkg=com.google.android.GoogleCamera
> cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity
> (has clip) (has extras) } from null (pid=-1, uid=10098) with revoked
> permission android.permission.CAMERA 09-25 21:57:55.263 32657-32657/?
> E/ResolverActivity: at
> android.os.Parcel.readException(Parcel.java:1599) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Parcel.readException(Parcel.java:1552) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.app.ActivityManagerProxy.startActivityAsCaller(ActivityManagerNative.java:2730)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> android.app.Instrumentation.execStartActivityAsCaller(Instrumentation.java:1725)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> android.app.Activity.startActivityAsCaller(Activity.java:4047) 09-25
> 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity$DisplayResolveInfo.startAsCaller(ResolverActivity.java:983)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity.safelyStartActivity(ResolverActivity.java:772)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity.onTargetSelected(ResolverActivity.java:754)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ChooserActivity.onTargetSelected(ChooserActivity.java:305)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity.startSelected(ResolverActivity.java:603)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ChooserActivity.startSelected(ChooserActivity.java:310)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ChooserActivity$ChooserRowAdapter$2.onClick(ChooserActivity.java:990)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> android.view.View.performClick(View.java:5198) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.view.View$PerformClick.run(View.java:21147) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Handler.handleCallback(Handler.java:739) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Handler.dispatchMessage(Handler.java:95) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Looper.loop(Looper.java:148) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.app.ActivityThread.main(ActivityThread.java:5417) 09-25
> 21:57:55.263 32657-32657/? E/ResolverActivity: at
> java.lang.reflect.Method.invoke(Native Method) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 09-25
> 21:57:55.286 1159-1159/? I/Keyboard.Facilitator: onFinishInput() 09-25
> 21:57:55.297 32657-32676/? E/Surface: getSlotFromBufferLocked: unknown
> buffer: 0xaec352e0 09-25 21:57:55.344 325-349/? V/RenderScript:
> 0xb3693000 Launching thread(s), CPUs 4 09-25 21:57:57.290 325-349/?
> E/Surface: getSlotFromBufferLocked: unknown buffer: 0xb3f88240


Is this a known problem with Android M? And more importantly how do I work around this?


in the manifest I have the following,

<uses-permission android:name="android.permission.CAMERA" />


and this is the code I use to let the user click a pic with the Camera and/or select an image

public static Intent openImageIntent(Context context, Uri cameraOutputFile) {

// Camera.
final List<Intent> cameraIntents = new ArrayList<Intent>();
final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
final PackageManager packageManager = context.getPackageManager();
final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
for(ResolveInfo res : listCam) {
final String packageName = res.activityInfo.packageName;
final Intent intent = new Intent(captureIntent);
intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
intent.setPackage(packageName);
intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraOutputFile);
cameraIntents.add(intent);
}

// Filesystem.
final Intent galleryIntent = new Intent();
galleryIntent.setType("image/*");
galleryIntent.setAction(Intent.ACTION_GET_CONTENT);

// Chooser of filesystem options.
final Intent chooserIntent = Intent.createChooser(galleryIntent, "Take or select pic");

// Add the camera options.
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{}));
return chooserIntent;
}


I call the
openImageIntent()
on a button click in my activity. When I do not have the CAMERA permission in my app it works fine, but with that added I get the exception posted above.

@Override
public void onClick(View v) {
Intent picCaptureIntenet = openImageIntent(MainActivity.this, getTempImageFileUri(MainActivity.this));
try {
startActivityForResult(picCaptureIntenet, 100);
} catch(Exception e) {
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}



Answer

I had the same issue and find this doc from google: https://developer.android.com/reference/android/provider/MediaStore.html#ACTION_IMAGE_CAPTURE

"Note: if you app targets M and above and declares as using the CAMERA permission which is not granted, then atempting to use this action will result in a SecurityException."

This is really weird. Don't make sense at all. The app declares Camera permission using intent with action IMAGE_CAPTURE just run into SecurityException. But if your app doesn't declare Camera permission using intent with action IMAGE_CAPTURE can launch Camera app without issue.

The workaround would be check is the app has camera permission included in the manifest, if it's , request camera permission before launching intent.

Here is the way to check if the permission is included in the manifest, doesn't matter the permission is granted or not.

public boolean hasPermissionInManifest(Context context, String permissionName) {
    final String packageName = context.getPackageName();
    try {
        final PackageInfo packageInfo = context.getPackageManager()
                .getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
        final String[] declaredPermisisons = packageInfo.requestedPermissions;
        if (declaredPermisisons != null && declaredPermisisons.length > 0) {
            for (String p : declaredPermisisons) {
                if (p.equals(permissionName)) {
                    return true;
                }
            }
        }
    } catch (NameNotFoundException e) {

    }
    return false;
}