The ApplicationPermissionsManager API offers a way to streamline the request for application permissions to improve the user experience. The API allows an application to request all required permissions at one time and tell the user why the permissions are needed. Applications are also able to determine which permissions they have and if its is possible to get the desired level of permissions.

However, concurrent requests for permissions were not possible until BlackBerry® Device Software 5.0.0.796, Bundle 1342. If the application permissions screen was open when another application (or another entry point of the same application) requested permissions, the request would fail with an exception in some cases or would simply fail silently with no request shown to the user. The exception seen was an IllegalArgumentException, with the message “object already exists”. This is due to the single allowed permissions screen before the limitation was fixed.

The attached sample code shows how to make permission requests in the background which account for these situations more gracefully.

  • Since only one permissions request can be handled at a time, the sample code foregrounds a hidden permissions screen in case the user had dismissed it or another application had taken the foreground.
  • After foregrounding any prior screens, the applications own request is queued up using the ApplicationPermissionsManager API.
  • If it fails with the IllegalArgumentException “object already exists”, then the request is re-queued when the application is foregrounded again, since the user must handle the existing permission request first.
  • The sample also handles instances where the user has put the permissions request into the background but tries to run the application again, and it has been altered to not show any the main UI until the permissions have been accepted. This is not always the best choice of user experience, but may be desired by some applications.
import net.rim.device.api.applicationcontrol.ApplicationPermissions;
import net.rim.device.api.applicationcontrol.ApplicationPermissionsManager;
import net.rim.device.api.system.ApplicationDescriptor;
import net.rim.device.api.system.ApplicationManager;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.EventLogger;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.Ui;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.UiEngine;
import net.rim.device.api.ui.component.BasicEditField;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.DialogClosedListener;
import net.rim.device.api.ui.container.MainScreen;

public class BackgroundPermissions extends UiApplication {

	private long UID = 0x85d1d1d3e756befdL;

	private HarnessScreen screen;

	public BackgroundPermissions(boolean gui) {
		EventLogger.register(UID, "BackgroundPermissions", EventLogger.VIEWER_STRING);
		if (gui) {
			screen = new HarnessScreen();
			screen.checkPermissions();
		} else {
			while (ApplicationManager.getApplicationManager().inStartup()) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			new BackgroundPermissionsCheck().start();
		}
	}

	public void log(String message) {
		if (message != null) {
			System.out.println(message);
			EventLogger.logEvent(UID, message.getBytes(), EventLogger.ALWAYS_LOG);
		}
	}

	private class BackgroundPermissionsCheck extends Thread {
		public void run() {
			String message = "Requesting permissions";
			System.out.println(message);
			EventLogger.logEvent(UID, message.getBytes(), EventLogger.ALWAYS_LOG);

			ApplicationPermissions perms = ApplicationPermissionsManager.getInstance().getApplicationPermissions();
			perms.addPermission(ApplicationPermissions.PERMISSION_RECORDING);
			boolean result;
			synchronized (UiApplication.getEventLock()) {
				result = ApplicationPermissionsManager.getInstance().invokePermissionsRequest(perms);
			}
			if (result) {
				message = "Accepted Permissions ... returned true to Listener";
			} else {
				message = "Did not accept Permissions .. returned false to Listener";
			}
			System.out.println(message);
			EventLogger.logEvent(UID, message.getBytes(), EventLogger.ALWAYS_LOG);
			System.exit(0);
		}
	}

	public void activate() {
		log("Activate");
		if (screen != null) {
			log("Screen exists");
			if (screen.isCheckingPermissions()) {
				log("Screen is waiting on permissions");
				if (screen.isPermissionsScreenInBackground() >= 0) {
					screen.foregroundPermissionsScreen();
				} else {
					screen.checkPermissions();
				}
			} else if (screen.isQueueAfterException()) {
				screen.checkPermissions();
			}
		}
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		BackgroundPermissions app;
		if (args != null && args.length > 0 && args[0].equals("gui")) {
			app = new BackgroundPermissions(true);
		} else {
			app = new BackgroundPermissions(false);
		}
		app.enterEventDispatcher();
	}

	private class HarnessScreen extends MainScreen {

		private BasicEditField log = new BasicEditField("Log: ", "");

		private boolean checkingPermissions = false;
		private boolean queueAfterException = false;

		public HarnessScreen() {
			setTitle("Sync Listener");
			add(log);
		}

		public synchronized void checkPermissions() {
			new PermissionsCheck().start();
		}

		public synchronized boolean isCheckingPermissions() {
			return checkingPermissions;
		}

		public synchronized boolean isQueueAfterException() {
			return queueAfterException;
		}

