Installing Xposed in Android on Chrome OS

UPDATEAt present, for the latest Chrome OS versions (69, 70+), the following method seems to be the most reliable:

1. Ensure that your Android container is rooted, and that the rootfs is writable.

(If you've already used the rooting script, but you find the Android rootfs still isn't writeable, this may help).

2.  Download the Xposed framework installer app (to ~/Downloads).

3. Install the app via the Chrome OS shell (as root), e.g. :
sudo su -
setenforce 0
printf "pm install /storage/emulated/0/Download/XposedInstaller_3.1.5.apk" | android-sh 
printf reboot | android-sh
4. Assuming the installation was successful, open the Xposed installer app from the Chrome OS Launcher, click on 'Install/Update' then, in the next menu, click on 'Install'.

NB: If the prompt to grant root access doesn't show up at this point, see step 2. of the older instructions further down this page.

5. Once the framework installer app indicates it is 'Done', then reboot. 

The above steps should work on current Chrome OS versions. If you find it doesn't work for you, feel free to leave a comment on this post.

The original blog post is below...





Just a short post to note that it is now possible to successfully install and use Xposed in Android in Chrome OS. It works for me on CrOS version 67.0.3390.0 canary (arm), but it may be possible on some earlier builds, too.* 





I created a shell script that copies over Xposed's files some time ago, just as an experiment to see if it worked, but did not have success booting Android after modifying the rootfs with the script. I decided to take another look recently, and realised that it had a couple of bugs. Once those were fixed, I was able to successfully install Xposed with my script. 

 After success with a script based approach, I thought I'd try using the official installer app, and found that it actually isn't necessary to copy the files manually/with a script. Given a writeable Android rootfs, the installer app was able to install the framework successfully, as long as I allowed the app to gain root (which, on at least one CrOS version, necessitated temporarily setting 'Default Access' in SuperSU to grant root automatically, since the regular pop-up root request prompt didn't appear for this app). 

Here are the exact steps I took to install Xposed from its installer app.

1. After creating a rooted writeable Android rootfs with the rooting script, I downloaded the latest Xposed installer app from the Xposed announcement thread on xda.  

2. Next, I had to open the SuperSU app, go into its settings and temporarily set 'Default Access' to 'Grant'.

3.  After this, I simply clicked on 'Install/Update' in the Xposed installer, and then 'Install' again in the following menu. 

Following this, Xposed was installed, and all that was left to do now was reboot Android. To reboot Android without having to reboot Chrome OS, in a root CrOS shell, I entered 

printf reboot | android-sh

Just like on regular Android devices, the first boot after installing Xposed takes some time. To check on the progress (and to ensure that the Android container wasn't bootlooping, as had been the case when I'd previously attempted to install Xposed manually, without success, some time ago), I entered 

printf logcat  android-sh

into the CrOS root shell.  Happily, unlike my previous attempts in the past, this time I could see the boot proceeding as expected. 

After Android had loaded up, I went into the Xposed installer again, and added a few modules. Everything seems to be working as expected, as far as I can see, within certain limitations, mostly related to running in a container environment.

For example: adding shortcuts and actions to screen tap areas with 'Xposed Edge' works, but the shortcuts/actions only function when Android apps are actually on the screen. Also, the lack of 'parallel running of tasks' in the CrOS version I'm currently running could possibly be an issue.

In any case, now I actually have Xposed working, I can't seem to see much use for it at present. However, I guess it could possibly be useful for something in future.

*It seems not to be easily possible currently to install Xposed via the installer app on CrOS version 65 at present, due to a (temporary) issue mounting the Android rootfs as writeable at runtime. Installing files (manually, or via a script) into the container image while it is mounted elsewhere does still work. The writeable mount issue is also present on v66 but can be fixed by editing the container config.json (deleting the string "dev", from the "rootfs/root" section under "mounts". 

Modifying hardware key layouts for Android apps on Chrome OS

I recently used an Android terminal emulator on Chrome OS for the first time in a while, and encountered the following issue: although my locale/keyboard settings in CrOS were set to UK, it seemed to be the case that certain Android apps, e.g. terminal emulators were stuck with the US keyboard mappings. Due to this, I found myself mistyping certain characters (mainly @ and "), when switching back and forth between the Chrome OS shell and Termux/Terminal Emulator. 

To fix the keylayout for the locale of my Chromebook's physical keyboard, I modified a couple of files on the rootfs. Although I edited the files directly, which requires a writeable Android system, it's also possible to make a copy, edit the copy, then bind mount it over the original.


Switching the " and @


First way - editing the file directly. Modified (R/W) Android rootfs needed.

With a read/write Android filesystem, as created with the rooting scripts, the easiest way to fix the key layout seemed to be by editing the relevant key mapping file directly. It would be best to make a backup of the file to be edited beforehand.

I opened a root shell in CrOS and entered
vi /opt/google/containers/android/rootfs/root/system/usr/keychars/Generic.kcm
(To swap the " and @ characters, in vi I entered /@ to search Generic.kcm for the @ character, pressed x to delete the @, then pressed i to enter "insert text mode" and typed " to add the quote mark character at the place where the @ had been. I did similar with the " character, replacing it with @)

After modifying the file I saved and quit vi by typing :wq!

I then rebooted the Android container
printf reboot | android-sh
Now, in Termux etc., Shift+2 types the " character, and Shift+' types the @ character, as is expected with a UK keyboard layout. 

Second way - Editing a copy of the file and bind mounting it over the original.

It's also possible to do it without editing the file directly, by copying it out to /usr/local (or to a place accessible within the File Browser such as ~/Downloads) editing the copy, bind mounting it back in place, then rebooting the Android container. In this case, the bind mount and Android reboot have to be redone after every time Chrome OS is rebooted.

i.e.

Either:

To copy the file to /usr/local and edit it with vi, then bind it back: 

Copy the file with permissions preserved
cp -a /opt/google/containers/android/rootfs/root/system/usr/keychars/Generic.kcm /usr/local
Edit the copy e.g. in vi (as above).

Bind mount the file back over the original
mount --bind /usr/local/keychars/generic.kcm /opt/google/containers/android/rootfs/root/system/usr/keychars/Generic.kcm
Then reboot the Android container so it sees the new file
printf reboot | android-sh
Or: 

Alternatively, it might be easier to copy the file to ~/Downloads and edit it with another text editor (e.g. Caret), then move it to /usr/local and bind mount it & reboot Android

Copy the file to ~/Downloads
cp -a /opt/google/containers/android/rootfs/root/system/usr/keychars/Generic.kcm /home/chronos/user/Downloads
After editing the file in text editor, move it to /usr/local/
mv /home/chronos/user/Downloads/Generic.kcm /usr/local/Generic.kcm
Mount bind and then reboot Android as above

Adding the #~ key found on UK keyboards


After swapping the " and @, I still had an issue with the key for # and ~ (located to the left of the Enter key on my UK keyboard). It appeared to have the same mapping in Android as the \ key (to the right of my left Shift key). It turned out that to fix this I had to do two things:

Step 1. Changing key 43 in cros_ec.kl

I found that key 43 and key 86 were both mapped to BACKSLASH at /system/usr/keylayout/cros_ec.kl in the Android container. I had a look at the list of key codes for Android, and decided to change key 43 from BACKSLASH to POUND, since the key code POUND in Android apparently refers to the # key found on phones.

Step 2. Changing the relevant key declaration in Generic.kcm  

then opened the key character map file I'd previously modified (/system/usr/keychars/Generic.kcm), found the key declaration for POUND therein, which looked like this:


key POUND {
    label:                              '#'
    base:                              '#'
}

And added a line for the shift modifier.


key POUND {
    label:                              '#'
    base:                              '#'
    shift:                               '~'
}

 I rebooted Android again, and now my keyboard was finally behaving how I wanted it to in Android terminal apps. 

If anyone is considering making the changes detailed above, or similar, I should mention that Android is quite particular about how it expects these files to be; if the formatting is at all out of place or if an unexpected character is present then Android might fail to load, or revert to a fallback character mapping. If the latter is happening then logcat usually notes the offending line in the problematic file, which can be seen in the output to the following (entered in a CrOS root shell):

printf "logcat | grep key" | android-sh
In  the event of Android failing to load completely and the above command returning an error, if the files had been bind mounted, a reboot would fix it, and if they were edited directly it might be necessary to restore the original file/s from backups.

Enabling Managed Profile in Android in Chrome OS

Sometimes it's useful to be able to run two copies of an app simultaneously in Android, e.g. for testing purposes, or to have two separate logins with an app which doesn't usually support this functionality. 

This can usually be done via the excellent app Island, which utilizes the Work/Managed profile functionality in Android in order to 'clone' selected apps, which can then be used either independently or both at the same time.

I wanted to have this functionality in Android on Chrome OS, but I found that initially the Island app didn't seem to be able to install properly.

I modified the file /system/etc/permissions/cheets.xml within the Android container, removing the word "unavailable" from two relevant lines, so

<unavailable-feature name="android.software.device_admin" />
<unavailable-feature name="android.software.managed_users" />


 becomes

<feature name="android.software.device_admin" />
<feature name="android.software.managed_users" />


After rebooting Android, I was able to install Island fully, and was able to run it as normal, cloning apps, then keeping two copies of the same app open at the same time.

Since the Android file /system/etc/permissions/cheets.xml is located within the Android container, a writeable Android rootfs is needed in order to modify the file directly. One way of achieving this is via the rooting scripts.

Alternatively, for example if one perhaps wants just to run a quick test for a cloned app, the original rootfs can be kept untouched, and an edited cheets.xml file bind mounted over the original. However, the bind mount would only last until the Chromebook is rebooted, which could lead to unexpected consequences.

e.g:

Copy cheets xml to /usr/local
 cp /opt/google/containers/android/rootfs/root/system/etc/permissions/cheets.xml /usr/local/cheets.xml   
Remove the word "unavailable-" from the device_admin line
sed -i 's/unavailable-feature name="android.software.device_admin"/feature name="android.software.device_admin"/g' /usr/local/cheets.xml
Remove the word "unavailable-" from the managed_users line
sed -i 's/unavailable-feature name="android.software.managed_users"/feature name="android.software.managed_users"/g' /usr/local/cheets.xml
Bind mount the edited cheets.xml back over the original
mount -o bind /usr/local/cheets.xml /opt/google/containers/android/rootfs/root/system/etc/permissions/cheets.xml
Finally, reboot the android container
printf reboot | android-sh
I should note that, although I haven't noticed any issues as yet, enabling unsupported features may have unexpected consequences. It would be prudent to ensure any important files are backed up before experimenting.