首页 > 其他分享 >PMS简单学习【1.apk安装前的准备-APK检验】

PMS简单学习【1.apk安装前的准备-APK检验】

时间:2022-11-06 20:22:33浏览次数:42  
标签:PackageInstaller return APK PMS apk Intent INSTALL null final

InstallStart

InstallStart 是PackageInstaller中的入口Activity,其中PackageInstaller是系统内置的应用程序,用于安装和卸载应用。
当调用PackageInstaller来安装应用时,会跳转到InstallStart并且调用它的onCreate方法。

InstallStart的AndroidManifest.xml如下所示:

<activity android:name=".InstallStart"
                android:theme="@android:style/Theme.Translucent.NoTitleBar"
                android:exported="true"
                android:excludeFromRecents="true">
            <intent-filter android:priority="1">
                <action android:name="android.intent.action.VIEW" />
                <action android:name="android.intent.action.INSTALL_PACKAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="content" />
                <data android:mimeType="application/vnd.android.package-archive" />
            </intent-filter>
            <intent-filter android:priority="1">
                <action android:name="android.intent.action.INSTALL_PACKAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="package" />
                <data android:scheme="content" />
            </intent-filter>
            <intent-filter android:priority="1">
                <action android:name="android.content.pm.action.CONFIRM_INSTALL" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

InstallStart的onCreate方法如下所示:

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPackageManager = getPackageManager();
        mUserManager = getSystemService(UserManager.class);
        Intent intent = getIntent();
        String callingPackage = getCallingPackage();
        String callingAttributionTag = null;

        //"android.content.pm.action.CONFIRM_INSTALL"
        final boolean isSessionInstall =
                PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());

        // 如果Activity是通过PackageInstaller会话启动,就要从该会话中检索调用包
        // PackageInstaller.EXTRA_SESSION_ID : 一个操作正在使用的整数会话ID
        final int sessionId = (isSessionInstall
                ? intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1)
                : -1);
        if (callingPackage == null && sessionId != -1) {
            PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
            PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId);
            callingPackage = (sessionInfo != null) ? sessionInfo.getInstallerPackageName() : null;
            callingAttributionTag =
                    (sessionInfo != null) ? sessionInfo.getInstallerAttributionTag() : null;
        }

        final ApplicationInfo sourceInfo = getSourceInfo(callingPackage);
        final int originatingUid = getOriginatingUid(sourceInfo);
        boolean isTrustedSource = false;
        if (sourceInfo != null
                && (sourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
            isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
        }

        if (!isTrustedSource && originatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
            final int targetSdkVersion = getMaxTargetSdkVersionForUid(this, originatingUid);
            if (targetSdkVersion < 0) {
                Log.w(LOG_TAG, "Cannot get target sdk version for uid " + originatingUid);
                // Invalid originating uid supplied. Abort install.
                mAbortInstall = true;
            } else if (targetSdkVersion >= Build.VERSION_CODES.O && !isUidRequestingPermission(
                    originatingUid, Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
                Log.e(LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission "
                        + Manifest.permission.REQUEST_INSTALL_PACKAGES);
                mAbortInstall = true;
            }
        }
        if (mAbortInstall) {
            setResult(RESULT_CANCELED);
            finish();
            return;
        }

        Intent nextActivity = new Intent(intent);
        nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
                | Intent.FLAG_GRANT_READ_URI_PERMISSION);

        // 将此Activity作为源,进行启动其他的Activity。
        nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
        nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_ATTRIBUTION_TAG,
                callingAttributionTag);
        nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
        nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);

        if (isSessionInstall) {
            nextActivity.setClass(this, PackageInstallerActivity.class);
        } else {
            Uri packageUri = intent.getData();

            if (packageUri != null && packageUri.getScheme().equals(
                    ContentResolver.SCHEME_CONTENT)) {
                // content 
                // [IMPORTANT] 这一部分已经deprecated,但是仍然应该有效。
                nextActivity.setClass(this, InstallStaging.class);
            } else if (packageUri != null && packageUri.getScheme().equals(
                    PackageInstallerActivity.SCHEME_PACKAGE)) {
                // package
                nextActivity.setClass(this, PackageInstallerActivity.class);
            } else {
                Intent result = new Intent();
                result.putExtra(Intent.EXTRA_INSTALL_RESULT,
                        PackageManager.INSTALL_FAILED_INVALID_URI);
                setResult(RESULT_FIRST_USER, result);

                nextActivity = null;
            }
        }

        if (nextActivity != null) {
            startActivity(nextActivity);
        }
        finish();
    }

虽然上面说目前以content进行判断的地方已经deprecated了,但是仍然是可用的。这里简单了解一下。
InstallStaging的onResume()方法如下所示:

@Override
    protected void onResume() {
        super.onResume();

        // This is the first onResume in a single life of the activity
        if (mStagingTask == null) {
            // File does not exist, or became invalid
            if (mStagedFile == null) {
                // Create file delayed to be able to show error
                try {
                    // mStagedFile 用于存储临时数据
                    mStagedFile = TemporaryFileManager.getStagedFile(this);
                } catch (IOException e) {
                    showError();
                    return;
                }
            }

            mStagingTask = new StagingAsyncTask();
            mStagingTask.execute(getIntent().getData());
        }
    }

