📜 ⬆️ ⬇️

Application implementation - device owner for Android

This article is a guide to writing and installing on the target device of the application - the device owner. It led me to write this article that when I myself began to study this question, it turned out that there was no good official documentation with examples, and the information had to be collected with the help of Google.

In Android OS, starting with version 5.0 Lollipop (API 21), there is a wonderful opportunity to control the device programmatically, while in device owner mode. For example, it has become possible to make a “silent” installation / removal of applications, “hide” applications (and they hide qualitatively, that is, disappear from the list of applications in the settings, disappear from the launcher and the list of recently used applications), and do much more. These are very useful features for implementing MDM. An overview of all the possibilities that device owner-s is beyond the scope of the article, you can read about them here and here .

Terminology


To begin, we define the terms. The device owner is obviously the owner of the device. Usually, the first created user becomes the owner of the device at the initial device configuration, which occurs after the device is first turned on after the purchase. But in our case we will make it so that the owner of the device will be our application, which will get access to the use of advanced device management capabilities. Unprovisioned state - the state of the device before the initial configuration. The device is in this state after the first power up and after wipe. Provisioned state - the state of the device after the initial configuration was performed.

What is required?


For our experiments, we will need two devices on which there is NFC and one of these devices we will have to wipe. In order to install the device owner application on the target device, you need to upload it to the server so that it is available at the URL of the target device, for example example.com/deviceowner.apk example.com/deviceowner.apk (I tried only the http protocol). Next, you need to bring the target device to an unprovisioned state, for example, make a wipe. After that, you need to install an installer on another device. Then you need to combine these two devices so that NFC data exchange is possible, usually you just need to attach the devices to each other with the back surfaces, then you need to confirm the transfer via NFC by tapping on the device screen with the application - installer. After that, the initial configuration of the target device will continue and you will need to configure the network so that the device can download the apk file with the application - device owner. After the initial configuration is completed, the device owner application will be available for launch and use and it will not be possible to stop / delete it in any way and with nothing but to wipe the device.
')

Application - device owner


I wrote an example of device owner and installer applications. Below are the most interesting code snippets. It makes no sense to write all the code in the article; at the end of the article there will be links to projects with full source code.

Application - device owner will control the visibility of other applications. Those. with it you can hide / show other applications. There is an AppsManager class in the application, it encapsulates the list building and application management. The list is obtained in AsyncTaske-e:

 private class LoadingTask extends AsyncTask<Void, Void, List<ApplicationInfo>> { @Override protected List<ApplicationInfo> doInBackground(final Void... params) { final PackageManager packageManager = mContext.getPackageManager(); return packageManager.getInstalledApplications( PackageManager.GET_META_DATA | PackageManager.GET_UNINSTALLED_PACKAGES ); } @Override protected void onPostExecute(final List<ApplicationInfo> result) { if (result != null) { mAppsList = result; } else { mAppsList = Lists.newArrayList(); } mStateObservable.setValue(State.IDLE); } } 

Control takes place via DevicePolicyManager:

 mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); public void showApp(final ApplicationInfo app) { if (mStateObservable.getValue() != State.IDLE) { return; } mDevicePolicyManager.setApplicationHidden(mAdminComponent, app.packageName, false); } public void hideApp(final ApplicationInfo app) { if (mStateObservable.getValue() != State.IDLE) { return; } mDevicePolicyManager.setApplicationHidden(mAdminComponent, app.packageName, true); } public boolean isAppHidden(final ApplicationInfo app) { return mDevicePolicyManager.isApplicationHidden(mAdminComponent, app.packageName); } 

