vendredi 20 février 2015

10 things to know about Device Owner Apps in Android 5

Device Owner Apps is another key feature of Android for Enterprise available in Android 5 (API 21). Device Owner app is a special kind of Admin app that help you create users, and configure global settings without the need to be a privileged system app.

Let’s see 10 facts to know about Device Owner Apps.

1. It can be set via NFC

Because this app has some special “power”, it can only be set using special means.

The official way of setting a Device Owner App is by using a NFC message. You could use a classic NFC tag, or Android Beam.
This message has a special MIME type (application/com.android.managedprovisioning) and contains at least 3 properties :

For instance, on an Android Beam, you could create an Activity implementing NfcAdapter.CreateNdefMessageCallback and NfcAdapter.OnNdefPushCompleteCallback, and have the following code in your createNdefMessage()

import static android.app.admin.DevicePolicyManager.*;
... 
 Properties p = new Properties();
 p.setProperty(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME, "com.test.my_device_owner_app");
 p.setProperty(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION, "https://raw.githubusercontent.com/.../MY_APP.apk");
 p.setProperty(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM,AGt-"ELmIalMx6tdNKWbKBZ4YdGo"); 
 ByteArrayOutputStream bos = new ByteArrayOutputStream();
 OutputStream out = new ObjectOutputStream(bos);
 p.store(out, "");
 final byte[] bytes = bos.toByteArray();

 NdefMessage msg = new NdefMessage(NdefRecord.createMime("application/com.android.managedprovisioning", bytes));

Some additionnal information may also be provided like Wifi informations, proxy, locale, time zone, email address, etc. All other options are available under the EXTRA_PROVISIONING_XXXXconstants in DevicePolicyManager

The checksum of the APK is calculated with :

cat MY_APP.apk | openssl dgst -binary -sha1 | openssl base64 | tr '+/' '-_' | tr -d '='

(Under Windows, you could use a cygwin-like bash (like Babun for instance) to provide these commands.)

Important thing to know : It has to be done on an unprovisioned device , on the FIRST step, once your device is booted (if your device is already provisionned, go to Settings > Backup & Reset > Factory data reset to reset it. Beware : all your data will be lost). Once the NFC message is delivered, the second step will ask you to setup the Wifi connection. Once the Wifi connection is validated, the APK is donwloaded and your device is provisioned.
Also make sure the device screen are unlocked. NFC is not working when the screen is locked on Android.

see also http://stackoverflow.com/questions/21183328/how-to-make-my-app-a-device-owner

2. it can also be set via a shell command

Another way to set a Device Owner App on your device is by using the dpm command line tool. It’s useful when you are developping or when you have a full access on a rooted device.
In this case, your app will have to be installed first, like any other casual app, and then you could set it as a Device Owner App using the shell (mostly through adb if you are developing)

for instance :

$adh shell dpm set-device-owner com.test.my_device_owner_app/.MyDeviceAdminReceiver

The dpm utility is really simple actually. Its goal is to create a new file called device_owner.xml under /data/system/device_owner.xml (if the storage is not encrypted) that references the Device/Profile owner apps.

The android platform is then reading this file to check which application is considered as Device Owner or Profile Owner App.

On a rooted device, you could indeed, create this file by yourself, but since the dpm tool is doing it, you’d better use it (DRY principle) :

Runtime.getRuntime().exec(dpm set-device-owner com.test.my_device_owner_app);

Also notice that this tool is working only if no account is set for the user (make sure no account is set in Settings > Accounts) before its use.

More information on this tool is provided in a previous post.

3. There can be only one Device Owner App per device…

Once the Device Admin app is set, it’s absolutely possible to update new versions of this app in a normal way, without re-provisioning it through NFC or the dpm tool).

The file /data/system/device_owner.xml contains the package name of applications registered as Device Owner / Profile Owner apps. Updating these apps won’t infer on this file since the package name stays the same. Just make sure you’re always using the same certificate as the one you previously used when first setting you device owner for the first time (which is a standard rule of security for every application update in Android anyway).
Permissions can also be updated without the need to re-provision it through NFC, nor dpm.