在来看一下 StagingAsyncTask干了啥事

private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {
        @Override
        protected Boolean doInBackground(Uri... params) {
            if (params == null || params.length <= 0) {
                return false;
            }
            Uri packageUri = params[0];
            try (InputStream in = getContentResolver().openInputStream(packageUri)) {
                // Despite the comments in ContentResolver#openInputStream the returned stream can
                // be null.
                if (in == null) {
                    return false;
                }

                try (OutputStream out = new FileOutputStream(mStagedFile)) {
                    byte[] buffer = new byte[1024 * 1024];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) >= 0) {
                        // Be nice and respond to a cancellation
                        if (isCancelled()) {
                            return false;
                        }
                        out.write(buffer, 0, bytesRead);
                    }
                }
            } catch (IOException | SecurityException | IllegalStateException e) {
                Log.w(LOG_TAG, "Error staging apk from content URI", e);
                return false;
            }
            return true;
        }

        @Override
        protected void onPostExecute(Boolean success) {
            if (success) {
                // Now start the installation again from a file
                Intent installIntent = new Intent(getIntent());
                installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);
                installIntent.setData(Uri.fromFile(mStagedFile));

                if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
                    installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
                }

                installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
                startActivity(installIntent);

                InstallStaging.this.finish();
            } else {
                showError();
            }
        }
    }

可以看到先是在doInBackground方法中将packageUri(content协议的URI)的内容写入到mStagedFile中,如果写入成功,则会在onPostExecute方法中去启动DeleteStagedFileOnResult的Activity,在DeleteStagedFileOnResultonCreate中,会启动PackageInstallerActivity这个Activity。查看上面其他的几个选择,发现也是去启动这个Activity。

PackageInstallerActivity

从功能的角度来说,PackageInstallerActivity才是PackageInstaller真正的入口Activity。接着先来看一下PackageInstallerActivity的onCreate方法。

@Override
protected void onCreate(Bundle icicle) {
    
    // PackageManager : 用于向应用程序进程提供一些功能,最终的功能是由PMS来实现的
    mPm = getPackageManager();
    // IPackageManager : 一个AIDL接口,用于和PMS进行进程间通信
    mIpm = AppGlobals.getPackageManager();
    // AppOpsManager : 用于动态检测,从Android4.3 开始被引入
    mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
    // PackageInstaller : 用于安装,升级和删除应用程序的功能
    mInstaller = mPm.getPackageInstaller();
    // UserManager : 用于多用户管理
    mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);

    final Intent intent = getIntent();

    mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);
    mCallingAttributionTag = intent.getStringExtra(EXTRA_CALLING_ATTRIBUTION_TAG);
    mSourceInfo = intent.getParcelableExtra(EXTRA_ORIGINAL_SOURCE_INFO);
    mOriginatingUid = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
            PackageInstaller.SessionParams.UID_UNKNOWN);
    mOriginatingPackage = (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN)
            ? getPackageNameForUid(mOriginatingUid) : null;

    final Uri packageUri;

    if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction())) {
        final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
        final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
        if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
            Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
            finish();
            return;
        }

        mSessionId = sessionId;
        packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath));
        mOriginatingURI = null;
        mReferrerURI = null;
    } else {
        mSessionId = -1;
        packageUri = intent.getData();
        mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
        mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
    }
    ···

    // ********************
    boolean wasSetUp = processPackageUri(packageUri);
    if (mLocalLOGV) Log.i(TAG, "wasSetUp: " + wasSetUp);

    if (!wasSetUp) {
        return;
    }
}

接着来看 PackageInstallerActivityprocessPackageUri 方法.

