Falta de privilegios para acceder al servicio de cámara en Android 6.0

Estoy usando Camera2API en Android 6.0. Yo estaba hecho sin error en Android 5.0. Sin embargo, cuando usé mi código en el Android 6.0, tengo un problema. El problema es que alguna vez puedo abrir la cámara con éxito y tomar fotos. Sin embargo, en algún momento la cámara no se puede abrir y tiene un error

java.lang.SecurityException: Lacking privileges to access camera service at android.hardware.camera2.utils.CameraBinderDecorator.throwOnError(CameraBinderDecorator.java:108) 

He añadido el permiso de tiempo de ejecución de la siguiente manera:

 String[] PERMISSIONS = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.CAMERA}; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){ if(!hasAllPermissions(this, PERMISSIONS)){ ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL); } } public static boolean hasAllPermissions(Context context, String... permissions) { if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null && permissions != null) { for (String permission : permissions) { if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) { return false; } } } return true; } 

Todo esto es log

 FATAL EXCEPTION: main Process: com.example.camera2api, PID: 5376 java.lang.SecurityException: Lacking privileges to access camera service at android.hardware.camera2.utils.CameraBinderDecorator.throwOnError(CameraBinderDecorator.java:108) at android.hardware.camera2.utils.CameraBinderDecorator$CameraBinderDecoratorListener.onAfterInvocation(CameraBinderDecorator.java:73) at android.hardware.camera2.utils.Decorator.invoke(Decorator.java:81) at java.lang.reflect.Proxy.invoke(Proxy.java:393) at $Proxy2.cancelRequest(Unknown Source) at android.hardware.camera2.impl.CameraDeviceImpl.stopRepeating(CameraDeviceImpl.java:926) at android.hardware.camera2.impl.CameraCaptureSessionImpl.close(CameraCaptureSessionImpl.java:378) at android.hardware.camera2.impl.CameraCaptureSessionImpl$2.onDisconnected(CameraCaptureSessionImpl.java:514) at android.hardware.camera2.impl.CameraDeviceImpl$7.run(CameraDeviceImpl.java:228) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:158) at android.app.ActivityThread.main(ActivityThread.java:7229) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120) 

Actualización: Para alguien que quiere ver mi código completo. Subo mi código completo aquí