you could also take a look at this question on SO.

4. … but there may be a Profile Owner for each user.

A Profile Owner App is, like Device Owner App, a specific kind of Admin App.
By definition there can be only one Device Owner app, but there may be a Profile Owner for each user.

The difference with the Device Owner App is that a Device Owner App is per-device basis, while a Profile Owner app is on a per profile basis. That means that while only one Device Owner App can be set, multiple Profile Owner app can be set, one on each user profile.

A Profile Owner App, is a subset of Device Owner App, that allows only certain API specific to users and profile. Profile Owner Apps can typically restrict the applications accessible or configure the settings for a specific user.

Setting a Profile Owner App can be done from a Device Owner App, when creating a new user, using the createAndInitializeUser() method where the DeviceAdminReceiver’s implementation represents the component that handle the Profile Owner.

When the user is created with createandInitializeUser(), the application corresponding to the package associated to the DeviceAdminReceiver is installer in the user’s profile. Internally, the API IPackageManager.installExistingPackageAsUser and the user is then started in background. The profile’s DeviceAdminReceiver and its associated package are considered as admin and Profile Owner for this user

When developping, you could also use dpm and specify for which user you want to set this app as a Profile Owner.

$adh shell dpm set-profile-owner com.test.my_device_owner_app/.MyDeviceAdminReceiver <USER_ID>

Notice that the use of dpm set-profile-owner is only possible for a user that is not set up (not yet initialized).

There’s also a way to set an application as a profile owner, without the need of a Device Owner App, or dpm. In this case, the user will be prompted to accept this application as a profile owner by sending a specific Intent (not detailed here though).

5. They can not be unset by any user …

… even the owner is not allowed to unset the Device Owner app!

Because a Device Owner app has full control over the device. The Device Owner app can not be modified by the user and the only way of removing this app is to do a factory reset.

You could reset your device, by going into Settings > Backup & Reset > Factory data reset. If this option is disabled, you’ll have to reboot your device in Recovery Mode and factory reset from here (depending on your hardware).

dpm utility offers no way to unset the Device Owner application.

Fortunately, there ‘s a way a unset it programmatically : ask the app to unset by itself. You can use DevicePolicyManager.clearDeviceOwnerApp() to make sure your app is not set as a Device Owner app anymore.

For example, in one of your Device Owner App’s activity :

DevicePolicyManager mDPM = (DevicePolicyManager) this.getSystemService(Context.DEVICE_POLICY_SERVICE);
mDPM.clearDeviceOwnerApp(getPackage());

6. They can dynamically manage users

A Device Owner app can create a new user, swith to its session, remove it.
This is a big advantage over other normal apps which are not autorized to do this.

2 methods are available to create users :

  • createUser() creates a new user, but this user which is not provisionned nor restricted. In a programmatic sense, no Profile Owner app is associated to it.
  • createAndInitializeUser() creates a new user and associate a Profile Owner app which is used to provision and restrict the user programmatically.

Device Owner apps also provides a way to switch to a user session from its handle.
This is particulary useful when you create a user and want to switch directly to its session.
A Device Owner App can also remove a specific user from its handle.

DevicePolicyManager mDPM = (DevicePolicyManager) this.getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName mDeviceAdminRcvr = new ComponentName(this, DeviceAdminRcvr.class);
UserHandle newUserHandle = mDPM.createUser(mDeviceAdminRcvr, "Temporary user");
mDPM.switchUser(mDeviceAdminRcvr, newUserHandle);

...
// once done with the user
mDPM.removeUser(mDeviceAdminRcvr, newUserHandle);

7. They can restrict the apps accessible for a user

You can easily limit the applications accessible for your user by “hiding” them using setApplicationHidden.

Example of use to hide the Google+ app :

DevicePolicyManager mDPM = (DevicePolicyManager) this.getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName mDeviceAdminRcvr = new ComponentName(this, DeviceAdminRcvr.class);
mDPM.setApplicationHidden(mDeviceAdminRcvr, "com.google.android.apps.plus", false)