// 解析Uri并且设置这个包的安装程序    
private boolean processPackageUri(final Uri packageUri) {
    mPackageURI = packageUri;

    final String scheme = packageUri.getScheme();
    if (mLocalLOGV) Log.i(TAG, "processPackageUri(): uri=" + packageUri + ", scheme=" + scheme);

    switch (scheme) {
        case SCHEME_PACKAGE: {
            try {
                ···
        } break;

        case ContentResolver.SCHEME_FILE: {
            File sourceFile = new File(packageUri.getPath());
            // 对要安装的文件根据uid,用户状态信息和PackageManager的配置等变量对包信息进行进一步处理并且得到PackageInfo.
            mPkgInfo = PackageUtil.getPackageInfo(this, sourceFile,
                    PackageManager.GET_PERMISSIONS);

            // 检查解析的结果,如果没有解析出该对象,则会有报错的Dialog。
            if (mPkgInfo == null) {
                Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
                showDialogInner(DLG_PACKAGE_ERROR);
                setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
                return false;
            }
            if (mLocalLOGV) Log.i(TAG, "creating snippet for local file " + sourceFile);
            // 加载应用程序的标签,比如:应用图标,应用名称等等。
            mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
        } break;

        default: {
            throw new IllegalArgumentException("Unexpected URI scheme " + packageUri);
        }
    }

    return true;
}

创建dialog的方法如下所示:

private DialogFragment createDialog(int id) {
        if (mLocalLOGV) Log.i(TAG, "createDialog(" + id + ")");
        switch (id) {
            case DLG_PACKAGE_ERROR:
                return SimpleErrorDialog.newInstance(R.string.Parse_error_dlg_text);
            case DLG_OUT_OF_SPACE:
                return OutOfSpaceDialog.newInstance(
                        mPm.getApplicationLabel(mPkgInfo.applicationInfo));
            case DLG_INSTALL_ERROR:
                return InstallErrorDialog.newInstance(
                        mPm.getApplicationLabel(mPkgInfo.applicationInfo));
            case DLG_NOT_SUPPORTED_ON_WEAR:
                return NotSupportedOnWearDialog.newInstance();
            case DLG_INSTALL_APPS_RESTRICTED_FOR_USER:
                return SimpleErrorDialog.newInstance(
                        R.string.install_apps_user_restriction_dlg_text);
            case DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER:
                return SimpleErrorDialog.newInstance(
                        R.string.unknown_apps_user_restriction_dlg_text);
            case DLG_EXTERNAL_SOURCE_BLOCKED:
                return ExternalSourcesBlockedDialog.newInstance(mOriginatingPackage);
            case DLG_ANONYMOUS_SOURCE:
                return AnonymousSourceDialog.newInstance();
        }
        return null;
    }

在onCreate方法中干的活已经看了个大概,接着来看onResume方法中干了些什么。onResume干的事情我们就比较熟悉了。

@Override
protected void onResume() {
    super.onResume();

    if (mLocalLOGV) Log.i(TAG, "onResume(): mAppSnippet=" + mAppSnippet);

    if (mAppSnippet != null) {
        // 当 mAppSnippet不为空的时候,在`确定`按钮被禁用的情况下加载布局
        // 并且开始 安装确认
        bindUi();
        checkIfAllowedAndInitiateInstall();
    }

    if (mOk != null) {
        // mOk就是确定按钮
        mOk.setEnabled(mEnableOk);
    }
}

这一块主要是进行UI的绑定,当检测到
bindUI方法代码如下所示:

private void bindUi() {
    mAlert.setIcon(mAppSnippet.icon);
    mAlert.setTitle(mAppSnippet.label);
    mAlert.setView(R.layout.install_content_view);
    mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
            (ignored, ignored2) -> {
                // 点击了确认,并且mSessionId 不为-1
                if (mOk.isEnabled()) {
                    if (mSessionId != -1) {
                        mInstaller.setPermissionsResult(mSessionId, true);
                        finish();
                    } else {
                        startInstall();
                    }
                }
            }, null);
    mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
            (ignored, ignored2) -> {
                // Cancel and finish
                setResult(RESULT_CANCELED);
                if (mSessionId != -1) {
                    mInstaller.setPermissionsResult(mSessionId, false);
                }
                finish();
            }, null);
    setupAlert();
    // mOk就是确定按钮
    mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
    mOk.setEnabled(false);

    if (!mOk.isInTouchMode()) {
        mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).requestFocus();
    }
}

点击完确认后,还需要去检查能否允许安装,这就调用到了checkIfAllowedAndInitiateInstall()方法。

    // 检查是否允许安装,如果允许,安装;否则,显示对应的对话框
    private void checkIfAllowedAndInitiateInstall() {
        // 首先检查安装应用的用户限制
        final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(
                UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());
        if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
            if (mLocalLOGV) Log.i(TAG, "install not allowed: " + UserManager.DISALLOW_INSTALL_APPS);
            showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);
            return;
        } else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
            if (mLocalLOGV) {
                Log.i(TAG, "install not allowed by admin; showing "
                        + Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
            }
            startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
            finish();
            return;
        }

        if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
            if (mLocalLOGV) Log.i(TAG, "install allowed");
            initiateInstall();
        } else {
            // 检查位置来源限制
            final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(
                    UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());
            final int unknownSourcesGlobalRestrictionSource = mUserManager.getUserRestrictionSource(
                    UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, Process.myUserHandle());
            final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM
                    & (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource);
            if (systemRestriction != 0) {
                if (mLocalLOGV) Log.i(TAG, "Showing DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER");
                showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
            } else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
                startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
            } else if (unknownSourcesGlobalRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
                startAdminSupportDetailsActivity(
                        UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
            } else {
                handleUnknownSources();
            }
        }
    }

接着回到点击确认安装按钮的地方,也就是bindUi中收到对应的点击事件的回调之后,便开始startInstall操作。代码如下所示:

private void startInstall() {
    // 启动子Activity来实际安装应用层序
    Intent newIntent = new Intent();
    newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
            mPkgInfo.applicationInfo);
    newIntent.setData(mPackageURI);
    newIntent.setClass(this, InstallInstalling.class);
    String installerPackageName = getIntent().getStringExtra(
            Intent.EXTRA_INSTALLER_PACKAGE_NAME);
    if (mOriginatingURI != null) {
        newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
    }
    if (mReferrerURI != null) {
        newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
    }
    if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
        newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
    }
    if (installerPackageName != null) {
        newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
                installerPackageName);
    }
    if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
        newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
    }
    newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
    startActivity(newIntent);
    finish();
}

