美文网首页Android
Android三方应用实现静默安装

Android三方应用实现静默安装

作者: 最忆是深秋 | 来源:发表于2018-07-19 15:30 被阅读992次

    背景

      一个三方应用想要内置进我们 ROM,并且想要能够静默安装应用。

    App的静默安装和卸载

       Android系统本身提供了安装卸载功能,但是api接口是@hide的,不是公开的接口,所以在应用级别
    是无法实现静默安装和卸载的,要实现静默安装和卸载需要是系统应用,要有系统签名和相应的权限。

    思路1

    1. 通过反射获得安装接口installPackage和 卸载接口 deletePackage
    2. 在自己的包中引入两个接口IPackageInstallObserver和IPackageDeleteObserver的空实现
    3. 调用安装卸载的方法,回调上面的两个接口
    4. 添加权限 <uses-permission android:name="android.permission.DELETE_PACKAGES"/>
      <uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
      进行系统签名
    5. 将应用push到系统中,作为系统应用

    在PackageManager中的提供的接口如下:

        /**
         * @deprecated replaced by {@link PackageInstaller}
         * @hide
         */
        @Deprecated
        public abstract void installPackage(
                Uri packageURI,
                PackageInstallObserver observer,
                @InstallFlags int flags,
                String installerPackageName);
    
        /**
         * Attempts to delete a package. Since this may take a little while, the
         * result will be posted back to the given observer. A deletion will fail if
         * the calling context lacks the
         * {@link android.Manifest.permission#DELETE_PACKAGES} permission, if the
         * named package cannot be found, or if the named package is a system
         * package.
         *
         * @param packageName The name of the package to delete
         * @param observer An observer callback to get notified when the package
         *            deletion is complete.
         *            {@link android.content.pm.IPackageDeleteObserver#packageDeleted}
         *            will be called when that happens. observer may be null to
         *            indicate that no callback is desired.
         * @hide
         */
        @RequiresPermission(Manifest.permission.DELETE_PACKAGES)
        public abstract void deletePackage(String packageName, IPackageDeleteObserver observer,
                @DeleteFlags int flags);
    

    引入两个回掉的空实现

    在自己应用的工程中新建一个包android.content.pm,并添加两个文件

    • IPackageDeleteObserver.java
    package android.content.pm;
    public interface IPackageDeleteObserver extends android.os.IInterface {
        public abstract static class Stub extends android.os.Binder implements android.content.pm.IPackageDeleteObserver {
            public Stub() {
                throw new RuntimeException("Stub!");
            }
    
            public static android.content.pm.IPackageDeleteObserver asInterface(android.os.IBinder obj) {
                throw new RuntimeException("Stub!");
            }
    
            public android.os.IBinder asBinder() {
                throw new RuntimeException("Stub!");
            }
    
            public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
                    throws android.os.RemoteException {
                throw new RuntimeException("Stub!");
            }
        }
    
        public abstract void packageDeleted(java.lang.String packageName, int returnCode)
                throws android.os.RemoteException;
    }
    
    • IPackageInstallObserver.java
    package android.content.pm;
    public interface IPackageInstallObserver extends android.os.IInterface {
    
        public abstract static class Stub extends android.os.Binder implements android.content.pm.IPackageInstallObserver {
            public Stub() {
                throw new RuntimeException("Stub!");
            }
    
            public static android.content.pm.IPackageInstallObserver asInterface(android.os.IBinder obj) {
                throw new RuntimeException("Stub!");
            }
    
            public android.os.IBinder asBinder() {
                throw new RuntimeException("Stub!");
            }
    
            public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
                    throws android.os.RemoteException {
                throw new RuntimeException("Stub!");
            }
        }
    
        public abstract void packageInstalled(java.lang.String packageName, int returnCode)
                throws android.os.RemoteException;
    }
    

    自定义接口回调

    • OnPackagedObserver.java
    public interface OnPackagedObserver {
        public void packageInstalled(String packageName, int returnCode);
        public void packageDeleted(String packageName,int returnCode);
    }
    

    代码调用

    • 反射调用
        public static final int INSTALL_REPLACE_EXISTING = 0x00000002; //如果已经存在的包使用这个flag
        public static final int INSTALL_ALL_USERS = 0x00000040;
                    
        PackageManager pm = getPackageManager();
        Class<?>[] types = new Class[] {Uri.class, IPackageInstallObserver.class, int.class, String.class};
        try {
             Method method = pm.getClass().getMethod("installPackage", types);
             method.invoke(pm,Uri.fromFile(file), new PackageInstallObserver(), INSTALL_ALL_USERS, null);
         }catch (Exception e){
              e.printStackTrace();
         }
    
        Class<?>[] uninstalltypes = new Class[] {String.class, IPackageDeleteObserver.class, int.class};
        try {
             Method uninstallmethod = pm.getClass().getMethod("deletePackage", uninstalltypes);
             uninstallmethod.invoke(pm, "your packagename", new PackageDeleteObserver(), 0);
        }catch (Exception e){
              e.printStackTrace();
        }
    
    • 接口实现
        private OnPackagedObserver onInstallOrDeleteObserver = new OnPackagedObserver() {
            @Override
            public void onPackageInstalled(String packageName, int returnCode) {
                Log.d("test","onPackageInstalled");
            }
    
            @Override
            public void onPackageDeleted(String packageName, int returnCode) {
                Log.d("test","onPackageDeleted");
            }
        };
    
        class PackageInstallObserver extends IPackageInstallObserver.Stub {
    
            public void packageInstalled(String packageName, int returnCode) throws RemoteException {
                if (onInstallOrDeleteObserver != null) {
                    onInstallOrDeleteObserver.onPackageInstalled(packageName, returnCode);
                }
            }
        }
    
        class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
    
            public void packageDeleted(String packageName, int returnCode) throws RemoteException {
                if (onInstallOrDeleteObserver != null) {
                    onInstallOrDeleteObserver.onPackageDeleted(packageName, returnCode);
                }
            }
        }
    

    签名

    生成一个apk文件,需要对这个apk文件进行系统签名,由于<uses-permission android:name="android.permission.DELETE_PACKAGES"/>
    <uses-permission android:name="android.permission.INSTALL_PACKAGES"/> 是系统应用需要的权限,在开发应用时,如果加在AndroidManifest.xml
    中会编译不过,需要先用工具 apktool 工具先把apk文件解压出来,用编辑器在AndroidManifest.xml中加入上面的两个权限,然后在用工具 apktool重新打包

    • 反编译(解压)
    apktool  d -f test.apk
    
    • 修改 AndroidManifest.xml 加入权限声明之后重新打包
    apktool  b  test.apk
    

    重新打包的时候很有可能会报错,仔细看下报错的位置,在解压后的文件中找到处理掉,该删的删。这一步成功后我们得到了一个 AndroidManifest中声明了权限的 apk, 这个时候这个apk是没有进行签名的,安装不了。需要再进行系统签名:

    java -jar signapk.jar platform.x509.pem platform.pk8 unsign.apk signed.apk
    

    signapk.jar 位于 out/host/linux-86/framework/signapk.jar
    platform.x509.pem platform.pk8 位于 build/target/product/security/platform.x509.pem, platform.pk8
    最后生成的apk就是已经进行系统签名的apk

    思路1 参考博客 https://www.jianshu.com/p/8c7da720062d

    按照思路1的做法,我只做到了重新打包,生成了添加了权限后的apk文件,但是再使用上述方法给apk进行签名的时候始终报错,最后是采用mk编译的方法签名成功的。

    思路2

       其实归根结底,还是三方应用无法拿到 INSTALL_PACKAGES 和DELETE_PACKAGES权限,在AndroidManifest文件中声明的时候会编译不过。那么就可以在framework中权限检测的地方对这个特定包名赋予权限。这样即使没有在AndroidManifest中声明,也可以拿到权限。这样的话,思路1的4,5步操作即可省略。

    07-18 11:48:26.705: W/System.err(12203): Caused by: java.lang.SecurityException: Neither user 10124 nor current process has android.permission.INSTALL_PACKAGES.
    07-18 11:48:26.705: W/System.err(12203):    at android.os.Parcel.readException(Parcel.java:2004)
    07-18 11:48:26.705: W/System.err(12203):    at android.os.Parcel.readException(Parcel.java:1950)
    07-18 11:48:26.705: W/System.err(12203):    at android.content.pm.IPackageManager$Stub$Proxy.installPackageAsUser(IPackageManager.java:4092)
    07-18 11:48:26.705: W/System.err(12203):    at android.app.ApplicationPackageManager.installCommon(ApplicationPackageManager.java:1828)
    07-18 11:48:26.705: W/System.err(12203):    at android.app.ApplicationPackageManager.installPackage(ApplicationPackageManager.java:1809)
    07-18 11:48:26.705: W/System.err(12203):    ... 11 more
    

    根据异常的 log跟一下 framework 的代码,最终在AMS的checkComponentPermission方法中来做:

        int checkComponentPermission(String permission, int pid, int uid,
                int owningUid, boolean exported) {
            if (pid == MY_PID) {
                return PackageManager.PERMISSION_GRANTED;
            }
           .............
            if ("android.permission.INSTALL_PACKAGES".equals(permission)) {
                try {
                    String callingPkgName = AppGlobals.getPackageManager().getNameForUid(uid);
                    if ("your  packagename".equals(callingPkgName)) {
                        return PackageManager.PERMISSION_GRANTED;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.w(TAG, "install packages getNameForUid exception");
                }
    
            }
          ..............
          ..............
        }
    

    这样就好啦,省去了 反编译重新打包 以及 重新签名 的过程。

    附录

    • Android 8.1 Delete Package返回码对照表
        public static final int DELETE_SUCCEEDED = 1;
        public static final int DELETE_FAILED_INTERNAL_ERROR = -1;
        public static final int DELETE_FAILED_DEVICE_POLICY_MANAGER = -2;
        public static final int DELETE_FAILED_USER_RESTRICTED = -3;
        public static final int DELETE_FAILED_OWNER_BLOCKED = -4;
        public static final int DELETE_FAILED_ABORTED = -5;
        public static final int DELETE_FAILED_USED_SHARED_LIBRARY = -6;
    
    • Android 8.1 Install Package返回码对照表
        public static final int INSTALL_SUCCEEDED = 1;
        public static final int INSTALL_FAILED_ALREADY_EXISTS = -1;
        public static final int INSTALL_FAILED_INVALID_APK = -2;
        public static final int INSTALL_FAILED_INVALID_URI = -3;
        public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = -4;
        public static final int INSTALL_FAILED_DUPLICATE_PACKAGE = -5;
        public static final int INSTALL_FAILED_NO_SHARED_USER = -6;
        public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = -7;
        public static final int INSTALL_FAILED_SHARED_USER_INCOMPATIBLE = -8;
        public static final int INSTALL_FAILED_MISSING_SHARED_LIBRARY = -9;
        public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = -10;
        public static final int INSTALL_FAILED_DEXOPT = -11;
        public static final int INSTALL_FAILED_OLDER_SDK = -12;
        public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13;
        public static final int INSTALL_FAILED_NEWER_SDK = -14;
        public static final int INSTALL_FAILED_MISSING_FEATURE = -17;
        public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
        public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;
        public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20;
        public static final int INSTALL_FAILED_VERIFICATION_TIMEOUT = -21;
        public static final int INSTALL_FAILED_VERIFICATION_FAILURE = -22;
        public static final int INSTALL_FAILED_PACKAGE_CHANGED = -23;
        public static final int INSTALL_FAILED_UID_CHANGED = -24;
        public static final int INSTALL_FAILED_VERSION_DOWNGRADE = -25;
        public static final int INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE = -26;
        public static final int INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE = -27;
        public static final int INSTALL_PARSE_FAILED_NOT_APK = -100;
        public static final int INSTALL_PARSE_FAILED_BAD_MANIFEST = -101;
        public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102;
        public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103;
        public static final int INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES = -104;
        public static final int INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING = -105;
        public static final int INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME = -106;
        public static final int INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID = -107;
        public static final int INSTALL_PARSE_FAILED_MANIFEST_MALFORMED = -108;
        public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109;
        public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;
        public static final int INSTALL_FAILED_USER_RESTRICTED = -111;
        public static final int INSTALL_FAILED_DUPLICATE_PERMISSION = -112;
        public static final int INSTALL_FAILED_NO_MATCHING_ABIS = -113;
        public static final int NO_NATIVE_LIBRARIES = -114;
        public static final int INSTALL_FAILED_ABORTED = -115;
        public static final int INSTALL_FAILED_INSTANT_APP_INVALID = -116;
    

    小结

    据我自己实操:

    • 思路1
      在重新打包 和 系统签名 的过程中都遇到了问题,重新打包的后来把报错的地方删掉打包成功了,但是系统签名按照上面 shell 方式签名始终没有成功。虽然最后使用 mk 方式,源码环境下编译签名成功了,但是思路1 的整个过程还是比较坎坷的
    • 思路2
      比较方便,基本没走弯路,相当于是 framework 给开了口子。

    总而言之,在没有和ROM厂家合作的情况下,三方应用是不可能实现静默安装的。要么让它成为系统应用,要么让rom厂家在framework中开口子~

    相关文章

      网友评论

        本文标题:Android三方应用实现静默安装

        本文链接:https://www.haomeiwen.com/subject/awtopftx.html