From 852bcd16b075d74f3c24373d8321f9b193200c2e Mon Sep 17 00:00:00 2001 From: LuK1337 Date: Thu, 17 Dec 2015 21:32:46 +0800 Subject: Browser: runtime permissions Change-Id: I19caa970421898196c005aa9ad7cc651cd574c0a Signed-off-by: jrizzoli --- AndroidManifest.xml | 3 +- res/values/cm_strings.xml | 6 + src/com/android/browser/BrowserActivity.java | 29 ++++- src/com/android/browser/DownloadHandler.java | 64 +++++++++- src/com/android/browser/PermissionsActivity.java | 143 +++++++++++++++++++++++ src/com/android/browser/PermissionsPrompt.java | 39 ++++++- src/com/android/browser/Tab.java | 1 + 7 files changed, 279 insertions(+), 6 deletions(-) create mode 100644 src/com/android/browser/PermissionsActivity.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 0ffdec4..53c6325 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -20,7 +20,8 @@ - + diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml index e5f22ac..321c33a 100644 --- a/res/values/cm_strings.xml +++ b/res/values/cm_strings.xml @@ -39,4 +39,10 @@ OneHand Navigation Easier web navigation with swipes and quick actions + + Permission not granted + The permission to write to external storage was not granted. Cannot download. + Browser error + Dismiss + The app does not have critical permissions needed to run. Please check your permissions settings. diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java index 4166b11..85af8af 100644 --- a/src/com/android/browser/BrowserActivity.java +++ b/src/com/android/browser/BrowserActivity.java @@ -17,12 +17,15 @@ package com.android.browser; import android.app.Activity; +import android.app.AlertDialog; import android.app.KeyguardManager; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.os.Bundle; import android.os.PowerManager; +import android.support.v4.app.ActivityCompat; import android.util.Log; import android.view.ActionMode; import android.view.ContextMenu; @@ -37,7 +40,8 @@ import android.view.Window; import com.android.browser.stub.NullController; import com.google.common.annotations.VisibleForTesting; -public class BrowserActivity extends Activity { +public class BrowserActivity extends Activity + implements ActivityCompat.OnRequestPermissionsResultCallback { public static final String ACTION_SHOW_BOOKMARKS = "show_bookmarks"; public static final String ACTION_SHOW_BROWSER = "show_browser"; @@ -51,6 +55,8 @@ public class BrowserActivity extends Activity { private ActivityController mController = NullController.INSTANCE; + public static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1; + @Override public void onCreate(Bundle icicle) { if (LOGV_ENABLED) { @@ -304,4 +310,25 @@ public class BrowserActivity extends Activity { super.dispatchGenericMotionEvent(ev); } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, + int[] grantResults) { + switch (requestCode) { + case PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: + // If request is cancelled, the result array is empty + if (grantResults.length > 0 && + grantResults[0] == PackageManager.PERMISSION_GRANTED) { + DownloadHandler.checkPendingDownloads(this); + } else { + new AlertDialog.Builder(this) + .setTitle(R.string.permission_not_granted_dialog_title) + .setMessage(R.string.permission_not_granted_dialog_message) + .setPositiveButton(android.R.string.ok, null) + .show(); + } + break; + } + } + } diff --git a/src/com/android/browser/DownloadHandler.java b/src/com/android/browser/DownloadHandler.java index 5fc5b79..37ab027 100755 --- a/src/com/android/browser/DownloadHandler.java +++ b/src/com/android/browser/DownloadHandler.java @@ -28,6 +28,8 @@ import android.content.pm.ResolveInfo; import android.net.Uri; import android.net.WebAddress; import android.os.Environment; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; import android.text.TextUtils; import android.util.Log; import android.webkit.CookieManager; @@ -44,6 +46,46 @@ public class DownloadHandler { private static final String LOGTAG = "DLHandler"; + //Singleton to hold the information about any pending downloads + private static PendingDownloadBundle pendingDownloadBundle; + + private static class PendingDownloadBundle { + String url; + String userAgent; + String contentDisposition; + String mimetype; + String referer; + boolean privateBrowsing; + + public static PendingDownloadBundle create(String url, String userAgent, + String contentDisposition, String mimetype, String referer, + boolean privateBrowsing) { + PendingDownloadBundle pdb = new PendingDownloadBundle(); + pdb.url = url; + pdb.userAgent = userAgent; + pdb.contentDisposition = contentDisposition; + pdb.mimetype = mimetype; + pdb.referer = referer; + pdb.privateBrowsing = privateBrowsing; + return pdb; + } + } + + /** + * Check if there is any pending download and start the download automatically in case + * there is one. + * @param activity Activity requesting the download. + */ + public static void checkPendingDownloads(Activity activity) { + if (pendingDownloadBundle != null) { + onDownloadStartNoStream(activity, pendingDownloadBundle.url, + pendingDownloadBundle.userAgent, pendingDownloadBundle.contentDisposition, + pendingDownloadBundle.mimetype, pendingDownloadBundle.referer, + pendingDownloadBundle.privateBrowsing); + pendingDownloadBundle = null; + } + } + /** * Notify the host application a download should be done, or that * the data should be streamed if a streaming viewer is available. @@ -130,7 +172,7 @@ public class DownloadHandler { /** * Notify the host application a download should be done, even if there - * is a streaming viewer available for thise type. + * is a streaming viewer available for this type. * @param activity Activity requesting the download. * @param url The full url to the content that should be downloaded * @param userAgent User agent of the downloading application. @@ -143,6 +185,26 @@ public class DownloadHandler { String url, String userAgent, String contentDisposition, String mimetype, String referer, boolean privateBrowsing) { + // Check permissions first when download will be start. + int permissionCheck = ContextCompat.checkSelfPermission(activity, + android.Manifest.permission.WRITE_EXTERNAL_STORAGE); + if (permissionCheck == PackageManager.PERMISSION_GRANTED) { + onDownloadNoStreamImpl(activity, url, userAgent, contentDisposition, + mimetype, referer, privateBrowsing); + } else { + pendingDownloadBundle = PendingDownloadBundle.create(url, userAgent, + contentDisposition, mimetype, referer, privateBrowsing); + // Permission not granted, request it from the user + ActivityCompat.requestPermissions(activity, + new String[] {android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, + BrowserActivity.PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); + } + } + + private static void onDownloadNoStreamImpl(Activity activity, + String url, String userAgent, String contentDisposition, + String mimetype, String referer, boolean privateBrowsing) { + String filename = URLUtil.guessFileName(url, contentDisposition, mimetype); diff --git a/src/com/android/browser/PermissionsActivity.java b/src/com/android/browser/PermissionsActivity.java new file mode 100644 index 0000000..68f2b19 --- /dev/null +++ b/src/com/android/browser/PermissionsActivity.java @@ -0,0 +1,143 @@ +package com.android.browser; + +import android.Manifest; +import android.app.Activity; +import android.app.Dialog; +import android.app.AlertDialog; +import android.app.KeyguardManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.content.pm.PackageManager; +import android.preference.PreferenceManager; +import android.os.Bundle; +import android.util.Log; +import android.view.KeyEvent; +import android.view.Window; +import android.view.WindowManager; +import com.android.browser.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 String PREF_HAS_SEEN_PERMISSIONS_DIALOGS = "pref_has_seen_permissions_dialogs"; + private static int PERMISSION_REQUEST_CODE = 1; + private static int RESULT_CODE_OK = 1; + private static int RESULT_CODE_FAILED = 2; + + private int mIndexPermissionRequestStorage; + private boolean mShouldRequestStoragePermission; + private int mNumPermissionsToRequest; + private boolean mFlagHasStoragePermission; + private SharedPreferences mPrefs; + + /** + * Close activity when secure app passes lock screen or screen turns + * off. + */ + private final BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Log.v(TAG, "received intent, finishing: " + intent.getAction()); + finish(); + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mPrefs = PreferenceManager.getDefaultSharedPreferences(this); + } + + @Override + protected void onResume() { + super.onResume(); + mNumPermissionsToRequest = 0; + checkPermissions(); + } + + private void checkPermissions() { + if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED) { + mNumPermissionsToRequest++; + mShouldRequestStoragePermission = true; + } else { + mFlagHasStoragePermission = true; + } + + if (mNumPermissionsToRequest != 0) { + if (!mPrefs.getBoolean(PREF_HAS_SEEN_PERMISSIONS_DIALOGS, false)) { + buildPermissionsRequest(); + } else { + // Permissions dialog has already been shown + // and we're still missing permissions. + handlePermissionsFailure(); + } + } else { + handlePermissionsSuccess(); + } + } + + private void buildPermissionsRequest() { + String[] permissionsToRequest = new String[mNumPermissionsToRequest]; + int permissionsRequestIndex = 0; + + if (mShouldRequestStoragePermission) { + permissionsToRequest[permissionsRequestIndex] = Manifest.permission.READ_EXTERNAL_STORAGE; + mIndexPermissionRequestStorage = permissionsRequestIndex; + permissionsRequestIndex++; + } + + Log.v(TAG, "requestPermissions count: " + permissionsToRequest.length); + requestPermissions(permissionsToRequest, PERMISSION_REQUEST_CODE); + } + + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], + int[] grantResults) { + Log.v(TAG, "onPermissionsResult counts: " + permissions.length + ":" + grantResults.length); + + if (mShouldRequestStoragePermission) { + if (grantResults.length > 0 && grantResults[mIndexPermissionRequestStorage] == + PackageManager.PERMISSION_GRANTED) { + mFlagHasStoragePermission = true; + } else { + handlePermissionsFailure(); + } + } + + if (mFlagHasStoragePermission) { + handlePermissionsSuccess(); + } + } + + private void handlePermissionsSuccess() { + Editor edit = mPrefs.edit(); + edit.putBoolean(PREF_HAS_SEEN_PERMISSIONS_DIALOGS, true); + edit.commit(); + + Intent intent = new Intent(this, BrowserActivity.class); + startActivity(intent); + finish(); + } + + private void handlePermissionsFailure() { + new AlertDialog.Builder(this).setTitle(getResources().getString(R.string.browser_error_title)) + .setMessage(getResources().getString(R.string.error_permissions)) + .setPositiveButton(getResources().getString(R.string.dialog_dismiss), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }) + .show(); + } +} diff --git a/src/com/android/browser/PermissionsPrompt.java b/src/com/android/browser/PermissionsPrompt.java index 29412d9..47c6c4e 100644 --- a/src/com/android/browser/PermissionsPrompt.java +++ b/src/com/android/browser/PermissionsPrompt.java @@ -16,7 +16,11 @@ package com.android.browser; +import android.Manifest; +import android.app.Activity; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; import android.net.Uri; import android.util.AttributeSet; import android.view.Gravity; @@ -29,7 +33,9 @@ import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; +import java.util.ArrayList; import java.util.Enumeration; +import java.util.List; import java.util.Vector; public class PermissionsPrompt extends RelativeLayout { @@ -37,14 +43,17 @@ public class PermissionsPrompt extends RelativeLayout { private Button mAllowButton; private Button mDenyButton; private CheckBox mRemember; + private Context mContext; private PermissionRequest mRequest; public PermissionsPrompt(Context context) { this(context, null); + mContext = context; } public PermissionsPrompt(Context context, AttributeSet attrs) { super(context, attrs); + mContext = context; } @Override @@ -117,9 +126,33 @@ public class PermissionsPrompt extends RelativeLayout { */ private void handleButtonClick(boolean allow) { hide(); - if (allow) - mRequest.grant(mRequest.getResources()); - else + if (allow) { + String[] resources = mRequest.getResources(); + List permissionsToRequest = new ArrayList(); + + for (String resource : resources) { + if (resource.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE) && + mContext.checkSelfPermission(Manifest.permission.CAMERA) != + PackageManager.PERMISSION_GRANTED) { + permissionsToRequest.add(Manifest.permission.CAMERA); + } else if (resource.equals(PermissionRequest.RESOURCE_AUDIO_CAPTURE) && + mContext.checkSelfPermission(Manifest.permission.RECORD_AUDIO) != + PackageManager.PERMISSION_GRANTED) { + permissionsToRequest.add(Manifest.permission.RECORD_AUDIO); + } + } + + if (permissionsToRequest.size() > 0) { + String[] permissions = permissionsToRequest.toArray( + new String[permissionsToRequest.size()]); + ((Activity) mContext).requestPermissions(permissions, 1); + Intent intent = new Intent(mContext, PermissionsActivity.class); + mContext.startActivity(intent); + } else { + mRequest.grant(mRequest.getResources()); + } + } else { mRequest.deny(); + } } } diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java index ceacd42..df4bc52 100644 --- a/src/com/android/browser/Tab.java +++ b/src/com/android/browser/Tab.java @@ -23,6 +23,7 @@ import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; +import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; -- cgit v1.1