You can set them visible in a same way by placing the last flag to true.
Also notice that, you will not see notifications of hidden applications.

Bad thing is that you cannot, programmatically and silently, install new applications for a user. To do this, you’ll need to have the permission INSTALL_PACKAGES, and this permission is not given for third party apps. You could still use it if your app is a system app or on a rooted device.

8. They can restrict the device and configure its settings

Device Owner App and Profile Owner App ARE Admin Apps. Everything you were used to do with Admin apps, can also be done with a Device Owner app. That includes restricting the security policy (password policies, lock policies, encryption policies, camera policies) and also wipe its data, lock the device or prompt for a password.

A Device Owner app is going further by offering some settings customization, through DevicePolicyManager.addUserRestriction, DevicePolicyManager.setSecureSetting, DevicePolicyManager.setGlobalSetting

You should know that you can :

  • Disallow some settings modifications with DevicePolicyManager.addUserRestriction. The list of settings that could be disallowed is provided in the UserManager.DISALLOW_XXX constants with some specific cases depending if you are calling from a Device Owner App, Profile Owner App, or from the Owner user.
  • Modifiy Global device settings with DevicePolicyManage.setGlobalSetting like auto time, adb enabled (Only a subset of Global Settings is available though). Only a Device Owner App can call this method.
  • Modify User settings with DevicePolicyManager.setSecureSetting. Only 2 settings are modifiable like the default input method and the skipping of the first use hint. These settings can be set from a Device Owner App or a Profile Owner App.

Sadly the documentation is not clear about what settings you can set or value to use, so I’won’t go in more detail here (I’m preparing a complete article on these).

9. They can make a your app a real Kiosk app

Screen Pinning is another great feature of Android for Workplace and Education. And when it’s configured from a Device Owner App through setLockTaskPackages, it offers a real Kiosk App.

Here are some screen pinning behavior that could be set when a Device Owner App autorizes an app to be pinned :

  • Only the Back(enter image description here) button remains visible in the navigation bar. Home(enter image description here) and Overview(enter image description here) buttons are not visible
  • … and because of this, it’s impossible to unpin the application manually
  • You can enter the pin mode without user confirmation.

You can have more details in my blog post Android 5 Screen Pinning.

10. Create Restricted Profile is impossible once a Device Owner App is set

The Android source code indicates that the creation a restricted profile is not allowed from Settings on tablets with a device owner or phones.

Programmatically, it’s not possible to create a new profile based on an existing one. The API exists but hidden (UserManager.createProfileForUser(String, int, int)). Accessing this API via reflection cannot be done because it needs the system permission MANAGE_USERS.

You should be aware of that and because you have more power, means you’ll have to restrict your user by yourself when a Device Owner App is set. You could use all restrictions API explained earlier to do that.

Going further

For now, because the documentation is very light on these subjets, and no book is out, The best way to go further is by browsing the Android Source Code base.

Some classes are particularly interesting : LauncherApps, DevicePolicyManager, DevicePolicyManagerService, deviceAdminReceiver, UserManager

vendredi 6 février 2015

Android 5 Screen Pinning

One of the key feature of Android Lollipop “For Workplace And Education” is a new Screen mode called “Screen Pinning”. This new mode offers a true way to create Kiosk applications within the android Lollipop platform.

In the next chapters, we’ll see in more details how to use them in Android 5 (API 21).

Pin an app : the manual way

Pinning an application is not enabled by default. You’ll have to enable it by going to : Settings > Security > Advanced > Screen Pinning : On. These step only need to be done once.

You can pin an app by :

  • Launch your app
  • touch on the Overview Button (enter image description here)
  • select your app and touch the pin icon enter image description here at the bottom of the app.

A confirmation message will appear. You need to confirm this dialog message to start the pinning mode :

enter image description here

To exit the screen pinning mode :

  • Touch and hold Back (enter image description here) and Overview(enter image description here) buttons at the same time for 2-3 seconds.

