可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am trying to silently install apk into the system.
My app is located in /system/app and successfully granted permission \"android.permission.INSTALL_PACKAGES\"
However I can\'t find anywhere how to use this permission. I tried to copy files to /data/app and had no success. Also I tried using this code
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(
Uri.parse(\"file:///sdcard/app.apk\"),
\"application/vnd.android.package-archive\");
startActivity(intent);
But this code opens standard installation dialog. How can I install app silently without root with granted android.permission.INSTALL_PACKAGES
?
PS I am writing an app that will install many apks from folder into the system on the first start (replace Setup Wizard). I need it to make firmware lighter.
If you think that I am writing a virus: All programs are installed into /data/app. Permission Install_packages can only be granted to system-level programs located in /system/app or signed with the system key. So virus can\'t get there.
As said http://www.mail-archive.com/android-porting@googlegroups.com/msg06281.html apps CAN be silent installed if they have install_packages permission. Moreover you don\'t need Install_packages permission to install packages not silently. Plus http://www.androidzoom.com/android_applications/tools/silent-installer_wgqi.html
回答1:
Your first bet is to look into Android\'s native PackageInstaller. I would recommend modifying that app the way you like, or just extract required functionality.
Specifically, if you look into PackageInstallerActivity and its method onClickListener
:
public void onClick(View v) {
if(v == mOk) {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
...
newIntent.setClass(this, InstallAppProgress.class);
...
startActivity(newIntent);
finish();
} else if(v == mCancel) {
// Cancel and finish
finish();
}
}
Then you\'ll notice that actual installer is located in InstallAppProgress class. Inspecting that class you\'ll find that initView
is the core installer function, and the final thing it does is call to PackageManager
\'s installPackage
function:
public void initView() {
...
pm.installPackage(mPackageURI, observer, installFlags, installerPackageName);
}
Next step is to inspect PackageManager, which is abstract class. You\'ll find installPackage(...)
function there. The bad news is that it\'s marked with @hide. This means it\'s not directly available (you won\'t be able to compile with call to this method).
/**
* @hide
* ....
*/
public abstract void installPackage(Uri packageURI,
IPackageInstallObserver observer,
int flags,String installerPackageName);
But you will be able to access this methods via reflection.
If you are interested in how PackageManager
\'s installPackage
function is implemented, take a look at PackageManagerService.
Summary
You\'ll need to get package manager object via Context
\'s getPackageManager()
. Then you will call installPackage
function via reflection.
回答2:
I have checked how ADB installs apps.
- It copies the APK to /data/local/tmp
- it runs \'shell:pm install /data/local/tmp/app.apk\'
I have tried to replicate this behaviour by doing: (on pc, using usb-cable)
adb push app.apk /sdcard/app.apk
adb shell
$ pm install /sdcard/app.apk
This works. The app is installed.
I made an application (named AppInstall) which should install the other app.
(installed normally, non-rooted device)
It does:
Runtime.getRuntime().exec(\"pm install /sdcard/app.apk\").waitFor();
But this gives the error:
java.lang.SecurityException: Neither user 10019 nor current process has android.permission.INSTALL_PACKAGES.
It seems like the error is thrown by pm, not by AppInstall.
Because the SecurityException is not catched by AppInstall and the app does not crash.
I\'ve tried the same thing on a rooted device (same app and AppInstall) and it worked like a charm.
(Also normally installed, not in /system or anything)
AppInstall didn\'t even ask root-permission.
But thats because the shell is always #
instead of $
on that device.
Btw, you need root to install an app in /system, correct?
I tried adb remount on the non-rooted device and got:
remount failed: Operation not permitted.
That\'s why I could not try the /system thing on the non-rooted device.
Conclusion: you should use a rooted device
Hope this helps :)
回答3:
You should define
<uses-permission
android:name=\"android.permission.INSTALL_PACKAGES\" />
in your manifest, then if whether you are in system partition (/system/app) or you have your application signed by the manufacturer, you are going to have INSTALL_PACKAGES permission.
My suggestion is to create a little android project with 1.5 compatibility level used to call installPackages via reflection and to export a jar with methods to install packages and to call the real methods.
Then, by importing the jar in your project you will be ready to install packages.
回答4:
I have been implementing installation without user consent recently - it was a kiosk application for API level 21+ where I had full control over environment.
The basic requirements are
- API level 21+
- root access to install the updater as a system privileged app.
The following method reads and installs APK from InputStream:
public static boolean installPackage(Context context, InputStream in, String packageName)
throws IOException {
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
params.setAppPackageName(packageName);
// set params
int sessionId = packageInstaller.createSession(params);
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
OutputStream out = session.openWrite(\"COSU\", 0, -1);
byte[] buffer = new byte[65536];
int c;
while ((c = in.read(buffer)) != -1) {
out.write(buffer, 0, c);
}
session.fsync(out);
in.close();
out.close();
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra(\"info\", \"somedata\"); // for extra data if needed..
Random generator = new Random();
PendingIntent i = PendingIntent.getActivity(context, generator.nextInt(), intent,PendingIntent.FLAG_UPDATE_CURRENT);
session.commit(i.getIntentSender());
return true;
}
The following code calls the installation
try {
InputStream is = getResources().openRawResource(R.raw.someapk_source);
installPackage(MainActivity.this, is, \"com.example.apk\");
} catch (IOException e) {
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
for the whole thing to work you desperately need INSTALL_PACKAGES
permission, or the code above will fail silently
<uses-permission
android:name=\"android.permission.INSTALL_PACKAGES\" />
to get this permission you must install your APK as System application which REQUIRES root (however AFTER you have installed your updater application it seem to work WITHOUT root)
To install as system application I created a signed APK and pushed it with
adb push updater.apk /sdcard/updater.apk
and then moved it to system/priv-app
- which requires remounting FS (this is why the root is required)
adb shell
su
mount -o rw,remount /system
mv /sdcard/updater.apk /system/priv-app
chmod 644 /system/priv-app/updater.apk
for some reason it didn\'t work with simple debug version, but logcat shows useful info if your application in priv-app
is not picked up for some reason.
回答5:
I tried on rooted Android 4.2.2 and this method works for me:
private void installApk(String filename) {
File file = new File(filename);
if(file.exists()){
try {
final String command = \"pm install -r \" + file.getAbsolutePath();
Process proc = Runtime.getRuntime().exec(new String[] { \"su\", \"-c\", command });
proc.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
}
回答6:
I had no idea of how to do this, because nobody answered that time, and I found no documentation about this permission. So I found my own solution. It is worser that yours, but this is a solution anyway.
I installed busybox, that set 777 permission to /data/app (I dont care about security). Then just executed \"busybox install\" from app. This works, but has a big security leak. If you set permissions 777, no root required.
回答7:
You can use the hidden API android.content.pm.IPackageInstallObserver
by reflection:
public class PackageManagement {
public static final int INSTALL_REPLACE_EXISTING = 0x00000002;
public static final int INSTALL_SUCCEEDED = 1;
private static Method installPackageMethod;
private static Method deletePackageMethod;
static {
try {
installPackageMethod = PackageManager.class.getMethod(\"installPackage\", Uri.class, IPackageInstallObserver.class, Integer.TYPE, String.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
public static void installPackage(PackageManager pm, Uri mPackageUri, IPackageInstallObserver observer, int installFlags, String installerPackageName) {
try {
installPackageMethod.invoke(pm, mPackageUri, observer, installFlags, installerPackageName);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Import android.content.pm.IPackageInstallObserver
into your project. Your app must be system. You must activate the permission android.permission.INSTALL_PACKAGES
in your manifest file.
回答8:
You can simply use adb install command to install/update APK silently. Sample code is below
public static void InstallAPK(String filename){
File file = new File(filename);
if(file.exists()){
try {
String command;
filename = StringUtil.insertEscape(filename);
command = \"adb install -r \" + filename;
Process proc = Runtime.getRuntime().exec(new String[] { \"su\", \"-c\", command });
proc.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
}
回答9:
I made a test app for silent installs, using PackageManager.installPackage method.
I get installPackage method through reflection, and made android.content.pm.IPackageInstallObserver interface in my src folder (because it\'s hidden in android.content.pm package).
When i run installPackage, i got SecurityException with string indication, that my app has no android.permission.INSTALL_PACKAGES, but it defined in AndroidManifest.xml.
So, i think, it\'s not possible to use this method.
PS. I tested in on Android SDK 2.3 and 4.0. Maybe it will work with earlier versions.
回答10:
Try this LD_LIBRARY_PATH=/vendor/lib:/system/lib
before pm install. It works well.
回答11:
An 3rd party application cannot install an Android App sliently.
However, a 3rd party application can ask the Android OS to install a application.
So you should define this:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(\"file:///sdcard/app.apk\", \"application/vnd.android.package-archive\");
startActivity(intent);
You can also try to install it as a system app to grant the permission and ignore this define. (Root Required)
You can run the following command on your 3rd party app to install an app on the rooted device.
The code is:
private void installApk(String filename) {
File file = new File(filename);
if(file.exists()){
try {
final String command = \"pm install -r \" + file.getAbsolutePath();
Process proc = Runtime.getRuntime().exec(new String[] { \"su\", \"-c\", command });
proc.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
}
I hope that this answer is helpful for you.
回答12:
Prerequisite:
Your APK needs to be signed by system as correctly pointed out earlier. One way to achieve that is building the AOSP image yourself and adding the source code into the build.
Code:
Once installed as a system app, you can use the package manager methods to install and uninstall an APK as following:
Install:
public boolean install(final String apkPath, final Context context) {
Log.d(TAG, \"Installing apk at \" + apkPath);
try {
final Uri apkUri = Uri.fromFile(new File(apkPath));
final String installerPackageName = \"MyInstaller\";
context.getPackageManager().installPackage(apkUri, installObserver, PackageManager.INSTALL_REPLACE_EXISTING, installerPackageName);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
Uninstall:
public boolean uninstall(final String packageName, final Context context) {
Log.d(TAG, \"Uninstalling package \" + packageName);
try {
context.getPackageManager().deletePackage(packageName, deleteObserver, PackageManager.DELETE_ALL_USERS);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
To have a callback once your APK is installed/uninstalled you can use this:
/**
* Callback after a package was installed be it success or failure.
*/
private class InstallObserver implements IPackageInstallObserver {
@Override
public void packageInstalled(String packageName, int returnCode) throws RemoteException {
if (packageName != null) {
Log.d(TAG, \"Successfully installed package \" + packageName);
callback.onAppInstalled(true, packageName);
} else {
Log.e(TAG, \"Failed to install package.\");
callback.onAppInstalled(false, null);
}
}
@Override
public IBinder asBinder() {
return null;
}
}
/**
* Callback after a package was deleted be it success or failure.
*/
private class DeleteObserver implements IPackageDeleteObserver {
@Override
public void packageDeleted(String packageName, int returnCode) throws RemoteException {
if (packageName != null) {
Log.d(TAG, \"Successfully uninstalled package \" + packageName);
callback.onAppUninstalled(true, packageName);
} else {
Log.e(TAG, \"Failed to uninstall package.\");
callback.onAppUninstalled(false, null);
}
}
@Override
public IBinder asBinder() {
return null;
}
}
/**
* Callback to give the flow back to the calling class.
*/
public interface InstallerCallback {
void onAppInstalled(final boolean success, final String packageName);
void onAppUninstalled(final boolean success, final String packageName);
}
===> Tested on Android 8.1 and worked well.
回答13:
!/bin/bash
f=/home/cox/myapp.apk #or $1 if input from terminal.
#backup env var
backup=$LD_LIBRARY_PATH
LD_LIBRARY_PATH=/vendor/lib:/system/lib
myTemp=/sdcard/temp.apk
adb push $f $myTemp
adb shell pm install -r $myTemp
#restore env var
LD_LIBRARY_PATH=$backup
This works for me.
I run this on ubuntu 12.04, on shell terminal.