Android PKMS拦截adb安装应用

我们再PKMS汇总拦截adb 安装的应用,在分析PKMS的时候我们也知道,在installPackageAsUser有如下代码,代表是adb安装的。

        if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
            installFlags |= PackageManager.INSTALL_FROM_ADB;

        }

所以我们可以在startCopy函数中做手脚

        final boolean startCopy() {
            boolean res;
            try {
                if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);

                if (++mRetries > MAX_RETRIES) {//超过4次
                    Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
                    mHandler.sendEmptyMessage(MCS_GIVE_UP);
                    handleServiceError();
                    return false;
                } else {
                    handleStartCopy();
                    res = true;
                }
            } catch (RemoteException e) {
                if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
                mHandler.sendEmptyMessage(MCS_RECONNECT);
                res = false;
            }

            //add
            if (mApkPath == null) {
                handleReturnCode();
                return res;
            }
	    //是adb 安装的
            if ((((InstallParams)this).installFlags & PackageManager.INSTALL_FROM_ADB) != 0) {
                mLock = new ReentrantLock();
                mCondition = mLock.newCondition();

                class AlertDialogThread extends Thread {
                    public Handler mHandler;

                    public void run() {
                        Looper.prepare();
                        showAlertDialog();

                        Looper.loop();
                    }
                }
                AlertDialogThread alertDialogThread = new AlertDialogThread();
                alertDialogThread.start();

                mLock.lock();
                try {
                    mCondition.await();
                } catch (InterruptedException e) {
                }
                mLock.unlock();

                /*if (!mContinueToInstall) {
                    res = false;
                }*/
            }
            handleReturnCode();
            return res;
        }

上面代码就是增加了一个显示的dialog,点击继续才会继续安装,这里我们有一个当点击取消或者5秒没有点击就不继续安装,这里返回值res不能为false。为什么呢?我们来看下调用startCopy的地方,当startCopy返回true才会把mPendingInstalls中的第一项删除,否则就会不断调用startCopy直到超过4次才会删除。所以我们上面不继续安装的话也不能返回一个false。

                   else if (mPendingInstalls.size() > 0) {
                        HandlerParams params = mPendingInstalls.get(0);
                        if (params != null) {
                            if (params.startCopy()) {
                                // We are done...  look for more work or to
                                // go idle.
                                if (DEBUG_SD_INSTALL) Log.i(TAG,
                                        "Checking for more work or unbind...");
                                // Delete pending install
                                if (mPendingInstalls.size() > 0) {
                                    mPendingInstalls.remove(0);
                                }
                                if (mPendingInstalls.size() == 0) {
                                    if (mBound) {
                                        if (DEBUG_SD_INSTALL) Log.i(TAG,
                                                "Posting delayed MCS_UNBIND");
                                        removeMessages(MCS_UNBIND);
                                        Message ubmsg = obtainMessage(MCS_UNBIND);
                                        // Unbind after a little delay, to avoid
                                        // continual thrashing.
                                        sendMessageDelayed(ubmsg, 10000);
                                    }
                                } else {
                                    // There are more pending requests in queue.
                                    // Just post MCS_BOUND message to trigger processing
                                    // of next pending install.
                                    if (DEBUG_SD_INSTALL) Log.i(TAG,
                                            "Posting MCS_BOUND for next work");
                                    mHandler.sendEmptyMessage(MCS_BOUND);
                                }
                            }
                        }

再看看在HandlerParams 中增加的代码,大部分是显示dialog相关。

    private abstract class HandlerParams {
        private static final int MAX_RETRIES = 4;

        /**
         * Number of times startCopy() has been attempted and had a non-fatal
         * error.
         */
        private int mRetries = 0;

        /** User handle for the user requesting the information or installation. */
        private final UserHandle mUser;

        HandlerParams(UserHandle user) {
            mUser = user;
            mApkPath = null;//add
        }

        UserHandle getUser() {
            return mUser;
        }

        private Builder mAlertDialogBuilder;
        private AlertDialog mAlertDialog;
        private CountDownTimer mCountDownTimer;
        protected String mApkPath;
        protected boolean mContinueToInstall = true;//注意这个很重要

        private void cancelAlertDialog(Dialog dialog) {
            mLock.lock();

            if (mHandler.mPendingInstalls.size() != 0) {
                InstallParams params = (InstallParams)mHandler.mPendingInstalls.get(0);
                try {
                    if (params.observer != null) {
                        params.observer.onPackageInstalled("", INSTALL_FAILED_USER_CANCELLED, null, null);
                    }
                } catch (RemoteException re) {
                }
            }

            mHandler.mPendingInstalls.clear();
            mHandler.disconnectService();
            mContinueToInstall = false;//没有点击继续安装

            mCondition.signal();
            mLock.unlock();
            dialog.dismiss();
        }

        private Drawable getApkIcon(Context context, String apkPath) {
            PackageManager pm = context.getPackageManager();
            PackageInfo info = pm.getPackageArchiveInfo(apkPath,
                PackageManager.GET_ACTIVITIES);
            if (info != null) {
                ApplicationInfo appInfo = info.applicationInfo;
                appInfo.sourceDir = apkPath;
                appInfo.publicSourceDir = apkPath;
                try {
                    return appInfo.loadIcon(pm);
                } catch (OutOfMemoryError e) {
                    Log.e("getApkIcon", e.toString());
                }
            }

            return null;
        }

        private CharSequence getAppLabel(Context context, String apkPath) {
            PackageManager pm = context.getPackageManager();
            PackageInfo info = pm.getPackageArchiveInfo(apkPath,
                PackageManager.GET_ACTIVITIES);
            if (info != null) {
                ApplicationInfo appInfo = info.applicationInfo;
                appInfo.sourceDir = apkPath;
                appInfo.publicSourceDir = apkPath;
                return appInfo.loadLabel(pm);
            }

            return null;
        }

        private void showAlertDialog() {
            final Context settingsContext = new ContextThemeWrapper(mContext,
                com.android.internal.R.style.Theme_DeviceDefault_Settings);
            mAlertDialogBuilder = new AlertDialog.Builder(settingsContext);
            mAlertDialogBuilder.setTitle(getAppLabel(mContext, mApkPath));
            mAlertDialogBuilder.setMessage(R.string.install_hint);
            mAlertDialogBuilder.setIcon(getApkIcon(mContext, mApkPath));
            mAlertDialogBuilder.setNegativeButton(R.string.adb_install_cancel,
                new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    mCountDownTimer.cancel();
                    cancelAlertDialog(mAlertDialog);
                }
            });
            mAlertDialogBuilder.setPositiveButton(R.string.adb_install_continue_to_install,
                new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    mCountDownTimer.cancel();

                    mLock.lock();
                    mContinueToInstall = true;//点击继续安装
                    mCondition.signal();
                    mLock.unlock();

                    mAlertDialog.dismiss();
                }
            });

            mAlertDialog = mAlertDialogBuilder.create();
            Window alertDialogWindow = mAlertDialog.getWindow();
            alertDialogWindow.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);// TYPE_SYSTEM_DIALOG

            mAlertDialog.setCanceledOnTouchOutside(false);
            mAlertDialog.show();
            alertDialogWindow.setGravity(Gravity.BOTTOM);

            final Button negativeButton = mAlertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
            int intervalMs = 1000;
            int timeLeftMs = 5000;
            final String cancel = mContext.getString(R.string.adb_install_cancel);
            mCountDownTimer = new CountDownTimer(timeLeftMs, intervalMs) {
                public void onTick(long millisUntilFinished) {
                    negativeButton.setText(cancel + "(" + millisUntilFinished / 1000 + ")");
                }

                public void onFinish() {
                    cancelAlertDialog(mAlertDialog);
                }
            };
            mCountDownTimer.start();
        }