7 Things to know about the manual pinning mode

  • Home (enter image description here) and Overview (enter image description here) buttons are disabled, but every time you touch one of them, you’ll get a message to help you exit the pinning mode.
  • If a Screen Lock (Pattern, Pin or Password) is activated, you can force the user to unlock the Screen before unpinning. The confirmation dialog shown when starting a app pinning will provide an extra option “Ask for unlock pattern before unpinning”.
  • If a Screen lock is activated and this option is set, you’ll go to this Screen Lock and need to unlock your screen to go Home. Once the Screen Lock is displayed, You have no way to go back your app.
  • No notifications will be shown. Even your own app’s notifications won’t show.
  • From a pinned app, you cannot start a secondary app, unless this one has the same shared user ID (which means that the sharedUserIdis set in the AndroidManifest.xml and that second application is packaged with the same certificate). Other apps’ Activities won’t be allowed to be started and doing so (by using Context.startActivity()) will simply be ignored.
  • the Status bar is invisible. No notification, time, battery charge or other status information is displayed.
  • It’s always possible to turn the device off, or to turn the volume.

This fact that the user can unpin the application and go back to the Screen Lock is annoying, we’ll see, in the next chapter, how we programmatically pin an to go even further in pinning apps.

Pin an app : the programmatic way

Screen pinning is referred as “Lock Task mode” in the documentation API.
This mode is mainly referred in 3 public API available in Activity through startLockTask()/startLockTask() and DevicePolicyManager through setLockTaskPackages() . We’ll see also see other API useful to subscribe to Lock Task’s events, and to get current’s activity state.

There are actually two Lock Task modes, depending on whether you are “authorized” by a Device Owner App or not. These two modes also have impacts on the behavior of the pinning.

Note :Authorized” is clearly not the best word of choice for this because its sense is not clear in this context. This is however used in the API’s documentation to refer to the different level of restriction a user will have when entering in the Screen Pinning mode. Both modes can indeed pin the app, but the authorized one will put the user in a more restricted experience as we’ll see later.

Before going further, some things to know about the API :

  • It’s only possible for an app to pin itself. There is no public API to tell another app to pin (except for system-apps, which we’ll discuss later).
  • Because of this last restriction, you can only pin in authorized mode an app that you own.
  • Rebooting the device will always bring you back to the original mode : no application pinned.
  • No special permission is required.

Un-authorized Task Lock mode

Any application’s Activity can programmatically be pinned by using Activity.startLockTask().

A use-case for this is to pin the screen when a user touch a button. You could go further and only call this method if the app is not already pinned. You can check its status by using ActivityManager.isInLockTaskMode(). For example :

// Main Activity
protected void onCreate(Bundle savedInstanceState) {
    // ...
    am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    lockBtn = (Button) findViewById(R.id.btn_lock);
    lockBtn.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            pin();
        }
    });
    // ...
}

void pin() {
    if(!am.isInLockTaskMode()) {
        startLockTask();
    }
}

Exiting the Pinning mode is reversely done by using Activity.stopLockTask() :

void unlockClicked() {
    stopLockTask();
} 

Because this mode you can be used freely by any developer, a confirmation message will appear to the end user to confirm entering in this mode. Additionnaly, this mode is not totally restricted compared to the “Authorized Task Lock mode”. Here are some points to know when using pinning in unauthorized mode :

  • Just after the startLockTask() is executed, the user will get the confirmation dialog “Use Screen Pinning ?”.
  • You can manually unlock the app at anytime : Touch and hold Back (enter image description here) and Overview(enter image description here) buttons at the same time for 2-3 seconds.
  • Unlock (manually or programmatically) will bring you back to the Screen lock if a Keyguard is set and the option “Ask for unlock pattern before unpinning” has been set.

Authorized Task Lock mode

Only a Device Owner app can specify which apps are authorized to pin. So this mode is typically useful for “Organizations” who want to restrict their user to only one app without no way to manually exit the app.
DevicePolicyManager.setLockTaskPackages() is used to specify which apps (though their package names) are pinnable. In other words, it specify which apps are authorized to be pinned in a more restrictive way.
This method has to be called from a Device Owner application (Setting an application as a Device Owner App is beyond the scope of this article, and is probably be an idea for a future article).