可以看到,在这里还要启动一个InstallInstalling的Activity进行后续操作,并且关闭当前的PackageInstallerActivity
InstallInstalling类主要用于向包管理器发送包的信息并处理包管理的回调。
接着来看下InstallInstallingonCreate方法。

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ApplicationInfo appInfo = getIntent()
                .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
        mPackageURI = getIntent().getData();

        // scheme 协议是package
        if ("package".equals(mPackageURI.getScheme())) {
            try {
                getPackageManager().installExistingPackage(appInfo.packageName);
                launchSuccess();
            } catch (PackageManager.NameNotFoundException e) {
                launchFailure(PackageInstaller.STATUS_FAILURE,
                        PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
        } else {
            // 对Uri协议是 content的进行处理
            final File sourceFile = new File(mPackageURI.getPath());
            PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);

            mAlert.setIcon(as.icon);
            mAlert.setTitle(as.label);
            mAlert.setView(R.layout.install_content_view);
            mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
                    (ignored, ignored2) -> {
                        if (mInstallingTask != null) {
                            mInstallingTask.cancel(true);
                        }

                        if (mSessionId > 0) {
                            getPackageManager().getPackageInstaller().abandonSession(mSessionId);
                            mSessionId = 0;
                        }

                        setResult(RESULT_CANCELED);
                        finish();
                    }, null);
            setupAlert();
            requireViewById(R.id.installing).setVisibility(View.VISIBLE);

            if (savedInstanceState != null) {
                // 如果savedInstanceState 不为空,就获取之前保存的seddionId和installId
                // mSessionId 是安装包的会话id
                // mInstallId 是等待的安装时间id
                mSessionId = savedInstanceState.getInt(SESSION_ID);
                mInstallId = savedInstanceState.getInt(INSTALL_ID);

                // 注册结果,如果Activity在销毁的时候交付了结果,可能会立即回调
                // InstallEventReceiver 是继承自 BroadcastReceiver,用于接收安装事件并且回调给EventResultPersister
                try {
                    InstallEventReceiver.addObserver(this, mInstallId,
                            this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    // Does not happen
                }
            } else {
                // 创建一个SessionParams对象用来存储传过来的意图参数
                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                params.setInstallAsInstantApp(false);
                params.setReferrerUri(getIntent().getParcelableExtra(Intent.EXTRA_REFERRER));
                params.setOriginatingUri(getIntent()
                        .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI));
                params.setOriginatingUid(getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
                        UID_UNKNOWN));
                params.setInstallerPackageName(getIntent().getStringExtra(
                        Intent.EXTRA_INSTALLER_PACKAGE_NAME));
                params.setInstallReason(PackageManager.INSTALL_REASON_USER);

                File file = new File(mPackageURI.getPath());
                try {
                    final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
                    final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
                            input.reset(), file, /* flags */ 0);
                    if (result.isError()) {
                        Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");
                        Log.e(LOG_TAG,
                                "Cannot calculate installed size " + file + ". Try only apk size.");
                        params.setSize(file.length());
                    } else {
                        final PackageLite pkg = result.getResult();
                        params.setAppPackageName(pkg.getPackageName());
                        params.setInstallLocation(pkg.getInstallLocation());
                        params.setSize(
                                PackageHelper.calculateInstalledSize(pkg, params.abiOverride));
                    }
                } catch (IOException e) {
                    Log.e(LOG_TAG,
                            "Cannot calculate installed size " + file + ". Try only apk size.");
                    params.setSize(file.length());
                }

                try {
                    // 这里如果没有mInstallId,那么就生成一个新的mInstallId
                    mInstallId = InstallEventReceiver
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                    this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    launchFailure(PackageInstaller.STATUS_FAILURE,
                            PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }

                try {
                    // 通过PackageInstallerService的createSession方法来为mSessionId赋值。
                    mSessionId = getPackageManager().getPackageInstaller().createSession(params);
                } catch (IOException e) {
                    launchFailure(PackageInstaller.STATUS_FAILURE,
                            PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }
            }

            mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);
        }
    }

这里的launchFinishBasedOnResult方法是为安装的结果启动对应的完成Activity,也就是成功或者失败。可以看到如果statusCode是success,则会运行launchSuccess方法,这个方法会吊起安装成功的Activity,否则会运行launchFailure方法。
方法如下所示:


private void launchFinishBasedOnResult(int statusCode, int legacyStatus, String statusMessage) {
    if (statusCode == PackageInstaller.STATUS_SUCCESS) {
        launchSuccess();
    } else {
        launchFailure(statusCode, legacyStatus, statusMessage);
    }
}

接着来看一下onResume方法干了些什么.

@Override
protected void onResume() {
    super.onResume();

    // This is the first onResume in a single life of the activity
    if (mInstallingTask == null) {
        PackageInstaller installer = getPackageManager().getPackageInstaller();
        // sessionInfo 代表会话的详细信息。
        PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);

        if (sessionInfo != null && !sessionInfo.isActive()) {
            mInstallingTask = new InstallingAsyncTask();
            mInstallingTask.execute();
        } else {
            // we will receive a broadcast when the install is finished
            mCancelButton.setEnabled(false);
            setFinishOnTouchOutside(false);
        }
    }
}

