31

Is there a way to make an app install directly in the system/app folder while developing on Android Studio (the device is rooted)?

Meaning, when I press on the 'Run app' button, I want the apk to be placed in system/app.

If this is not possible, what is the recommended most convenient way to work on building and testing a system app?

dors
  • 5,802
  • 8
  • 45
  • 71

6 Answers6

45

Deploy automatically system app from AS

You can create a script that will do the job, and run it automatically each time you hit run in AS.

1. Create the script

You can adapt this script that I've created from my needs. Place it in: project_directory/installSystem.sh

#!/bin/bash

# CHANGE THESE FOR YOUR APP
app_package="com.example"
dir_app_name="MySysApp"
MAIN_ACTIVITY="SysAppMainActivity"

ADB="adb" # how you execute adb
ADB_SH="$ADB shell" # this script assumes using `adb root`. for `adb su` see `Caveats`

path_sysapp="/system/priv-app" # assuming the app is priviledged
apk_host="./app/build/outputs/apk/app-debug.apk"
apk_name=$dir_app_name".apk"
apk_target_dir="$path_sysapp/$dir_app_name"
apk_target_sys="$apk_target_dir/$apk_name"

# Delete previous APK
rm -f $apk_host

# Compile the APK: you can adapt this for production build, flavors, etc.
./gradlew assembleDebug || exit -1 # exit on failure

# Install APK: using adb root
$ADB root 2> /dev/null
$ADB remount # mount system
$ADB push $apk_host $apk_target_sys

# Give permissions
$ADB_SH "chmod 755 $apk_target_dir"
$ADB_SH "chmod 644 $apk_target_sys"

#Unmount system
$ADB_SH "mount -o remount,ro /"

# Stop the app
$ADB shell "am force-stop $app_package"

# Re execute the app
$ADB shell "am start -n \"$app_package/$app_package.$MAIN_ACTIVITY\" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER"

2. Bind it with AS Run

  1. Go to Run -> Edit Configurations
  2. Do the following changes on General tab (of your module)

    • Installation Options->Deplay: Nothing
    • Launch Options->Launch: Nothing
    • Before launch: press +, then Run External Tool, to select your script.
      • In the new dialog:
        • set any name.
        • On 'Tool Settings'->Program: navigate to the project's dir, and select your script

Caveats :

First installation

The device needs to be restarted (adb reboot) only once, on the very first installation of your app. Afterwards, you can simply press Run and everything will happen automatically.

This is because the host compiler (dex2oat) is not invoked automatically. Somehow the OS is not yet informed for this new system app. Calling dex2oat manually should solve this, but I had no luck. If anyone solves it please share.

adb root issues

Sometimes (usually the initial execution after the restart) the call to adb root does not find the device. You can simply re-play from AStudio, or sleep for a second after a successful adb root.

using su instead of adb root

adb push won't be working despite mounting system and giving permissions. To make it work replace the ADB_SH variable and the install section of the script with the following:

..
ADB_SH="$ADB shell su -c"
..
# Install APK: using adb su
$ADB_SH "mount -o rw,remount /system"
$ADB_SH "chmod 777 /system/lib/"
$ADB_SH "mkdir -p /sdcard/tmp" 2> /dev/null
$ADB_SH "mkdir -p $apk_target_dir" 2> /dev/null
$ADB push $apk_host /sdcard/tmp/$apk_name 2> /dev/null
$ADB_SH "mv /sdcard/tmp/$apk_name $apk_target_sys"
$ADB_SH "rmdir /sdcard/tmp" 2> /dev/null
jcady
  • 3,850
  • 2
  • 21
  • 21