If you try to use from an application that is not set as a device or profile owner, you’ll get a SecurityException.

setLockTaskPackages() is used with a ComponentName referring to your DeviceAdminReceiver’s implementation. You also pass it an Array of authorized packages as a last parameter :

 DevicePolicyManager mDPM = (DevicePolicyManager) this.getSystemService(DEVICE_POLICY_SERVICE);
 ComponentName mDeviceAdminRcvr = new ComponentName(this, MyDeviceAdminReceiver.class);

  mDPM.setLockTaskPackages(mDeviceAdminRcvr, {"com.android.test.authorizedpinningapp"});

Once your application is authorized in your Device Admin App, you can then call the Activity.startLockTask() from the app you want to pin (as presented in earlier chapter).

6 Things to know about the Authorized Pinning mode :

  • Only the Back(enter image description here) button remains visible in the navigation bar. Home(enter image description here) and Overview(enter image description here) buttons are not visible
  • … and because of this, it’s impossible to unpin the application manually
  • You’ll have to provide a way to programmatically unpin your app. (otherwise, you’ll have no way to go back Home, unless you reboot the device)
  • You can enter the pin mode without user confirmation.
  • An app which is authorized though the use of setLockTaskPackages can still be pinned manually by going to Overview (enter image description here) and click on the Pin icon. In this case, the behavior is the same as a manual pinning (confirmation message, etc.).
  • If the Back button is going back to another activity, a Toast appears “Unpinning is not allowed by your Organization”

Task Lock events

DeviceAdminReceiver’s onLockTaskModeEntering and onLockTaskExiting methods can be used in your Device Owner App to subscribe to events relative to entering and exiting “Lock Task mode”.
All pinning events are raised the same way, no matter if they are authorized or not, manually or programmatically pinned.

Here’s a short example :

public class MyDeviceAdminReceiver extends DeviceAdminReceiver {

    public void onLockTaskModeEntering(Context context, Intent intent, String pkg) {
Toast.makeText(context, "Lock task mode entered", LENGTH_LONG).show();
      // ....
    }

    public void onLockTaskModeExiting(Context context, Intent intent) {
        Toast.makeText(context, "Lock task mode exited", LENGTH_LONG).show();
       // ...
    }
}

The second parameter of onLockTaskModeEntering and onLockTaskModeExiting is relative to the sender’s intent. You can retrieve two useful information from this intent :

  • The corresponding Action by calling intent.getAction(). Depending on which method your are, you’ll get DeviceAdminReceiver.ACTION_LOCK_TASK_ENTERING or DeviceAdminReceiver.ACTION_LOCK_TASK_EXITING value.
  • The corresponding App Package by calling … no, no it’s a trap ! getPackage() will return null here. To get the package of the app whose Lock Task has changed, you’ll have to use intent.getStringExtra(DeviceAdminReceiver.EXTRA_LOCK_TASK_PACKAGE).

Going further :

The source code of this article is available on my Github’s repository.
The announcement on Android’s developers site.
Android Source Code on Github.
Android API : DevicePolicyManager, Activity, DeviceAdminReceiver, ActivityManager

jeudi 5 février 2015

Supervision JVisualVM de Tomcat 7 sous Vagrant

Le contexte en quelques mots :

  • J’ai une VM sous Vagrant (ou plus généralement sur un VirtualBox) headless (type Ubuntu)
  • Le VM fait tourner un serveur Tomcat 7
  • Je souhaite superviser ce serveur Tomcat 7 via JMX depuis le Host VirtualBox en utilisant JVisualVM (ou tout autre client JMX).

Paramétrer Tomcat :