其中,这里可以看到也是执行了一个AsyncTask,可以看到也是一个自定义AsyncTask。下面是InstallingAsyncTask
InstallingAsyncTask会根据Apk的Uri将Apk的信息通过I/O流的形式写入到``

// 将包发送到包安装程序,然后注册事件结果监听器,该监听器将会调用 launchFinishBasedOnResult方法。
private final class InstallingAsyncTask extends AsyncTask<Void, Void,
        PackageInstaller.Session> {
    volatile boolean isDone;

    @Override
    protected PackageInstaller.Session doInBackground(Void... params) {
        PackageInstaller.Session session;
        try {
            session = getPackageManager().getPackageInstaller().openSession(mSessionId);
        } catch (IOException e) {
            synchronized (this) {
                isDone = true;
                notifyAll();
            }
            return null;
        }

        session.setStagingProgress(0);

        try {
            // 拿到对应的apk 的Uri
            File file = new File(mPackageURI.getPath());

            try (InputStream in = new FileInputStream(file)) {
                long sizeBytes = file.length();
                try (OutputStream out = session
                        .openWrite("PackageInstaller", 0, sizeBytes)) {
                    byte[] buffer = new byte[1024 * 1024];
                    while (true) {
                        int numRead = in.read(buffer);

                        if (numRead == -1) {
                            session.fsync(out);
                            break;
                        }

                        if (isCancelled()) {
                            session.close();
                            break;
                        }

                        out.write(buffer, 0, numRead);
                        if (sizeBytes > 0) {
                            float fraction = ((float) numRead / (float) sizeBytes);
                            session.addProgress(fraction);
                        }
                    }
                }
            }

            return session;
        } catch (IOException | SecurityException e) {
            Log.e(LOG_TAG, "Could not write package", e);

            session.close();

            return null;
        } finally {
            synchronized (this) {
                isDone = true;
                notifyAll();
            }
        }
    }

    @Override
    protected void onPostExecute(PackageInstaller.Session session) {
        if (session != null) {
            Intent broadcastIntent = new Intent(BROADCAST_ACTION);
            broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
            broadcastIntent.setPackage(getPackageName());
            broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);

            PendingIntent pendingIntent = PendingIntent.getBroadcast(
                    InstallInstalling.this,
                    mInstallId,
                    broadcastIntent,
                    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);

            session.commit(pendingIntent.getIntentSender());
            mCancelButton.setEnabled(false);
            setFinishOnTouchOutside(false);
        } else {
            getPackageManager().getPackageInstaller().abandonSession(mSessionId);

            if (!isCancelled()) {
                launchFailure(PackageInstaller.STATUS_FAILURE,
                        PackageManager.INSTALL_FAILED_INVALID_APK, null);
            }
        }
    }
}

PackageInstaller.Session的commit方法如下所示,

         /**
 * Attempt to commit everything staged in this session. This may require
 * user intervention, and so it may not happen immediately. The final
 * result of the commit will be reported through the given callback.
 * <p>
 * Once this method is called, the session is sealed and no additional mutations may be
 * performed on the session. In case of device reboot or data loader transient failure
 * before the session has been finalized, you may commit the session again.
 * <p>
 * If the installer is the device owner or the affiliated profile owner, there will be no
 * user intervention.
 *
 * @param statusReceiver Called when the state of the session changes. Intents
 *                       sent to this receiver contain {@link #EXTRA_STATUS}. Refer to the
 *                       individual status codes on how to handle them.
 *
 * @throws SecurityException if streams opened through
 *             {@link #openWrite(String, long, long)} are still open.
 *
 * @see android.app.admin.DevicePolicyManager
 */
public void commit(@NonNull IntentSender statusReceiver) {
    try {
        mSession.commit(statusReceiver, false);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
   }
}

其中,mSession是一个IPackageInstallerSession对象,这是一个AIDL接口类型,所以查询下可以看到PackageInstallerSession实现了这个接口,接着看下PackageInstallerSession实现的这个commit方法。

@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
    if (hasParentSessionId()) {
        throw new IllegalStateException(
                "Session " + sessionId + " is a child of multi-package session "
                        + getParentSessionId() +  " and may not be committed directly.");
    }
    // markAsSealed 方法是判断是否被调用,如果没有调用,这个session就会被封存。
    if (!markAsSealed(statusReceiver, forTransfer)) {
        return;
    }
    if (isMultiPackage()) {
        synchronized (mLock) {
            final IntentSender childIntentSender =
                    new ChildStatusIntentReceiver(mChildSessions.clone(), statusReceiver)
                            .getIntentSender();
            boolean sealFailed = false;
            for (int i = mChildSessions.size() - 1; i >= 0; --i) {
                // 处理所有的子session,无论其中任何一个失败,处理完后都会被舍弃。
                if (!mChildSessions.valueAt(i)
                        .markAsSealed(childIntentSender, forTransfer)) {
                    sealFailed = true;
                }
            }
            if (sealFailed) {
                return;
            }
        }
    }

    // 如果没有被舍弃,就执行dispatchSessionSealed
    dispatchSessionSealed();
}

dispatchSessionSealed方法用来启动安装流程,第一步是保持密封标志,防止稍后创建的硬链接的突变。
dispatchSessionSealed方法如下所示:

private void dispatchSessionSealed() {
    mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
}

接着就会调到handleSessionSealed方法

private void handleSessionSealed() {
    assertSealed("dispatchSessionSealed");
    // Persist the fact that we've sealed ourselves to prevent
    // mutations of any hard links we create.
    mCallback.onSessionSealedBlocking(this);
    dispatchStreamValidateAndCommit();
}

dispatchStreamValidateAndCommit方法又会发送MSG_STREAM_VALIDATE_AND_COMMIT的Message。

private void dispatchStreamValidateAndCommit() {
    mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
}

接着就会执行handleStreamValidateAndCommit方法,

private void handleStreamValidateAndCommit() {
    PackageManagerException unrecoverableFailure = null;
    // This will track whether the session and any children were validated and are ready to
    // progress to the next phase of install
    boolean allSessionsReady = false;
    try {
        allSessionsReady = streamValidateAndCommit();
    } catch (PackageManagerException e) {
        unrecoverableFailure = e;
    }

    if (isMultiPackage()) {
        final List<PackageInstallerSession> childSessions;
        synchronized (mLock) {
            childSessions = getChildSessionsLocked();
        }
        int childCount = childSessions.size();

        // This will contain all child sessions that do not encounter an unrecoverable failure
        ArrayList<PackageInstallerSession> nonFailingSessions = new ArrayList<>(childCount);

        for (int i = childCount - 1; i >= 0; --i) {
            // commit all children, regardless if any of them fail; we'll throw/return
            // as appropriate once all children have been processed
            try {
                PackageInstallerSession session = childSessions.get(i);
                allSessionsReady &= session.streamValidateAndCommit();
                nonFailingSessions.add(session);
            } catch (PackageManagerException e) {
                allSessionsReady = false;
                if (unrecoverableFailure == null) {
                    unrecoverableFailure = e;
                }
            }
        }
        // If we encountered any unrecoverable failures, destroy all other sessions including
        // the parent
        if (unrecoverableFailure != null) {
            // {@link #streamValidateAndCommit()} calls
            // {@link #onSessionValidationFailure(PackageManagerException)}, but we don't
            // expect it to ever do so for parent sessions. Call that on this parent to clean
            // it up and notify listeners of the error.
            onSessionValidationFailure(unrecoverableFailure);
            // fail other child sessions that did not already fail
            for (int i = nonFailingSessions.size() - 1; i >= 0; --i) {
                PackageInstallerSession session = nonFailingSessions.get(i);
                session.onSessionValidationFailure(unrecoverableFailure);
            }
        }
    }
    if (!allSessionsReady) {
        return;
    }
    mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
}

接着会发送MSG_INSTALL的Message,该消息会去执行handleInstall方法。

private void handleInstall() {
    if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
        DevicePolicyEventLogger
                .createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
                .setAdmin(mInstallSource.installerPackageName)
                .write();
    }

    // Check if APEX update is allowed. We do this check in handleInstall, since this is one of
    // the places that:
    //   * Shared between staged and non-staged APEX update flows.
    //   * Only is called after boot completes.
    // The later is important, since isApexUpdateAllowed check depends on the
    // ModuleInfoProvider, which is only populated after device has booted.
    if (isApexSession()) {
        boolean checkApexUpdateAllowed =
                (params.installFlags & PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK)
                    == 0;
        synchronized (mLock) {
            if (checkApexUpdateAllowed && !isApexUpdateAllowed(mPackageName,
                        mInstallSource.installerPackageName)) {
                onSessionValidationFailure(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                        "Update of APEX package " + mPackageName + " is not allowed for "
                                + mInstallSource.installerPackageName);
                return;
            }
        }

        if (!params.isStaged) {
            // For non-staged APEX installs also check if there is a staged session that
            // contains the same APEX. If that's the case, we should fail this session.
            synchronized (mLock) {
                int sessionId = mStagingManager.getSessionIdByPackageName(mPackageName);
                if (sessionId != -1) {
                    onSessionValidationFailure(
                            PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                            "Staged session " + sessionId + " already contains "
                                    + mPackageName);
                    return;
                }
            }
        }
    }

    if (params.isStaged) {
        mStagingManager.commitSession(mStagedSession);
        // TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
        //  though ideally, we just need to send session committed broadcast.
        dispatchSessionFinished(INSTALL_SUCCEEDED, "Session staged", null);
        return;
    }

    verify();
}

接着verify方法又会接着往下执行,

private void verify() {
    try {
        verifyNonStaged();
    } catch (PackageManagerException e) {
        final String completeMsg = ExceptionUtils.getCompleteMessage(e);
        onSessionVerificationFailure(e.error, completeMsg);
    }
}

接着来看一下verifyNonStaged干了些啥,