The basis of the UI is RecyclerView, everything is trivial, I will give the adapter code:

 private static class AppsListAdapter extends RecyclerView.Adapter<AppsListAdapter.ViewHolder> { private final Context mContext; private final LayoutInflater mInflater; private final PackageManager mPackageManager; private List<ApplicationInfo> mAppsList; private final AdministrationModeManager mAdministrationModeManager = AdministrationModeManager.getInstance(); private final AppsManager mAppsManager = AppsManager.getInstance(); public AppsListAdapter(final Context context) { mContext = context; mInflater = LayoutInflater.from(mContext); mPackageManager = mContext.getPackageManager(); } @Override public ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) { final View layout = mInflater.inflate(android.R.layout.simple_list_item_multiple_choice, parent, false); return new ViewHolder(layout); } @Override public void onBindViewHolder(final ViewHolder holder, final int position) { holder.mAppTitleTextView.setText(mAppsList.get(position).loadLabel(mPackageManager)); if (mAdministrationModeManager.isAdministrator() && mAdministrationModeManager.isDeviceOwner()) { holder.mAppTitleTextView.setChecked(!mAppsManager.isAppHidden(mAppsList.get(position))); } } @Override public int getItemCount() { return mAppsList == null ? 0 : mAppsList.size(); } public void setAppsList(final List<ApplicationInfo> appsList) { mAppsList = appsList; } public class ViewHolder extends RecyclerView.ViewHolder { public final CheckedTextView mAppTitleTextView; public ViewHolder(final View itemView) { super(itemView); mAppTitleTextView = (CheckedTextView) itemView.findViewById(android.R.id.text1); mAppTitleTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { if (mAdministrationModeManager.isAdministrator() && mAdministrationModeManager.isDeviceOwner()) { if (mAppTitleTextView.isChecked()) { mAppsManager.hideApp(mAppsList.get(getAdapterPosition())); } else { mAppsManager.showApp(mAppsList.get(getAdapterPosition())); } notifyDataSetChanged(); } } }); } } } 

The peculiarity of the application is that it needs to implement some reciever to get administrator rights, at least empty:

 public class AdminReceiver extends DeviceAdminReceiver { // do nothing } 

In the manifest, you need to specify this receiver with the appropriate settings:

 <receiver android:name=".AdminReceiver" android:description="@string/app_name" android:label="@string/app_name" android:permission="android.permission.BIND_DEVICE_ADMIN"> <meta-data android:name="android.app.device_admin" android:resource="@xml/device_owner_receiver"/> <intent-filter> <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> <action android:name="android.app.action.PROFILE_PROVISIONING_COMPLETE"/> </intent-filter> </receiver> 

In order for these settings to work, you must also put the device_owner_receiver.xml file in the “xml” resource directory with a description of what the application is going to manage as an administrator:

 <?xml version="1.0" encoding="utf-8"?> <device-admin> <uses-policies> <limit-password/> <watch-login/> <reset-password/> <force-lock/> <wipe-data/> <expire-password/> <encrypted-storage/> <disable-camera/> </uses-policies> </device-admin> 

As a result, the application must be collected and the apk file put on the server

Application - installer


An installer application is simply an application that starts up and, after being combined with the target device, transmits NFC data that contains information from where the target device should take the device owner, below the code that forms the NFC message:

 private class NdefMessageCallback implements NfcAdapter.CreateNdefMessageCallback { @Override public NdefMessage createNdefMessage(final NfcEvent event) { final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final Properties properties = new Properties(); properties.put(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME, "com.example.deviceowner"); // Make sure to put local time in the properties. This is necessary on some devices to // reliably download the device owner APK from an HTTPS connection. properties.put( DevicePolicyManager.EXTRA_PROVISIONING_LOCAL_TIME, String.valueOf(System.currentTimeMillis()) ); // To calculate checksum execute command (taken from http://stackoverflow.com/questions/26509770/checksum-error-while-provisioning-android-lollipop): // cat Something.apk | openssl dgst -binary -sha1 | openssl base64 | tr '+/' '-_' | tr -d '=' properties.put( DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM, "[Device owner app checksum]" ); properties.put( DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION, "[Device owner app URL]" ); try { properties.store(outputStream, getString(R.string.nfc_comment)); final NdefRecord record = NdefRecord.createMime( DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC, outputStream.toByteArray() ); return new NdefMessage(new NdefRecord[]{record}); } catch (final IOException e) { throw new RuntimeException(e); } } } 

Pay attention to the following points:


Experiments


Both applications must be assembled, the installer must be installed on one device, the device owner must be laid out on the server, the target device is a wipe. After that, you can start the experiments.

To demonstrate the function of hiding the application, I will hide the system settings on the target device:


Here we see that the settings are in the list of applications.


Here we see that the settings are in the list of recently used applications.


Turn off the application - settings.


Settings disappeared from the list of recently used applications.


And from the list of applications.


Even if you try to run the settings from the “curtain” (circled by a green rectangle) and in other ways you will fail.

Links to projects


Source codes for application projects:

github.com/raynor73/DeviceOwner
github.com/raynor73/NfcProvisioning

Source: https://habr.com/ru/post/271951/


All Articles