Public class AndroidCamera extends AppCompatActivity {

 private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } private static final String TAG = "Camera2App"; private String mImageFileLocation = ""; private static final int STATE_PREVIEW = 0; private static final int STATE_WAIT_LOCK = 1; private static final int STATE_WAITING_PRECAPTURE = 2; /** * Camera state: Waiting for the exposure state to be something other than precapture. */ private static final int STATE_WAITING_NON_PRECAPTURE = 3; /** * Camera state: Picture was taken. */ private static final int STATE_PICTURE_TAKEN = 4; private int mState; private TextureView mTextureView; private Size mPreviewSize; private String mCameraId; String[] PERMISSIONS = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.CAMERA}; private static final int PERMISSION_ALL = 105; private static final int REQUEST_CAMERA_RESULT = 106; private boolean isRegistred=false; private int mSensorOrientation; private TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { if (!TextUtils.isEmpty(BleUtils.getCameraLens(AndroidCamera.this))) setupCamera(width, height,BleUtils.getCameraLens(AndroidCamera.this)); else setupCamera(width, height,"1"); openCamera(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { //closeCamera(); return false; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }; private Semaphore mCameraOpenCloseLock = new Semaphore(1); private CameraDevice mCameraDevice; private CameraDevice.StateCallback mCameraDeviceStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) { mCameraOpenCloseLock.release(); mCameraDevice = camera; //Toast.makeText(getApplicationContext(),"Camera Opened!", Toast.LENGTH_SHORT).show(); createCameraPreviewSession(); } @Override public void onDisconnected(CameraDevice camera) { camera.close(); mCameraDevice = null; } @Override public void onError(CameraDevice camera, int error) { camera.close(); mCameraDevice = null; } }; private CaptureRequest mPreviewCaptureRequest; private CaptureRequest.Builder mPreviewCaptureRequestBuilder; private CameraCaptureSession mCameraCaptureSession; private CameraCaptureSession.CaptureCallback mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() { private void process(CaptureResult result){ switch (mState){ case STATE_PREVIEW: break; case STATE_WAIT_LOCK: Integer afState=result.get(CaptureResult.CONTROL_AF_STATE); if(afState==CaptureRequest.CONTROL_AF_STATE_FOCUSED_LOCKED){ captureStillImage(); } else{ captureStillImage(); } break; } } @Override public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber) { super.onCaptureStarted(session, request, timestamp, frameNumber); } @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { super.onCaptureCompleted(session, request, result); process(result); } @Override public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) { super.onCaptureFailed(session, request, failure); Handler mHandler = new Handler(getMainLooper()); mHandler.post(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), "Focus Lock UnSuccesful", Toast.LENGTH_SHORT).show(); } }); } }; private HandlerThread mBackgroundThread; private Handler mBackgroundHandler; private static File mImageFile; private ImageReader mImageReader; private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage())); } }; private static class ImageSaver implements Runnable { private final Image mImage; private ImageSaver(Image image) { mImage = image; } @Override public void run() { ByteBuffer byteBuffer = mImage.getPlanes()[0].getBuffer(); byte[] bytes = new byte[byteBuffer.remaining()]; byteBuffer.get(bytes); FileOutputStream fileOutputStream = null; try { fileOutputStream = new FileOutputStream(mImageFile); fileOutputStream.write(bytes); } catch (IOException e) { e.printStackTrace(); } finally { mImage.close(); if (fileOutputStream != null) { try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.camera_activity); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){ if(!hasAllPermissions(this, PERMISSIONS)){ ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL); } } mTextureView = (TextureView) findViewById(R.id.texture); } public static boolean hasAllPermissions(Context context, String... permissions) { if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null && permissions != null) { for (String permission : permissions) { if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) { return false; } } } return true; } @Override public void onStart() { super.onStart(); if (!EventBus.getDefault().isRegistered(this)) { EventBus.getDefault().register(this); } } //onStop @Override public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); } @Subscribe public void onCaptureNumberReceived(OnCaptureEvent event) { //get the phone number value here and do something with it String capturecode = event.getCodeCapture(); Log.d(TAG, capturecode); if (capturecode.equals("capture")) { try { mImageFile = createImageFile(); } catch (IOException e) { e.printStackTrace(); } lockFocus(); MediaActionSound sound = new MediaActionSound(); sound.play(MediaActionSound.SHUTTER_CLICK); } else if(capturecode.equals("end_capture")) { finish(); // call this to finish the current activity Intent homeIntent = new Intent(Intent.ACTION_MAIN); homeIntent.addCategory( Intent.CATEGORY_HOME ); homeIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(homeIntent); } } public void takepicture(View view) { try { mImageFile = createImageFile(); Log.d("TAG","=====Take picture====="); } catch (IOException e) { e.printStackTrace(); } lockFocus(); MediaActionSound sound = new MediaActionSound(); sound.play(MediaActionSound.SHUTTER_CLICK); } public void switch_camera(View view) { closeCamera(); //swap the id of the camera to be used if(mCameraId == String.valueOf(Camera.CameraInfo.CAMERA_FACING_BACK)){ mCameraId = String.valueOf(Camera.CameraInfo.CAMERA_FACING_FRONT); } else { mCameraId = String.valueOf(Camera.CameraInfo.CAMERA_FACING_BACK); } BleUtils.setCameraLens(this, mCameraId); if (mTextureView.isAvailable()) { setupCamera(mTextureView.getWidth(), mTextureView.getHeight(),mCameraId); openCamera(); } else { mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); } } File createImageFile() throws IOException { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); String imageFileName = "BLE_" + timeStamp + "_"; File storageDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); if(!storageDirectory.exists()){ if(!storageDirectory.mkdirs()){ Log.e("Dir", "Failed to create directory"); Log.d("MAKE DIR", storageDirectory.mkdir() + "" + storageDirectory.getParentFile() + ""); return null; } } File image = File.createTempFile(imageFileName, ".jpg", storageDirectory); mImageFileLocation = image.getAbsolutePath(); return image; } @Override public void onResume() { super.onResume(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){ if(!hasAllPermissions(this, PERMISSIONS)){ ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL); } } openBackgroundThread(); if (mTextureView.isAvailable()) { if (!TextUtils.isEmpty(BleUtils.getCameraLens(AndroidCamera.this))) setupCamera(mTextureView.getWidth(), mTextureView.getHeight(),BleUtils.getCameraLens(AndroidCamera.this)); else setupCamera(mTextureView.getWidth(), mTextureView.getHeight(),"1"); closeCamera(); openCamera(); } else { mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); } } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG,"onDestroy"); } public void onPause() { Log.d(TAG,"onPause"); closeCamera(); closeBackgroundThread(); super.onPause(); } private void setupCamera(int width, int height, String cameraId) { CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId); StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); Size largestImageSize = Collections.max( Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), new Comparator<Size>() { @Override public int compare(Size lhs, Size rhs) { return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight()); } } ); mImageReader = ImageReader.newInstance(largestImageSize.getWidth(), largestImageSize.getHeight(), ImageFormat.JPEG, 1); mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler); mPreviewSize = getPreferredPreviewSize(map.getOutputSizes(SurfaceTexture.class), width, height); mCameraId = cameraId; Log.d("CAMERA_ID",String.valueOf(mCameraId)); // } } catch (CameraAccessException e) { e.printStackTrace(); } } private Size getPreferredPreviewSize(Size[] mapSizes, int width, int height) { List<Size> collectorSizes = new ArrayList<>(); for (Size option : mapSizes) { if (width > height) { if (option.getWidth() > width && option.getHeight() > height) { collectorSizes.add(option); } } else { if (option.getWidth() > height && option.getHeight() > width) { collectorSizes.add(option); } } } if (collectorSizes.size() > 0) { return Collections.min(collectorSizes, new Comparator<Size>() { @Override public int compare(Size lhs, Size rhs) { return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight()); } }); } return mapSizes[0]; } private void openCamera() { CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { Log.v("CAMERA", mCameraId + " " + mCameraDeviceStateCallback); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){ if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){ cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback,mBackgroundHandler); } else { if (shouldShowRequestPermissionRationale(android.Manifest.permission.CAMERA)){ Toast.makeText(this,"No Permission to use the Camera services", Toast.LENGTH_SHORT).show(); } requestPermissions(new String[] {android.Manifest.permission.CAMERA},REQUEST_CAMERA_RESULT); } } else { cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, mBackgroundHandler); } } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode){ case REQUEST_CAMERA_RESULT: if (grantResults[0] != PackageManager.PERMISSION_GRANTED){ Toast.makeText(this, "Cannot run application because camera service permission have not been granted", Toast.LENGTH_SHORT).show(); } break; default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); break; } } private void closeCamera(){ if(mCameraCaptureSession!=null){ mCameraCaptureSession.close(); mCameraCaptureSession=null; } if (mCameraDevice!=null){ mCameraDevice.close(); mCameraDevice=null; if(mImageReader!=null){ mImageReader.close(); mImageReader=null; } } } private void createCameraPreviewSession(){ try{ SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture(); surfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(),mPreviewSize.getHeight()); Surface previewSurface= new Surface(surfaceTexture); mPreviewCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mPreviewCaptureRequestBuilder.addTarget(previewSurface); mPreviewCaptureRequestBuilder.set(CaptureRequest.JPEG_QUALITY, (byte)100); mCameraDevice.createCaptureSession(Arrays.asList(previewSurface,mImageReader.getSurface()), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { if(mCameraDevice==null){ return; } try { mPreviewCaptureRequest = mPreviewCaptureRequestBuilder.build(); mCameraCaptureSession = session; mCameraCaptureSession.setRepeatingRequest( mPreviewCaptureRequest, mSessionCaptureCallback, mBackgroundHandler ); }catch (CameraAccessException e){ e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession session) { Handler mHandler = new Handler(getMainLooper()); mHandler.post(new Runnable() { @Override public void run() { Toast.makeText( getApplicationContext(), "create camera session failed!", Toast.LENGTH_SHORT ).show(); } }); } },null); }catch (CameraAccessException e){ e.printStackTrace(); } } private void openBackgroundThread(){ mBackgroundThread=new HandlerThread("Camera2 background thread"); mBackgroundThread.start(); mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); } private void closeBackgroundThread(){ mBackgroundThread.quitSafely(); try{ mBackgroundThread.join(); mBackgroundThread=null; mBackgroundHandler=null; }catch (InterruptedException e){ e.printStackTrace(); } } private void lockFocus(){ try{ mState=STATE_WAIT_LOCK; mPreviewCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); mCameraCaptureSession.capture(mPreviewCaptureRequestBuilder.build(), mSessionCaptureCallback,mBackgroundHandler); }catch (CameraAccessException e){ e.printStackTrace(); } } private void unLockFocus(){ try{ mState=STATE_PREVIEW; mPreviewCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); mCameraCaptureSession.capture(mPreviewCaptureRequestBuilder.build(), mSessionCaptureCallback,mBackgroundHandler); }catch (CameraAccessException e){ e.printStackTrace(); } } private void captureStillImage(){ try { CaptureRequest.Builder captureStillBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); captureStillBuilder.addTarget(mImageReader.getSurface()); // Use the same AE and AF modes as the preview. captureStillBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // setAutoFlash(captureBuilder); // Orientation int rotation=0; //Front camera if(mCameraId.equals("1")) { rotation = this.getWindowManager().getDefaultDisplay().getRotation(); captureStillBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation)); } else { rotation = this.getWindowManager().getDefaultDisplay().getRotation(); captureStillBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)); } CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { super.onCaptureCompleted(session, request, result); //Toast.makeText(getApplicationContext(),"Image Captured",Toast.LENGTH_SHORT).show(); unLockFocus(); } }; mCameraCaptureSession.capture( captureStillBuilder.build(),captureCallback,null ); }catch (CameraAccessException e){ e.printStackTrace(); } } private int getOrientation(int rotation) { return (ORIENTATIONS.get(rotation) + mSensorOrientation +180) % 360; } 

}

setupCamera() se llama a la derecha de onSurfaceTextureAvailable , que puede ser anterior a los permisos se conceden.

Lo que hay que hacer es rastrear si los permisos se conceden y si la textura de la superficie está disponible en ambos callbacks.

Realice una sola entrada para comprobar estas condiciones y configurar la cámara

 private boolean mSurfaceTextureAvailable; private boolean mPermissionsGranted; private boolean mCameraOpened; private void setupCameraIfPossible() { if (!mCameraOpened && mSurfaceTextureAvailable && mPermissionsGranted) { String cameraLens = BleUtils.getCameraLens(AndroidCamera.this); if (TextUtils.isEmpty(cameraLens)) { cameraLens = "1"; } setupCamera(mTextureView.getWidth(), mTextureView.getHeight(), cameraLens); openCamera(); } } private final TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mSurfaceTextureAvailable = true; setupCameraIfPossible(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { //closeCamera(); mSurfaceTextureAvailable = false; return false; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.camera_activity); openBackgroundThread(); // Make sure the boolean flag is set. Will be true for lower SDK mPermissionsGranted = hasAllPermissions(this, PERMISSIONS); if (!mPermissionsGranted) { ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL); } mTextureView = (TextureView) findViewById(R.id.texture); } 

En onResume() no es necesario comprobar si hay permisos. Si se les niega mientras están en el fondo, su Actividad será asesinada y pasará a través de onCreate() nuevo.

Quite el código en onPause() y onResume() !

 // @Override // public void onResume() { // super.onResume(); // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){ // if(!hasAllPermissions(this, PERMISSIONS)){ // ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL); // } // } // openBackgroundThread(); // if (mTextureView.isAvailable()) { // if (!TextUtils.isEmpty(BleUtils.getCameraLens(AndroidCamera.this))) // setupCamera(mTextureView.getWidth(), mTextureView.getHeight(),BleUtils.getCameraLens(AndroidCamera.this)); // else // setupCamera(mTextureView.getWidth(), mTextureView.getHeight(),"1"); // closeCamera(); // openCamera(); // } else { // mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); // } // } // public void onPause() { // Log.d(TAG,"onPause"); // closeCamera(); // // closeBackgroundThread(); // super.onPause(); // } 

Añadir esto a onStart()

 @Override public void onStart() { super.onStart(); openCameraIfPossible(); } 

Mover el cierre de la cámara a onStop()

 @Override public void onStop() { super.onStop(); closeCamera(); } private void closeCamera() { mCameraOpened = false; // set a field indicating it is closed ... } private void openCamera() { ... mCameraOpened = true; // If successful, set a field indicating it is opened } 

Ahora otra cosa que descubrí es que realmente debe buscar permisos en onRequestPermissionsResult() lugar de utilizar flags grantResults

 @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode){ case REQUEST_CAMERA_RESULT: mPermissionsGranted = hasAllPermissions(this, PERMISSIONS); setupCameraIfPossible(); break; default: break; } } 
  • ¿Cómo puedo verificar el permiso bajo el API nivel 23?
  • ¿Cómo elimino mi propio calendario personalizado corrompido mediante programación?
  • ¿Debo declarar el permiso "com.android.alarm.permission.SET_ALARM" cuando se utiliza el Administrador de alarmas en Android?
  • Obtener detalles del permiso mediante programación
  • ¿Puedo recibir los intentos C2DM de otra aplicación de Android si estoy firmado con la misma firma?
  • Modelo de permiso de Android Marshmallow en OS 4.0 READ_EXTERNAL_STORAGE permiso no siempre concedido
  • ¿Por qué se ha agregado el permiso READ_PHONE_STATE?
  • Permiso de Marshmallow: Componente Lifecycle
  • Negación de permisos de Android en Widget RemoteViewsFactory para contenido
  • Implementación del permiso de Marshmallow
  • Marshmallow FINE y COARSE permiso de ubicación
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.