summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzafir <zafir@google.com>2015-06-29 00:08:22 -0500
committerSteve Kondik <steve@cyngn.com>2016-08-18 15:07:20 -0700
commit219fbf4d3d7c47039c79822af26bace2849375e4 (patch)
tree34fcb84361c5e1b2f8da734aa74b47796f5c7aaa
parentdb4f5342e80662985c8bcb208e4fabf5c8db8a1a (diff)
downloadandroid_packages_apps_Snap-219fbf4d3d7c47039c79822af26bace2849375e4.tar.gz
android_packages_apps_Snap-219fbf4d3d7c47039c79822af26bace2849375e4.tar.bz2
android_packages_apps_Snap-219fbf4d3d7c47039c79822af26bace2849375e4.zip
Minimum viable Android M runtime permissions handling for H.
Creates new activity for permissions handling: both checking for permissions and handling error condition when critical permissions are not present. The reason for creating a new activity is so the app does not attempt to continue executing OnCreate, OnResume etc, which opens the camera while the dialogs are showing. This should not slow the app down because the permissions activity will only run when a) the first time the app has insufficient permissions and b) when a critical permission is missing and the app needs to shut down. Bug: 21273463 CRs-Fixed: 1019847 Change-Id: I603acfb3057ba26b9cfa7935eb4cb24b5d547cb5 (cherry picked from commit ad44cda82fe6ec5ee090115129223c6314f9e1bb) Fixes to M permissions. Previously, we called PermissionsActivity with startActivityForResult(). However, this creates race conditions as we check for permissions and the CameraActivity continues to operate. Now, we end CameraActivity and launch a new instance upon successful permissions resolution. We can also put the preload filmstrip logic back in its original place in onCreate. The checks for permissions happen in both onCreate and onResume. Bug: 22442745, 22478144, 22497152 CRs-Fixed: 1019847 Change-Id: I82e9125a46581db44aa61d4ee94aec5a820e9df0 (cherry picked from commit ac0e2425e77a9b69e76d2f31876798825ea44584) SnapdragonCamera: Fixed M permission issue - Resolved NPE while sending permission request - Made change to request non-critical permissions once only, but the critical permssions all th time. - Removed unused contant defines CRs-Fixed: 1019847 Change-Id: Ib997244cbcc041d86c094c7ee7a902bff56e92ad snap: Remove platform signature * We are using runtime permissions now. Change-Id: I3386214dbbc0915251941ef490e7cbaf27e6ed45
-rw-r--r--Android.mk2
-rw-r--r--AndroidManifest.xml10
-rw-r--r--res/values/strings.xml6
-rw-r--r--src/com/android/camera/CameraActivity.java63
-rw-r--r--src/com/android/camera/CameraSettings.java2
-rw-r--r--src/com/android/camera/PermissionsActivity.java191
6 files changed, 268 insertions, 6 deletions
diff --git a/Android.mk b/Android.mk
index 0b2bc5355..44b0b7ecf 100644
--- a/Android.mk
+++ b/Android.mk
@@ -15,8 +15,6 @@ LOCAL_SRC_FILES += $(call all-java-files-under, src_pd_gcam)
LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/res
-LOCAL_CERTIFICATE := platform
-
include $(LOCAL_PATH)/version.mk
LOCAL_AAPT_FLAGS := \
--auto-add-overlay \
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index eb5fb9fe0..e532e64f0 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -21,7 +21,6 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.NFC" />
- <uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
@@ -77,6 +76,15 @@
</intent-filter>
</activity>
+ <activity
+ android:name="com.android.camera.PermissionsActivity"
+ android:label="@string/app_name"
+ android:parentActivityName="com.android.camera.CameraActivity" >
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="com.android.camera.CameraActivity" />
+ </activity>
+
<activity-alias
android:name="com.android.camera.CameraLauncher"
android:label="@string/app_name"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fecad0815..7f97f5a9c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -170,6 +170,12 @@
<!-- message for the dialog showing the camera is disabled because of security policies. Camera cannot be used. -->
<string name="camera_disabled">Camera has been disabled because of security policies.</string>
+ <!-- message for the dialog showing that the app does not have sufficient permissions [CHAR LIMIT=NONE] -->
+ <string name="error_permissions">The app does not have critical permissions needed to run. Please check your permissions settings.</string>
+
+ <!-- Dialog "Dismiss" button. Closes the dialog [CHAR LIMIT=12]-->
+ <string name="dialog_dismiss">Dismiss</string>
+
<!-- alert to the user to wait for some operation to complete -->
<string name="wait">Please wait\u2026</string>
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index bdb03bb0a..a22a7ae41 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -19,6 +19,7 @@ package com.android.camera;
import android.view.Display;
import android.graphics.Point;
+import android.Manifest;
import android.animation.Animator;
import android.annotation.TargetApi;
import android.app.ActionBar;
@@ -33,6 +34,7 @@ import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
@@ -227,6 +229,7 @@ public class CameraActivity extends Activity
private boolean mPaused = true;
private View mPreviewCover;
private FrameLayout mPreviewContentLayout;
+ private boolean mHasCriticalPermissions;
private Uri[] mNfcPushUris = new Uri[1];
@@ -1428,6 +1431,11 @@ public class CameraActivity extends Activity
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
+ if (checkPermissions() || !mHasCriticalPermissions) {
+ Log.v(TAG, "onCreate: Missing critical permissions.");
+ finish();
+ return;
+ }
// Check if this is in the secure camera mode.
Intent intent = getIntent();
String action = intent.getAction();
@@ -1637,7 +1645,9 @@ public class CameraActivity extends Activity
@Override
public void onUserInteraction() {
super.onUserInteraction();
- mCurrentModule.onUserInteraction();
+ if (mCurrentModule != null) {
+ mCurrentModule.onUserInteraction();
+ }
}
@Override
@@ -1691,8 +1701,53 @@ public class CameraActivity extends Activity
if (focus) this.setSystemBarsVisibility(false);
}
+ /**
+ * Checks if any of the needed Android runtime permissions are missing.
+ * If they are, then launch the permissions activity under one of the following conditions:
+ * a) If critical permissions are missing, display permission request again
+ * b) If non-critical permissions are missing, just display permission request once.
+ * Critical permissions are: camera, microphone and storage. The app cannot run without them.
+ * Non-critical permission is location.
+ */
+ private boolean checkPermissions() {
+ boolean requestPermission = false;
+
+ if (checkSelfPermission(Manifest.permission.CAMERA) ==
+ PackageManager.PERMISSION_GRANTED &&
+ checkSelfPermission(Manifest.permission.RECORD_AUDIO) ==
+ PackageManager.PERMISSION_GRANTED &&
+ checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) ==
+ PackageManager.PERMISSION_GRANTED) {
+ mHasCriticalPermissions = true;
+ } else {
+ mHasCriticalPermissions = false;
+ }
+
+ if ((checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) !=
+ PackageManager.PERMISSION_GRANTED) || !mHasCriticalPermissions) {
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ boolean isRequestShown = prefs.getBoolean(CameraSettings.KEY_REQUEST_PERMISSION, false);
+ if(!isRequestShown || !mHasCriticalPermissions) {
+ Log.v(TAG, "Request permission");
+ Intent intent = new Intent(this, PermissionsActivity.class);
+ startActivity(intent);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(CameraSettings.KEY_REQUEST_PERMISSION, true);
+ editor.apply();
+ requestPermission = true;
+ }
+ }
+ return requestPermission;
+ }
+
@Override
public void onResume() {
+ if (checkPermissions() || !mHasCriticalPermissions) {
+ super.onResume();
+ Log.v(TAG, "onResume: Missing critical permissions.");
+ finish();
+ return;
+ }
UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
UsageStatistics.ACTION_FOREGROUNDED, this.getClass().getSimpleName());
@@ -1761,8 +1816,10 @@ public class CameraActivity extends Activity
getContentResolver().unregisterContentObserver(mLocalVideosObserver);
unregisterReceiver(mSDcardMountedReceiver);
- mCursor.close();
- mCursor=null;
+ if (mCursor != null) {
+ mCursor.close();
+ mCursor=null;
+ }
super.onDestroy();
}
diff --git a/src/com/android/camera/CameraSettings.java b/src/com/android/camera/CameraSettings.java
index bf6dfc080..86d84ac6c 100644
--- a/src/com/android/camera/CameraSettings.java
+++ b/src/com/android/camera/CameraSettings.java
@@ -241,6 +241,8 @@ public class CameraSettings {
public static final String KEY_QC_SUPPORTED_MANUAL_EXPOSURE_MODES = "manual-exposure-modes";
public static final String KEY_QC_SUPPORTED_MANUAL_WB_MODES = "manual-wb-modes";
+ public static final String KEY_REQUEST_PERMISSION = "request_permission";
+
public static final String KEY_SELFIE_FLASH = "pref_selfie_flash_key";
public static final String EXPOSURE_DEFAULT_VALUE = "0";
diff --git a/src/com/android/camera/PermissionsActivity.java b/src/com/android/camera/PermissionsActivity.java
new file mode 100644
index 000000000..27a3c3692
--- /dev/null
+++ b/src/com/android/camera/PermissionsActivity.java
@@ -0,0 +1,191 @@
+package com.android.camera;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import org.codeaurora.snapcam.R;
+
+/**
+ * Activity that shows permissions request dialogs and handles lack of critical permissions.
+ */
+public class PermissionsActivity extends Activity {
+ private static final String TAG = "PermissionsActivity";
+
+ private static int PERMISSION_REQUEST_CODE = 1;
+
+ private int mIndexPermissionRequestCamera;
+ private int mIndexPermissionRequestMicrophone;
+ private int mIndexPermissionRequestLocation;
+ private int mIndexPermissionRequestStorage;
+ private boolean mShouldRequestCameraPermission;
+ private boolean mShouldRequestMicrophonePermission;
+ private boolean mShouldRequestLocationPermission;
+ private boolean mShouldRequestStoragePermission;
+ private int mNumPermissionsToRequest;
+ private boolean mFlagHasCameraPermission;
+ private boolean mFlagHasMicrophonePermission;
+ private boolean mFlagHasStoragePermission;
+ private boolean mCriticalPermissionDenied;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (!mCriticalPermissionDenied) {
+ mNumPermissionsToRequest = 0;
+ checkPermissions();
+ } else {
+ mCriticalPermissionDenied = false;
+ }
+ }
+
+ private void checkPermissions() {
+ if (checkSelfPermission(Manifest.permission.CAMERA)
+ != PackageManager.PERMISSION_GRANTED) {
+ mNumPermissionsToRequest++;
+ mShouldRequestCameraPermission = true;
+ } else {
+ mFlagHasCameraPermission = true;
+ }
+
+ if (checkSelfPermission(Manifest.permission.RECORD_AUDIO)
+ != PackageManager.PERMISSION_GRANTED) {
+ mNumPermissionsToRequest++;
+ mShouldRequestMicrophonePermission = true;
+ } else {
+ mFlagHasMicrophonePermission = true;
+ }
+
+ if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
+ mNumPermissionsToRequest++;
+ mShouldRequestStoragePermission = true;
+ } else {
+ mFlagHasStoragePermission = true;
+ }
+
+ if (checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
+ != PackageManager.PERMISSION_GRANTED) {
+ mNumPermissionsToRequest++;
+ mShouldRequestLocationPermission = true;
+ }
+
+ if (mNumPermissionsToRequest != 0) {
+ buildPermissionsRequest();
+ } else {
+ handlePermissionsSuccess();
+ }
+ }
+
+ private void buildPermissionsRequest() {
+ String[] permissionsToRequest = new String[mNumPermissionsToRequest];
+ int permissionsRequestIndex = 0;
+
+ if (mShouldRequestCameraPermission) {
+ permissionsToRequest[permissionsRequestIndex] = Manifest.permission.CAMERA;
+ mIndexPermissionRequestCamera = permissionsRequestIndex;
+ permissionsRequestIndex++;
+ }
+ if (mShouldRequestMicrophonePermission) {
+ permissionsToRequest[permissionsRequestIndex] = Manifest.permission.RECORD_AUDIO;
+ mIndexPermissionRequestMicrophone = permissionsRequestIndex;
+ permissionsRequestIndex++;
+ }
+ if (mShouldRequestStoragePermission) {
+ permissionsToRequest[permissionsRequestIndex] = Manifest.permission.READ_EXTERNAL_STORAGE;
+ mIndexPermissionRequestStorage = permissionsRequestIndex;
+ permissionsRequestIndex++;
+ }
+ if (mShouldRequestLocationPermission) {
+ permissionsToRequest[permissionsRequestIndex] = Manifest.permission.ACCESS_COARSE_LOCATION;
+ mIndexPermissionRequestLocation = permissionsRequestIndex;
+ }
+ requestPermissions(permissionsToRequest, PERMISSION_REQUEST_CODE);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String permissions[], int[] grantResults) {
+
+ if (mShouldRequestCameraPermission) {
+ if (grantResults[mIndexPermissionRequestCamera] ==
+ PackageManager.PERMISSION_GRANTED) {
+ mFlagHasCameraPermission = true;
+ } else {
+ mCriticalPermissionDenied = true;
+ }
+ }
+ if (mShouldRequestMicrophonePermission) {
+ if (grantResults[mIndexPermissionRequestMicrophone] ==
+ PackageManager.PERMISSION_GRANTED) {
+ mFlagHasMicrophonePermission = true;
+ } else {
+ mCriticalPermissionDenied = true;
+ }
+ }
+ if (mShouldRequestStoragePermission) {
+ if (grantResults[mIndexPermissionRequestStorage] ==
+ PackageManager.PERMISSION_GRANTED) {
+ mFlagHasStoragePermission = true;
+ } else {
+ mCriticalPermissionDenied = true;
+ }
+ }
+
+ if (mShouldRequestLocationPermission) {
+ if (grantResults[mIndexPermissionRequestLocation] ==
+ PackageManager.PERMISSION_GRANTED) {
+ // Do nothing
+ } else {
+ // Do nothing
+ }
+ }
+
+ if (mFlagHasCameraPermission && mFlagHasMicrophonePermission && mFlagHasStoragePermission) {
+ handlePermissionsSuccess();
+ } else if (mCriticalPermissionDenied) {
+ handlePermissionsFailure();
+ }
+ }
+
+ private void handlePermissionsSuccess() {
+ Intent intent = new Intent(this, CameraActivity.class);
+ startActivity(intent);
+ finish();
+ }
+
+ private void handlePermissionsFailure() {
+ new AlertDialog.Builder(this)
+ .setTitle(getResources().getString(R.string.camera_error_title))
+ .setMessage(getResources().getString(R.string.error_permissions))
+ .setCancelable(false)
+ .setOnKeyListener(new Dialog.OnKeyListener() {
+ @Override
+ public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ finish();
+ }
+ return true;
+ }
+ })
+ .setPositiveButton(getResources().getString(R.string.dialog_dismiss),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+ })
+ .show();
+ }
+}