		public void foregroundPermissionsScreen() {
			int processId = isPermissionsScreenInBackground();
			if (processId >= 0) {
				foregroundPermissionsScreen(processId);
			}
		}

		private void foregroundPermissionsScreen(int processId) {
			ApplicationManager.getApplicationManager().requestForeground(processId);
		}

		/**
		 * Check for the permissions screen hidden in the background
		 *
		 * @return process Id of the screen, or -1 if not running
		 */
		private int isPermissionsScreenInBackground() {
			ApplicationDescriptor[] apps = ApplicationManager.getApplicationManager().getVisibleApplications();
			for (int i = 0; i < apps.length; i++) {
				if (apps[i].getModuleName() != null && apps[i].getModuleName().equals("net_rim_bb_application_permissions_proxy")) {
					int processId = ApplicationManager.getApplicationManager().getProcessId(apps[i]);
					log("Permission screen found at " + processId);
					return processId;
				}
			}
			return -1;
		}

		private class PermissionsCheck extends Thread {

			public void run() {
				if (!hasPermissions()) {
					log("Permissions not valid");
					requestPermissions();
				} else {
					log("Permissions Valid");
					synchronized (UiApplication.getEventLock()) {
						pushScreen(screen);
					}

				}
			}

			public boolean hasPermissions() {
				ApplicationPermissions perms = ApplicationPermissionsManager.getInstance().getApplicationPermissions();
				return checkPermission(perms, ApplicationPermissions.PERMISSION_RECORDING, "Recording");
			}

			public boolean checkPermission(ApplicationPermissions perms, int permission, String name) {
				int setting = perms.getPermission(permission);
				log("Checking " + name + " Permission: " + setting);
				if (setting != ApplicationPermissions.VALUE_ALLOW) {
					return false;
				}
				return true;
			}

			public ApplicationPermissions getDesiredPermissions() {
				ApplicationPermissions perms = ApplicationPermissionsManager.getInstance().getApplicationPermissions();
				perms.addPermission(ApplicationPermissions.PERMISSION_RECORDING);
				return perms;
			}

			public void requestPermissions() {
				log("Foregrounding any existing request");
				foregroundPermissionsScreen();
				queueAfterException = false;
				checkingPermissions = true;
				try {
					log("Enter invokePermissionsRequest : result must be True, False or Exception. ");
					boolean result = ApplicationPermissionsManager.getInstance().invokePermissionsRequest(getDesiredPermissions());
					if (result) {
						log("result: true - User accepted request");
						synchronized (UiApplication.getEventLock()) {
							pushScreen(screen);
						}
					} else {
						log("result: false - User did not accept request");
					}
				} catch (Throwable t) {
					log("result - exception : " + t.toString());
					if (t instanceof IllegalArgumentException) {
						// Queue up request again.
						log("Queueing");
						queueAfterException = true;
					}
				} finally {
					checkingPermissions = false;
					if (!isQueueAfterException()) {
						log("Enter ShowErrorAlertandCloseGui()");
						ShowErrorAlertandCloseGui();
					}
				}

			}

			private void ShowErrorAlertandCloseGui() {
				Dialog dialog = new Dialog(Dialog.D_OK, "-- Error Message -- ", Dialog.YES, Bitmap.getPredefinedBitmap(Bitmap.EXCLAMATION),
						Manager.VERTICAL_SCROLL);
				dialog.setEscapeEnabled(false);
				dialog.setDialogClosedListener(new DialogClosedListener() {
					public void dialogClosed(Dialog dialog, int i) {
						if (i == Dialog.OK) {
							log("ShowErrorAlertandCloseGui ********* System.exit(0) ****** ");
							System.exit(0);
						}
					}
				});
				try {
					log("To push global screen,  synchronized(Application.getEventLock())");
					synchronized (UiApplication.getEventLock()) {
						log("ShowErrorAlertandCloseGui - Enter pushGlobalScreen() ");
						Ui.getUiEngine().pushGlobalScreen(dialog, 1, UiEngine.GLOBAL_QUEUE);
						log("ShowErrorAlertandCloseGui - Exit pushGlobalScreen() ");
					}
				} catch (Exception e) {
					log("Exception - ErrorHandling.askGlobalDialogOK: " + e.toString());
				}
			}

		}

	}

}

[1] http://supportforums.blackberry.com/t5/Java-Development/How-to-Invoke-a-Permissions-request-with-other-applications-also/ta-p/652397

[2] http://supportforums.blackberry.com/t5/Java-Development/What-is-Best-practices-in-working-with-ApplicationPermissions/ta-p/624740