Paschalis
  • 11,929
  • 9
  • 52
  • 82
  • 3
    This is great!, I made one small change though, starting from Android 4.3+ the path is not `/system/app` it should be `/system/priv-app`, thank you for this, it has helped me automate the build process. – Roberto Nov 13 '16 at 18:11
  • you are welcome! `priv-app` is for apps that actually require system-level (e.g. Settings) permissions, whereas `app` is for the rest (e.g. Google Calendar) – Paschalis Nov 15 '16 at 17:18
  • 1
    This script is really useful! On Windows, I had to modify it a little bit in order to make it work: `ADB_EXE=/c/ADB/adb` and `ADB_SH="$ADB_EXE shell"` and everywhere `adb` is called, replace it with `$ADB_SH`. Also, in §2, on Tool Settings, section Programs: put C:\Program Files\Git\usr\bin\bash.exe for example, and section Params, the path of your script. – MBach Feb 16 '17 at 21:41
  • 3
    @Paschalis I tried this but it gave the error = 13 (Permission denied). Do you have any idea how to go around it? Thanks – João Cartucho Apr 26 '17 at 21:43
  • Have you given `execute` permissions on the script that runs from your host machine? Does your `target` device have root access? `adb root` or `su`? – Paschalis Apr 27 '17 at 04:52
  • 2
    @JoãoCartucho try `chmod +x installSystem.sh` – Vlad Aug 15 '17 at 06:12
  • 1
    If you want this to work with a virtual device you have to start the emulator using `emulator -avd AVD_NAME -writable-system`. – jcady Sep 08 '17 at 20:13
  • @JoãoCartucho just set the .sh file as executable – John61590 Jun 29 '18 at 01:21
  • @Paschalis I am getting CreateProcess error = 193. Have you got any idea how can I solve this error? – limonik Jun 25 '20 at 09:14
  • I'm getting mount: '/system' not in /proc/mounts while executing command mount -o rw,remount /system – bitdancer Sep 21 '21 at 04:09
  • 1
    @bitdancer at which version? `/system/` is readonly from Android10 onward I think! There is no way to make it writeable. They way to go is `magisk` with a custom magisk module. – Paschalis Sep 21 '21 at 05:14
  • @Paschalis Yes. I'm using Android 11. – bitdancer Sep 23 '21 at 06:01
  • am force-stop doesnt appear to work on android 11. i'm using 'adb shell killall $app_package' instead, which works for now – aep Mar 23 '22 at 15:25
9

Windows script for those interested:

Store this file the same way: in the root of your project directory (installSysPrivApp.bat)

::WIN BATCH SCRIPT

:: CHANGE THESE
set app_package=com.example.package
set dir_app_name=app
set MAIN_ACTIVITY=MainActivity

set ADB="adb"
::ADB_SH="%ADB% shell" # this script assumes using `adb root`. for `adb su` 
see `Caveats`

set path_sysapp=/system/priv-app
set apk_host=.\Application\build\outputs\apk\Application-debug.apk
set apk_name=%dir_app_name%.apk
set apk_target_dir=%path_sysapp%/%dir_app_name%
set apk_target_sys=%apk_target_dir%/%apk_name%

:: Delete previous APK
del %apk_host%

:: Compile the APK: you can adapt this for production build, flavors, etc.
call gradlew assembleDebug

set ADB_SH=%ADB% shell su -c

:: Install APK: using adb su
%ADB_SH% mount -o rw,remount /system
%ADB_SH% chmod 777 /system/lib/
%ADB_SH% mkdir -p /sdcard/tmp
%ADB_SH% mkdir -p %apk_target_dir%
%ADB% push %apk_host% /sdcard/tmp/%apk_name% 
%ADB_SH% mv /sdcard/tmp/%apk_name% %apk_target_sys%
%ADB_SH% rmdir /sdcard/tmp

:: Give permissions
%ADB_SH% chmod 755 %apk_target_dir%
%ADB_SH% chmod 644 %apk_target_sys%

::Unmount system
%ADB_SH% mount -o remount,ro /

:: Stop the app
%ADB% shell am force-stop %app_package%