其中有一个mApkPath我们接下来看在哪赋值。

就是handleStartCopy函数最后在copyApk函数(就是在copy apk到data/app目录下新建的临时目录)之后,mApkPath就是其目录下的base.apk

                    ret = args.copyApk(mContainerService, true);
                    mApkPath = args.getCodePath() + "/base.apk";

我们再看下有了这个apkPath就可以调用PackageManager的getPackageArchiveInfo来获取PackageInfo,然后解析出ICon和Label等。

PackageInfo info = pm.getPackageArchiveInfo(apkPath,
                PackageManager.GET_ACTIVITIES);

我们来看看这个函数,也是调用PackageParser来解析apk。

    public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) {
        final PackageParser parser = new PackageParser();
        final File apkFile = new File(archiveFilePath);
        try {
            PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0);
            if ((flags & GET_SIGNATURES) != 0) {
                parser.collectCertificates(pkg, 0);
                parser.collectManifestDigest(pkg);
            }
            PackageUserState state = new PackageUserState();
            return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
        } catch (PackageParserException e) {
            return null;
        }
    }


我们继续分析handleReturnCode函数,当mContinueToInstall为true就调用processPendingInstall函数继续装载应用,当为false,就调用doPreInstall。联系到之前mContinueToInstall默认为true,因为不是adb安装的话mContinueToInstall就应该默认为true,要不然不能继续装载应用了。

        @Override
        void handleReturnCode() {
            // If mArgs is null, then MCS couldn't be reached. When it
            // reconnects, it will try again to install. At that point, this
            // will succeed.
            if (mArgs != null) {
                if (mContinueToInstall) {
                    processPendingInstall(mArgs, mRet);
                } else {
                    mArgs.doPreInstall(PackageManager.INSTALL_FAILED_MISSING_FEATURE);
                }
                
            }
        }

我们再来看doPreInstall函数,当状态不是success,就删除一些临时文件。

        int doPreInstall(int status) {
            if (status != PackageManager.INSTALL_SUCCEEDED) {
                cleanUp();
            }
            return status;
        }





你可能感兴趣的:(Android PKMS拦截adb安装应用)