Access restrictions

Your TomTom BRIDGE device comes out of the box with two user access profiles:

  • You (Owner)
  • User

In owner profile, all settings are accessible, all apps are displayed in the application browser and ADB access is permitted. In user profile, settings and apps can be made inaccessible by the Owner. ADB access is not permitted.

The Owner can select which apps and settings are permitted in user mode by selecting checkboxes in the settings/users menu on the TomTom BRIDGE device.

Selecting which settings the "User" is allowed to change:

settings for user to change

Selecting which applications the "User" is allowed to use:

settings for user to use

Owner and restricted user

There are two types of users that can run a TomTom BRIDGE: an owner ("Owner") and restricted users ("User"). The Owner can do everything with the device that you can do with any other non-rooted Android device. The User can be enabled by the Owner, who can define what that user is allowed to do on the device: which applications are allowed to be started, can settings be changed, etc.

You are the Owner that defines a configuration for the TomTom BRIDGE that your customers will use as a User. This is because it will make it impossible for that user to change the configuration of the device and perhaps use it for other means than you intended.

You will find the user profiles under Settings / Users. To change the definition of a User, tap the settings icon to the right of it. Here you can select which apps the user is allowed to start, which settings it is allowed to change, etc.

The "Clear Personal Data" option in the Settings / Backup & reset menu, will only clear the data for the current User when that user is not the Owner. When the Owner selects this option, all the personal data of all users will be cleared.

Multi user boot sequence

Standard Android will always boot into the owner profile which makes sense for mobile phone and generic tablet scenarios. The TomTom BRIDGE has a different requirement in that it is being configured by the Owner to be used by a User, whom we do not want to have to enter a PIN each time they use the device. It is also not desirable to boot into the Owner profile every time since we do not want the end-user to be able to change the configuration. Make sure you have set a private PIN code for the Owner profile to lock this down completely.

This is why the TomTom BRIDGE will start up using the last used profile. To accomplish this we must allow Android boot in Owner profile, after which we switch to the last selected user as soon as possible to finish the boot process. The user holding the device cannot interfere with this so it will be transparent to him.

This does pose some challenges for developers though, since apps started on android.intent.action.BOOT_COMPLETED will get started for both Owner and User! They might therefore have issues with hardware features being claimed by the Owner while the User is trying to get at it too. The TomTom BRIDGE built in apps already handle these situations gracefully but if you plan to add your own apps to the device configuration, you must make them aware of this as well.

When the user profile is switched, all running applications of any other user will receive the ACTION_USER_BACKGROUND intent to inform them that they will be pushed to the background. This is what is happening at boot time too. Adding the following to your application will make it able to respond to the user switch:

1public class MyApplication extends Application {
2 private static final String TAG = MyApplication.class.getSimpleName()
3
4 /**
5 * Default implementation of the broadcast receiver which is responsible for killing the
6 * current application if a user goes to the background.
7 */
8 private class BackgroundUserReceiver extends BroadcastReceiver {
9 @Override
10 public void onReceive(final Context context, final Intent intent) {
11 if ((intent != null) && Intent.ACTION_USER_BACKGROUND.equals(intent.getAction())) {
12 killSelf();
13 }
14 }
15 }
16
17 @Override
18 public void onCreate() {
19 super.onCreate();
20
21 // First check if the application was started for correct user.
22 if (!checkCorrectUser(this)) {
23 return;
24 }
25
26 // If it is, then register a receiver for the cases when a user is switched.
27 registerReceiver(new BackgroundUserReceiver(), new IntentFilter(Intent.ACTION_USER_BACKGROUND));
28 }
29
30 // ...
31}

This will check if this application is run by the current foreground user. When it receives the message that this application is being moved to the background because a different user became active, the BackgroundUserReceiver will stop this application, thus making sure no two copies for different users of this application remain active at the same time. Example implementations of the helper functions look like this:

1/**
2 * Checks if the current user is the same user for which the application is started. If not -
3 * kills the process of the application.
4 *
5 * @return true if the application is started for correct user, false otherwise.
6 */
7private static boolean checkCorrectUser(final Context context) {
8 assert context != null;
9
10 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
11 final long currentUserId = getCurrentUserId(context);
12 final long lastUserId = getLastUserId(context);
13 Log.i(TAG, "currentUser=" + currentUserId + " lastUser=" + lastUserId);
14
15 if (((currentUserId == 0) && (lastUserId != 0)) || ((currentUserId != 0) && (lastUserId == 0))) {
16 Log.w(TAG, "App was started for the wrong user. Killing the process.");
17 killSelf();
18
19 return false;
20 }
21 }
22 return true;
23}
24
25// Suppress lint since explicit check for the current version is done above.
26@SuppressLint("NewApi")
27private static long getCurrentUserId(final Context context) {
28 assert context != null;
29
30 long userSerialNumber = 0;
31 final UserHandle userHandle = android.os.Process.myUserHandle();
32 final UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
33 if (userManager != null) {
34 userSerialNumber = userManager.getSerialNumberForUser(userHandle);
35 }
36 return userSerialNumber;
37}
38
39// Suppress lint since explicit check for the current version is done above.
40@SuppressLint("NewApi")
41private static long getLastUserId(final Context context) {
42 assert context != null;
43
44 return Settings.Global.getInt(context.getContentResolver(), "last_user_id", 0);
45}
46
47private static void killSelf() {
48 new Handler().post(new Runnable() {
49 @Override
50 public void run() {
51 android.os.Process.killProcess(android.os.Process.myPid());
52 }
53 });
54}

Note that most applications will not have a problem with running twice on the same device as different users. Only add the above if your application cannot function with two instances running.