:: Re execute the app
%ADB% shell am start -n \"%app_package%/%app_package%.%MAIN_ACTIVITY%\" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Birdnado
  • 156
  • 1
  • 7
  • I am getting CreateProcess error = 193. Have you got any idea how can I solve this error? – limonik Jun 25 '20 at 12:00
  • 1
    Just a possible improvement: delete the line to delete the previous APK and to call Gradle. Let Android Studio do that itself. It was giving me errors with that enabled. So I commented those 2 lines and let Android Studio mess with Gradle. Then the script only installs the APK that Android Studio created with Gradle. Maybe this might be a good idea too on Linux, no idea (I don't use Linux with Android Studio). I also put the `set apk_host` line as `set apk_host=.\app\build\outputs\apk\debug\app-debug.apk`, since Android Studio puts the APK there. – Edw590 Dec 16 '20 at 19:31
2

To bypass reboot issue from the @paschalis answer reinstall application with a help of package manager before remounting system to read only:

# Reinstall app
$ADB_SH "pm install -r $apk_target_sys"

# Unmount system
$ADB_SH "mount -o remount,ro /"

Package manager will invoke dex2oat by itself.

0

(Android Q >> Windows)

::WIN BATCH SCRIPT
::setup emulator https://stackoverflow.com/a/64397712/13361987

:: CHANGE THESE 
set app_package=com.project.package
set dir_app_name=NewApkName
set MAIN_ACTIVITY=Package.MainActivity

set ADB="adb"

set path_sysapp=/system/priv-app
set apk_host=.\app\build\outputs\apk\debug\app-debug.apk
set apk_name=%dir_app_name%.apk
set apk_target_dir=%path_sysapp%/%dir_app_name%
set apk_target_sys=%apk_target_dir%/%apk_name%

:: Delete previous APK
del %apk_host%

::  Compile the APK: you can adapt this for production build, flavors, etc.
call gradlew assembleDebug

set ADB_SH=%ADB% shell su 0

:: Install APK: using adb su
%ADB_SH% mount -o remount,rw /system
%ADB_SH% chmod 777 /system/lib/
%ADB_SH% mkdir -p /sdcard/tmp
%ADB_SH% mkdir -p %apk_target_dir%
%ADB% push %apk_host% /sdcard/tmp/%apk_name%
%ADB_SH% mv /sdcard/tmp/%apk_name% %apk_target_sys%
%ADB_SH% rm -r /sdcard/tmp

:: Give permissions
%ADB_SH% chmod 755 %apk_target_dir%
%ADB_SH% chmod 644 %apk_target_sys%

:: Unmount system
%ADB_SH% mount -o remount,ro /

:: Stop the app 
%ADB% shell am force-stop %app_package%

:: Re execute the app
%ADB% shell am start -n \"%app_package%/%app_package%.%MAIN_ACTIVITY%\" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER




:: from >> https://stackoverflow.com/questions/28302833/how-to-install-an-app-in-system-app-while-developing-from-android-studio
niek tuytel
  • 899
  • 7
  • 18
0

For mac

By using the script of @Paschalis I got 3 problems first I couldn't mount the system from adb so I did it with "terminal emulator for android" from jack palevich only (needed once) https://play.google.com/store/apps/details?id=jackpal.androidterm

mount -o rw,remount /system

the second problem was the JRE that was not the same as Android studio.

so I added

 export JAVA_HOME=/Applications/Android\ Studio.app/Contents/jre/jdk/Contents/Home/

to have the same version of java that android studio was using ("ctrl + ;" in android studio to get this path)

And the last problem because of adb root that can not run in production build so I flashed this zip with magisk

https://github.com/evdenis/adb_root

Android: adbd cannot run as root in production builds

but then my phone was not detected anymore so I removed adb root and this time all works well.

Also try to run the script manually line by line in a terminal to debug this script android studio does not give all the error.

bormat
  • 1,309
  • 12
  • 16
-4

I think adb push *.apk /system/app/*.apk should do just fine.

I don't know about Android Studio, but if you are on Linux you can get try to create an alias for

adb install

that points to that command, It should work!

Amedeo Baragiola
  • 314
  • 4
  • 14
  • This does not work. getting "failed to copy '[FILE NAME]' to '/system/app/[file name]': Read-only file system". I've already seen flows to make what you suggest work, such as http://stackoverflow.com/a/10362072/876603, but this is a very long flow for development – dors Feb 04 '15 at 17:41
  • You are right, I forgot you also have to re-mount the filesystem rw on order to make that trick work... I don't know any other method, sorry – Amedeo Baragiola Feb 04 '15 at 17:43