Ce qu’il faut savoir lorsque l’on (active le serveur JMX de Tomcat via setenv.sh)[http://tomcat.apache.org/tomcat-7.0-doc/monitoring.html#Enabling_JMX_Remote], c’est qu’il créé un port JMX additionnel avec un numéro aléatoire. C’est problématique dans le cas ou nous souhaitons nous connecter derrière un firewall, mais c’est également problématique dans le cas d’un accès depuis Vagrant car les ports entre le Host et le Guest sont forwardés explicitement dans le Vagrantfile (ou dans la configuration VirtualBox). Et comme nous ne pouvons pas savoir quel port forwarder, nous ne pouvons pas assurer une connexion JMX.

L’idée est de s’appuyer sur le JmxRemoteLifecycleListener pour fixer les numéros de ports utilisé lors de la connexion JMX, à travers les attributs rmiRegistryPortPlatform (le port normallement fixé via -Dcom.sun.management.jmxremote.port) et rmiServerPortPlatform (le port au numéro créé aléatoirement).

Editer le fichier <TOMCAT_HOME>/conf/server.xml et ajouter le noeud Listener. Dans mon exemple je mets les ports 10100 et 10101. A adapter si besoin :

<Server port="8005" shutdown="SHUTDOWN">
  ...

  <Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener"
    rmiRegistryPortPlatform="10100" rmiServerPortPlatform="10101" />

  ...
</Server>

Dans le fichier <TOMCAT_HOME>/conf/setenv.sh, il n’est plus nécessaire de spécifier le port, on limite au strict minimum, à savoir : l’activation de JMX, et les flags permettant de se connecter dans authentification et sans SSL (à adapter selon les besoins pour ces derniers).

 CATALINA_OPTS="$CATALINA_OPTS \
   ...
   -Dcom.sun.management.jmxremote \
   -Dcom.sun.management.jmxremote.authenticate=false \
   -Dcom.sun.management.jmxremote.ssl=false"

Paramétrer Vagrant

Pour assurer une communication entre le Host et le Guest, il faut assurer un Port forwarding.
Pour plus d’informations sur la communication entre Guest -> Host et Host -> Guest, aller voir cette réponse sur StackOverflow.
Notre Vagrantfile peut donc maintenant être configurer pour assurer le forward des ports du Guest vers le Host :

  config.vm.network "forwarded_port", guest: 10100, host: 10100
  config.vm.network "forwarded_port", guest: 10101, host: 10101

Paramétrer JVisualVM

Malgré que les ports soient forwardés sur notre Host avec les mêmes ports, nous ne pouvons pas utiliser les adresses 127.0.0.1 ou localhost pour la connexion depuis JVisualVm : ces adresses sont associés à l’interface réseau LoopBack (ma machine local - mon Host - n’expose pas directement ces ports. Ils ne sont visible en local que par l’adapteur VirtualBox).
Il est donc nécessaire d’utiliser l’adresse IP associé à l’interface réseau VirtualBox. Pour connaitre cette adresse , sous Windows :

C:\> ipconfig /all
  ...
Carte Ethernet VirtualBox Host-Only Network :

   Suffixe DNS propre à la connexion. . . :
   Description. . . . . . . . . . . . . . : VirtualBox Host-Only Ethernet Adapter
   Adresse physique . . . . . . . . . . . : 08-00-27-00-7C-55
   DHCP activé. . . . . . . . . . . . . . : Non
   Configuration automatique activée. . . : Oui
   Adresse IPv4. . . . . . . . . . . . . .: 192.168.56.1(préféré)
   Masque de sous-réseau. . . . . . . . . : 255.255.255.0
   Passerelle par défaut. . . . . . . . . :
   NetBIOS sur Tcpip. . . . . . . . . . . : Activé

Ici notre adresse est 192.168.56.1 : c’est cette adresse IP que nous devons utiliser pour JVisualVM.

Dans JVisualVM, Créer un nouveau Remote avec cette adresse :

  • Clic droit sur Remote > Add Remote Host > 192.168.56.1.
  • Advanced Settings > Port 10100, puis valider
  • Clic-droit sur le Remote nouvellement créé, puis Add JMX Connection.
  • renseigner Connection:192.168.56.1:10100, puis valider.
  • La connexion apparait dans la partie Local. Sélectionner le connexion, puis clic-droit > Open.