private void verifyNonStaged()
        throws PackageManagerException {
    final PackageManagerService.VerificationParams verifyingSession =
            prepareForVerification();
    if (verifyingSession == null) {
        return;
    }
    if (isMultiPackage()) {
        final List<PackageInstallerSession> childSessions;
        synchronized (mLock) {
            childSessions = getChildSessionsLocked();
        }
        // Spot check to reject a non-staged multi package install of APEXes and APKs.
        if (!params.isStaged && containsApkSession()
                && sessionContains(s -> s.isApexSession())) {
            throw new PackageManagerException(
                PackageManager.INSTALL_FAILED_SESSION_INVALID,
                "Non-staged multi package install of APEX and APK packages is not supported");
        }
        List<PackageManagerService.VerificationParams> verifyingChildSessions =
                new ArrayList<>(childSessions.size());
        boolean success = true;
        PackageManagerException failure = null;
        for (int i = 0; i < childSessions.size(); ++i) {
            final PackageInstallerSession session = childSessions.get(i);
            try {
                final PackageManagerService.VerificationParams verifyingChildSession =
                        session.prepareForVerification();
                if (verifyingChildSession != null) {
                    verifyingChildSessions.add(verifyingChildSession);
                }
            } catch (PackageManagerException e) {
                failure = e;
                success = false;
            }
        }
        if (!success) {
            final IntentSender statusReceiver;
            synchronized (mLock) {
                statusReceiver = mRemoteStatusReceiver;
            }
            sendOnPackageInstalled(mContext, statusReceiver, sessionId,
                    isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
                    failure.error, failure.getLocalizedMessage(), null);
            return;
        }
        mPm.verifyStage(verifyingSession, verifyingChildSessions);
    } else {
        mPm.verifyStage(verifyingSession);
    }
}

首先在上面执行了prepareForVerification方法,来看下这个方法干了些什么事,

// 这里主要是将会话分段进行验证,并返回表示此新分段状态的参数 VerificationParams对象,如果在验证继续之前需要请求权限,则返回null。
private PackageManagerService.VerificationParams prepareForVerification()
        throws PackageManagerException {
    assertNotLocked("makeSessionActive");

    @UserActionRequirement
    int userActionRequirement = USER_ACTION_NOT_NEEDED;
    // 这里的params是SessionParams,是PackageInstaller的一个内部类
    // isMultiPackage 如果提交的Session包含多个package,那么返回true
    if (!params.isMultiPackage) {
        userActionRequirement = computeUserActionRequirement();
        // 检查是否还需要确认权限,如果是,则return null。
        if (userActionRequirement == USER_ACTION_REQUIRED) {
            sendPendingUserActionIntent();
            return null;
        } // else, we'll wait until we parse to determine if we need to
    }

    boolean silentUpdatePolicyEnforceable = false;
    synchronized (mLock) {
        if (mRelinquished) {
            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                    "Session relinquished");
        }
        if (mDestroyed) {
            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                    "Session destroyed");
        }
        if (!mSealed) {
            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                    "Session not sealed");
        }
        PackageLite result = parseApkLite();
        if (result != null) {
            mPackageLite = result;
            synchronized (mProgressLock) {
                mInternalProgress = 0.5f;
                computeProgressLocked(true);
            }

            extractNativeLibraries(
                    mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());

            if (userActionRequirement == USER_ACTION_PENDING_APK_PARSING) {
                if (result.getTargetSdk() < Build.VERSION_CODES.Q) {
                    sendPendingUserActionIntent();
                    return null;
                }
                if (params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED) {
                    silentUpdatePolicyEnforceable = true;
                }
            }
        }
    }
    if (silentUpdatePolicyEnforceable) {
        if (!mSilentUpdatePolicy.isSilentUpdateAllowed(
                getInstallerPackageName(), getPackageName())) {
            // Fall back to the non-silent update if a repeated installation is invoked within
            // the throttle time.
            sendPendingUserActionIntent();
            return null;
        }
        mSilentUpdatePolicy.track(getInstallerPackageName(), getPackageName());
    }
    synchronized (mLock) {
        return makeVerificationParamsLocked();
    }
}

computeUserActionRequirement()代码如下所示:

