我试图默默地将apk安装到系统中。 我的应用程序位于/ system / app并成功授予权限“android.permission.INSTALL_PACKAGES”
但是我找不到任何地方如何使用此权限。我试图将文件复制到/ data / app并没有成功。我也尝试使用此代码
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(
Uri.parse("file:///sdcard/app.apk"),
"application/vnd.android.package-archive");
startActivity(intent);
但是此代码会打开标准安装对话框。如何在没有root的情况下以授权android.permission.INSTALL_PACKAGES
静默安装应用程序?
PS我正在编写一个应用程序,它将在第一次启动时从文件夹中将许多apks安装到系统中(替换安装向导)。我需要它来使固件更轻。
如果您认为我正在编写病毒:所有程序都安装在/ data / app中。权限Install_packages只能授予位于/ system / app中的系统级程序或使用系统密钥签名。所以病毒无法到达那里。
如果http://www.mail-archive.com/android-porting@googlegroups.com/msg06281.html个应用程序具有install_packages权限,则可以进行静默安装。此外,您不需要Install_packages权限来安装软件包。加http://www.androidzoom.com/android_applications/tools/silent-installer_wgqi.html
答案 0 :(得分:59)
您的第一个赌注是调查Android的原生PackageInstaller。我建议您按照自己喜欢的方式修改该应用,或者只是提取所需的功能。
具体来说,如果您查看PackageInstallerActivity及其方法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();
}
}
然后您会注意到实际的安装程序位于InstallAppProgress级。检查该类,您会发现initView
是核心安装程序函数,它最后会调用PackageManager
的{{1}}函数:
installPackage
下一步是检查PackageManager,它是抽象类。你会在那里找到public void initView() {
...
pm.installPackage(mPackageURI, observer, installFlags, installerPackageName);
}
功能。坏消息是它标有@hide。这意味着它不能直接使用(您将无法通过调用此方法进行编译)。
installPackage(...)
但您可以通过反射访问此方法。
如果您对 /**
* @hide
* ....
*/
public abstract void installPackage(Uri packageURI,
IPackageInstallObserver observer,
int flags,String installerPackageName);
PackageManager
函数的实施方式感兴趣,请查看PackageManagerService。
您需要通过installPackage
的{{1}}获取包管理器对象。然后,您将通过反射调用Context
函数。
答案 1 :(得分:12)
我已经检查过ADB如何安装应用程序
- 它将APK复制到/ data / local / tmp
- 运行&shell; pm install /data/local/tmp/app.apk'
我试图通过以下方式复制此行为:(在PC上,使用usb-cable)
adb push app.apk /sdcard/app.apk
adb shell
$ pm install /sdcard/app.apk
这有效。该应用已安装。
我创建了一个应用程序(名为AppInstall),它应该安装另一个应用程序
(正常安装,非根设备)
它确实:
Runtime.getRuntime().exec("pm install /sdcard/app.apk").waitFor();
但是这给出了错误:
java.lang.SecurityException: Neither user 10019 nor current process has android.permission.INSTALL_PACKAGES.
似乎错误是由pm而不是AppInstall抛出的
因为AppInstall没有捕获SecurityException,并且应用程序不会崩溃。
我在一个有根设备(同样的应用程序和AppInstall)上尝试过相同的东西,它就像一个魅力。
(也通常安装,不在/系统或任何东西)
AppInstall甚至没有要求root权限。
但那是因为shell在该设备上总是#
而不是$
。
顺便说一下,你需要root才能在/ system中安装应用程序,对吗?
我在非root设备上尝试了adb remount并得到了:
remount failed: Operation not permitted.
这就是为什么我无法在非root设备上尝试/ system的原因。
结论:您应该使用root设备
希望这会有所帮助:)
答案 2 :(得分:8)
你应该定义
<uses-permission
android:name="android.permission.INSTALL_PACKAGES" />
在您的清单中,如果您是在系统分区(/ system / app)中还是由制造商签署了您的应用程序,您将获得INSTALL_PACKAGES权限。
我的建议是创建一个具有1.5兼容级别的小型android项目,用于通过反射调用installPackages并导出jar,其中包含安装包和调用实际方法的方法。 然后,通过在项目中导入jar,您就可以安装软件包了。
答案 3 :(得分:8)
我最近在没有用户同意的情况下实施了安装 - 它是API级别21+的自助服务终端应用程序,我可以完全控制环境。
基本要求是
以下方法从InputStream读取并安装APK:
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;
}
以下代码调用安装
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();
}
要完成所有工作,您迫切需要INSTALL_PACKAGES
权限,否则上述代码将无声地失败
<uses-permission
android:name="android.permission.INSTALL_PACKAGES" />
要获得此权限,您必须将您的APK安装为系统应用程序,该应用程序需要root (但是,在安装了更新程序应用程序后,它似乎无法正常工作)
要作为系统应用程序安装,我创建了一个已签名的APK并用
推送它adb push updater.apk /sdcard/updater.apk
然后将其移至system/priv-app
- 这需要重新安装FS(这就是根本需要的原因)
adb shell
su
mount -o rw,remount /system
mv /sdcard/updater.apk /system/priv-app
chmod 644 /system/priv-app/updater.apk
由于某些原因它不适用于简单的调试版本,但如果priv-app
中的应用程序由于某种原因没有被选中, logcat 会显示有用的信息。
答案 4 :(得分:5)
我试过扎根Android 4.2.2,这个方法对我有用:
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();
}
}
}
答案 5 :(得分:4)
我不知道如何做到这一点,因为没有人回答那个时间,我没有找到关于此权限的文档。所以我找到了自己的解决方案。你的情况比较糟糕,但无论如何这都是一个解决方案。
我安装了busybox,将777权限设置为/ data / app(我不关心安全性)。然后从app执行“busybox install”。这有效,但有很大的安全漏洞。如果设置权限777,则不需要root。
答案 6 :(得分:4)
您可以通过反射使用隐藏的API android.content.pm.IPackageInstallObserver
:
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();
}
}
}
将android.content.pm.IPackageInstallObserver
导入您的项目。您的应用必须是系统。您必须在清单文件中激活权限android.permission.INSTALL_PACKAGES
。
答案 7 :(得分:3)
您可以使用adb install命令以静默方式安装/更新APK。示例代码位于
之下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();
}
}
}
答案 8 :(得分:0)
正如@inazaruk 在回答中提到的,installPackage
方法处于隐藏状态,您需要通过反射来调用它。但是,IPackageInstallObserver
回调也是隐藏的,它作为参数传递给 installPackage
,因此您需要使用动态代理才能实现此接口。您可以在下面找到一个包含反射和代理的代码片段:
private void silentAppInstall(Uri apkUri){
PackageManager pm = getContext().getPackageManager();
try {
Class<?> cPackageInstallObserver = Class.forName("android.content.pm.IPackageInstallObserver");
Object installObserver = Proxy.newProxyInstance(cPackageInstallObserver.getClassLoader(),
new Class[]{cPackageInstallObserver}, new InstallObserverHandler());
Class<?>[] types = new Class[] {Uri.class, cPackageInstallObserver, int.class, String.class};
Method method = pm.getClass().getMethod("installPackage", types);
method.invoke(pm, apkUri, installObserver, 2, null);
} catch (NoSuchMethodException | ClassNotFoundException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
static class InstallObserverHandler implements InvocationHandler {
@Override
public Object invoke(Object o, Method method, Object[] args) throws Throwable {
if (method.getName().equals("packageInstalled")){
// Place you code here
}
return null;
}
}
答案 9 :(得分:0)
答案 10 :(得分:0)
我检查了所有答案,结论似乎是您必须首先具有对该设备的root访问权才能使它工作。
但是后来我发现这些文章非常有用。由于我正在制造“公司所有”的设备。
How to Update Android App Silently Without User Interaction
Android Device Owner - Minimal App
这是google上有关“托管设备”的文档
答案 11 :(得分:0)
可以在Android 6及更高版本上进行静默安装。使用Boris Treukhov答案中提供的功能,忽略帖子中的所有其他内容,也不需要root。
以设备管理员身份安装您的应用,您可以使用完整的信息亭模式,并在后台静默安装更新。
答案 12 :(得分:0)
先决条件:
您的APK需要由系统正确签名,如之前正确指出的那样。实现此目的的一种方法是自己构建AOSP映像,并将源代码添加到构建中。
代码:
作为系统应用程序安装后,您可以使用程序包管理器方法来安装和卸载APK,如下所示:
安装:
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;
}
}
卸载:
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;
}
}
要在安装/卸载APK后进行回调,可以使用以下方法:
/**
* 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);
}
===>在Android 8.1上进行了测试,效果很好。
答案 13 :(得分:0)
第三方应用程序无法轻松安装Android应用程序。 但是,第三方应用程序可以要求Android操作系统安装应用程序。
所以你应该定义:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file:///sdcard/app.apk", "application/vnd.android.package-archive");
startActivity(intent);
您还可以尝试将其安装为系统应用程序以授予权限并忽略此定义。 (需要根)
您可以在第三方应用上运行以下命令,在root设备上安装应用。
代码是:
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();
}
}
}
我希望这个答案对你有所帮助。
答案 14 :(得分:0)
在安装pm之前尝试此LD_LIBRARY_PATH=/vendor/lib:/system/lib
。效果很好。
答案 15 :(得分:0)
我使用PackageManager.installPackage方法为静默安装创建了一个测试应用程序。
我通过反射得到installPackage方法,并在我的src文件夹中创建了android.content.pm.IPackageInstallObserver接口(因为它隐藏在android.content.pm包中)。
当我运行installPackage时,我得到带有字符串指示的SecurityException,我的应用程序没有android.permission.INSTALL_PACKAGES,但它在AndroidManifest.xml中定义。
所以,我认为,不可能使用这种方法。
PS。我在Android SDK 2.3和4.0上测试过。也许它适用于早期版本。
答案 16 :(得分:-6)
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
这对我有用。 我在shell终端上的ubuntu 12.04上运行它。