// 检查是否仍需要确认权限
private int computeUserActionRequirement() {
    final String packageName;
    synchronized (mLock) {
        if (mPermissionsManuallyAccepted) {
            return USER_ACTION_NOT_NEEDED;
        }
        packageName = mPackageName;
    }

    final boolean forcePermissionPrompt =
            (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0
                    || params.requireUserAction == SessionParams.USER_ACTION_REQUIRED;
    if (forcePermissionPrompt) {
        return USER_ACTION_REQUIRED;
    }
    // It is safe to access mInstallerUid and mInstallSource without lock
    // because they are immutable after sealing.
    final boolean isInstallPermissionGranted =
            (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
                    mInstallerUid) == PackageManager.PERMISSION_GRANTED);
    final boolean isSelfUpdatePermissionGranted =
            (mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES,
                    mInstallerUid) == PackageManager.PERMISSION_GRANTED);
    final boolean isUpdatePermissionGranted =
            (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
                    mInstallerUid) == PackageManager.PERMISSION_GRANTED);
    final boolean isUpdateWithoutUserActionPermissionGranted = (mPm.checkUidPermission(
            android.Manifest.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION, mInstallerUid)
            == PackageManager.PERMISSION_GRANTED);
    final int targetPackageUid = mPm.getPackageUid(packageName, 0, userId);
    final boolean isUpdate = targetPackageUid != -1 || isApexSession();
    final InstallSourceInfo existingInstallSourceInfo = isUpdate
            ? mPm.getInstallSourceInfo(packageName)
            : null;
    final String existingInstallerPackageName = existingInstallSourceInfo != null
            ? existingInstallSourceInfo.getInstallingPackageName()
            : null;
    final boolean isInstallerOfRecord = isUpdate
            && Objects.equals(existingInstallerPackageName, getInstallerPackageName());
    final boolean isSelfUpdate = targetPackageUid == mInstallerUid;
    final boolean isPermissionGranted = isInstallPermissionGranted
            || (isUpdatePermissionGranted && isUpdate)
            || (isSelfUpdatePermissionGranted && isSelfUpdate);
    final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
    final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);

    // Device owners and affiliated profile owners  are allowed to silently install packages, so
    // the permission check is waived if the installer is the device owner.
    final boolean noUserActionNecessary = isPermissionGranted || isInstallerRoot
            || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwner();

    if (noUserActionNecessary) {
        return USER_ACTION_NOT_NEEDED;
    }

    if (mPm.isInstallDisabledForPackage(getInstallerPackageName(), mInstallerUid, userId)) {
        // show the installer to account for device poslicy or unknown sources use cases
        return USER_ACTION_REQUIRED;
    }

    if (params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED
            && isUpdateWithoutUserActionPermissionGranted
            && (isInstallerOfRecord || isSelfUpdate)) {
        return USER_ACTION_PENDING_APK_PARSING;
    }

    return USER_ACTION_REQUIRED;
}
<string name="app_name" msgid="7488448184431507488">"软件包安装程序"</string>
<string name="install" msgid="711829760615509273">"安装"</string>
<string name="update" msgid="3932142540719227615">"更新"</string>
<string name="done" msgid="6632441120016885253">"完成"</string>
<string name="cancel" msgid="1018267193425558088">"取消"</string>
<string name="installing" msgid="4921993079741206516">"正在安装…"</string>
<string name="installing_app" msgid="1165095864863849422">"正在安装<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
<string name="install_done" msgid="5987363587661783896">"已安装应用。"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"要安装此应用吗?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"要更新此应用吗?"</string>

总结来说,这一部分主要是在安装前进行一些权限的检查,还没有真正进入安装的核心代码处。从InstallStart 开始,最后交由PMS进行实际的安装工作。这一块主要是对安装前的任务进行了检查,检查完之后才去对apk进行安装操作。

标签:PackageInstaller,return,APK,PMS,apk,Intent,INSTALL,null,final
From: https://www.cnblogs.com/zhenji-li/p/16863652.html

相关文章

  • android 手机 apk安装失败对应码
    下面是从网上找到的几种常见的错误及解决方法:1、INSTALL_FAILED_INVALID_APK:无效的安装包,安装包已损坏请检查安装包是否完整。如果是xpk包,可以通过手动安装xpk来检测一......
  • 我的APK作品
    1辅导王APK开发上线连接:https://www.wandoujia.com/apps/2198223/history_v17:开发时间:2012-2014 第一份工作就是开发androidapp. 十年前了, 2.西安公交查询系......
  • IIS7无法访问.apk文件的解决方法
    随着智能手机的普及,越来越多的人使用手机上网,很多网站也应手机上网的需要推出了网站客户端,.apk文件就是安卓(Android)的应用程序后缀名,默认情况下,使用IIS作为Web服务器......
  • Android APK 文件结构
    序言APK(全称:Androidapplicationpackage,Android应用程序包)是Android操作系统使用的一种应用程序包文件格式,用于分发和安装移动应用及中间件。APK文件基于ZIP文件格式......
  • MAUI发布APK初体验
    目的很早就有想编写安卓程序玩玩的念头了,所以这次学习将MAUI程序生成apk包来玩。本文apk下载地址:https://azrng.lanzouv.com/iBQRe0eeg8wf,内容很简单,一个时间轴以及一个......
  • Unable to allocate new pages in table space "PMS_TAB"
    Error:[IBM][CLIDriver][DB2/NT]SQL0289NUnabletoallocatenewpagesintablespace"PMS_TAB".SQLSTATE=57011,Connector'C_DB2_SBOSUB',Method-Insert-(......
  • APK downgrade extraction fails on Samsung smartphones
    AfriendofmineEllensheasksmetogiveherahand.CoupledaysagoshetriedtoextractLINEchathistoryfromaSamsungsmartphonerunningAndroid12asb......
  • 反编译APK获取代码以及资源
    需要工具:1、apktool:获取资源文件,提取图片文件,布局文件,还有一些XML的资源文件2、dex2jar:将APK反编译成Java源码(将classes.dex转化为jar文件)3、jd-gui:查看转换后的jar......
  • 极米投影仪安装apk的方法
    https://www.touying.com/t-37871-1.html 方法二:使用U盘安装:1.使用电脑下载软件apk,并将软件apk的后缀修改为“apk1”;2.然后将后缀为apk1的安装包文件拷贝到U盘,将U盘接......
  • android开发apk安装失败对应的错误码以及原因
    android开发apk安装失败对应的错误码以及原因代码在PackageManager.java系统类里中文意思可参考:https://www.cnblogs.com/cwfsoft/p/11776094.html/***@hide 未知*......