Android13 PMS是如何启动的?

news2024/11/25 2:28:35

作者:Arthas0v0

平常使用安卓实际就是在使用各种app,而下载的app实际是一个apk文件。这个apk文件的安装就交给了PackageManagerService来实现。PackageManagerService的启动也是在SystemServer中。这个过程比较长需要长一点的时间来理。

SystemServer.startBootstrapServices

//frameworks/base/services/java/com/android/server/SystemServer.java       
        Installer installer = mSystemServiceManager.startService(Installer.class);  
        IPackageManager iPackageManager;
        t.traceBegin("StartPackageManagerService");
        try {
            Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");
            Pair<PackageManagerService, IPackageManager> pmsPair = PackageManagerService.main(
                    mSystemContext, installer, domainVerificationService,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
            mPackageManagerService = pmsPair.first;
            iPackageManager = pmsPair.second;
        } finally {
            Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");
        }
   mFirstBoot = mPackageManagerService.isFirstBoot();

PackageManagerService的main方法初始化了PackageManagerService和IPackageManager,其中传入的installer需要特别关注一下,很关键的变量。

//frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public static Pair<PackageManagerService, IPackageManager> main(Context context,
            Installer installer, @NonNull DomainVerificationService domainVerificationService,
            boolean factoryTest, boolean onlyCore) {
        // Self-check for initial settings.
        PackageManagerServiceCompilerMapping.checkProperties();
        final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
                Trace.TRACE_TAG_PACKAGE_MANAGER);
        t.traceBegin("create package manager");
        final PackageManagerTracedLock lock = new PackageManagerTracedLock();
        final Object installLock = new Object();
    //创建了一个HandlerThread,并启动
        HandlerThread backgroundThread = new ServiceThread("PackageManagerBg",
                Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
        backgroundThread.start();
        Handler backgroundHandler = new Handler(backgroundThread.getLooper());
//真正的操作和相关的对象都放到了PackageManagerServiceInjector中。
        PackageManagerServiceInjector injector = new PackageManagerServiceInjector(
                context, lock, installer, installLock, new PackageAbiHelperImpl(),
                backgroundHandler,
                SYSTEM_PARTITIONS,
                (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mUserNeedsBadging),
                (i, pm) -> PermissionManagerService.create(context,
                        i.getSystemConfig().getAvailableFeatures()),
                (i, pm) -> new UserManagerService(context, pm,
                        new UserDataPreparer(installer, installLock, context, onlyCore),
                        lock),
                (i, pm) -> new Settings(Environment.getDataDirectory(),
                        RuntimePermissionsPersistence.createInstance(),
                        i.getPermissionManagerServiceInternal(),
                        domainVerificationService, backgroundHandler, lock),
                (i, pm) -> AppsFilterImpl.create(i,
                        i.getLocalService(PackageManagerInternal.class)),
                (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"),
                (i, pm) -> SystemConfig.getInstance(),
                (i, pm) -> new PackageDexOptimizer(i.getInstaller(), i.getInstallLock(),
                        i.getContext(), "*dexopt*"),
                (i, pm) -> new DexManager(i.getContext(), i.getPackageDexOptimizer(),
                        i.getInstaller(), i.getInstallLock()),
                (i, pm) -> new ArtManagerService(i.getContext(), i.getInstaller(),
                        i.getInstallLock()),
                (i, pm) -> ApexManager.getInstance(),
                (i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()),
                (i, pm) -> (IncrementalManager)
                        i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),
                (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class),
                        () -> LocalServices.getService(UserManagerInternal.class)),
                (i, pm) -> new DisplayMetrics(),
                (i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,
                        i.getDisplayMetrics(), pm.mCacheDir,
                        pm.mPackageParserCallback) /* scanningCachingPackageParserProducer */,
                (i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,
                        i.getDisplayMetrics(), null,
                        pm.mPackageParserCallback) /* scanningPackageParserProducer */,
                (i, pm) -> new PackageParser2(pm.mSeparateProcesses, false, i.getDisplayMetrics(),
                        null, pm.mPackageParserCallback) /* preparingPackageParserProducer */,
                // Prepare a supplier of package parser for the staging manager to parse apex file
                // during the staging installation.
                (i, pm) -> new PackageInstallerService(
                        i.getContext(), pm, i::getScanningPackageParser),
                (i, pm, cn) -> new InstantAppResolverConnection(
                        i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE),
                (i, pm) -> new ModuleInfoProvider(i.getContext()),
                (i, pm) -> LegacyPermissionManagerService.create(i.getContext()),
                (i, pm) -> domainVerificationService,
                (i, pm) -> {
                    HandlerThread thread = new ServiceThread(TAG,
                            Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
                    thread.start();
                    return new PackageHandler(thread.getLooper(), pm);
                },
                new DefaultSystemWrapper(),
                LocalServices::getService,
                context::getSystemService,
                (i, pm) -> new BackgroundDexOptService(i.getContext(), i.getDexManager(), pm),
                (i, pm) -> IBackupManager.Stub.asInterface(ServiceManager.getService(
                        Context.BACKUP_SERVICE)),
                (i, pm) -> new SharedLibrariesImpl(pm, i));
​
        if (Build.VERSION.SDK_INT <= 0) {
            Slog.w(TAG, "**** ro.build.version.sdk not set!");
        }
  //创建一个PackageManagerService实例
        PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest,
                PackagePartitions.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG,
                Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL);
        t.traceEnd(); // "create package manager"
.......
      //根据用户类型安装或卸载系统应用安装包
        m.installAllowlistedSystemPackages();
        //创建IPackageManager并添加到ServiceManager中,客户端交互
        IPackageManagerImpl iPackageManager = m.new IPackageManagerImpl();
        ServiceManager.addService("package", iPackageManager);
      //创建native的PackageManager,注册到ServiceManager中
        final PackageManagerNative pmn = new PackageManagerNative(m);
        ServiceManager.addService("package_native", pmn);
        LocalManagerRegistry.addManager(PackageManagerLocal.class, m.new PackageManagerLocalImpl());
    //返回PackageManagerService和IPackageManager
        return Pair.create(m, iPackageManager);
    }

在main方法中重要的就几件事

1.创建PackageManagerServiceInjector
2.创建PackageManagerService
3.创建iPackageManager添加到ServiceManager中
4.创建PackageManagerNative添加到ServiceManager中

PackageManagerServiceInjector就是很简单的做了一层封装,把所有相关的对象都初始化都保存了起来。接下来看PackageManagerService,这个初始化函数非常长,所以分几段来看,可以根据log分为BOOT_PROGRESS_PMS_START,BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,BOOT_PROGRESS_PMS_SCAN_END,BOOT_PROGRESS_PMS_READY,这几个阶段

BOOT_PROGRESS_PMS_START

 public PackageManagerService(PackageManagerServiceInjector injector, boolean onlyCore,
            boolean factoryTest, final String buildFingerprint, final boolean isEngBuild,
            final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
        mIsEngBuild = isEngBuild;
        mIsUserDebugBuild = isUserDebugBuild;
        mSdkVersion = sdkVersion;
        mIncrementalVersion = incrementalVersion;
        mInjector = injector;
        mInjector.getSystemWrapper().disablePackageCaches();
​
        final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
                Trace.TRACE_TAG_PACKAGE_MANAGER);
        mPendingBroadcasts = new PendingPackageBroadcasts();
        mInjector.bootstrap(this);
        mLock = injector.getLock();
        mPackageStateWriteLock = mLock;
        mInstallLock = injector.getInstallLock();//重要的lock
        LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES);
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
                SystemClock.uptimeMillis());//BOOT_PROGRESS_PMS_START阶段
​
        mContext = injector.getContext();
        mFactoryTest = factoryTest;
        mOnlyCore = onlyCore;//是否只启动核心业务
        mMetrics = injector.getDisplayMetrics();
        mInstaller = injector.getInstaller();//获取installer
        mEnableFreeCacheV2 = SystemProperties.getBoolean("fw.free_cache_v2", true);
        // 将当前PackageManagerInternal注册到了LocalServices中
        LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
        LocalServices.addService(TestUtilityService.class, this);
        mTestUtilityService = LocalServices.getService(TestUtilityService.class);
        //......中间都是获取PackageManagerServiceInjector的一些成员变量
        // 创建了Settings类了,mSettings管理着app的安装信息,这里添加了6个app的包名到共享用户列表,同时设置权限为系统级别的权限uid
        mSettings = injector.getSettings();
        mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.se", SE_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        //dex到odex/oat的转换
        mPackageDexOptimizer = injector.getPackageDexOptimizer();
        mDexManager = injector.getDexManager();
        mBackgroundDexOptService = injector.getBackgroundDexOptService();
        //ART虚拟机管理服务
        mArtManagerService = injector.getArtManagerService();
        mMoveCallbacks = new MovePackageHelper.MoveCallbacks(FgThread.get().getLooper());
        mViewCompiler = injector.getViewCompiler();
        //共享库
        mSharedLibraries = mInjector.getSharedLibrariesImpl();
        //获取分辨率
        mContext.getSystemService(DisplayManager.class)
                .getDisplay(Display.DEFAULT_DISPLAY).getMetrics(mMetrics);
​
        t.traceBegin("get system config");
        //获取系统配置
        SystemConfig systemConfig = injector.getSystemConfig();
        mAvailableFeatures = systemConfig.getAvailableFeatures();
        t.traceEnd();
        //限制几个系统app的访问,如launcher
        mProtectedPackages = new ProtectedPackages(mContext);
        mApexManager = injector.getApexManager();
        mAppsFilter = mInjector.getAppsFilter();
​
        mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager,
                mInjector.getUserManagerInternal(), new DeletePackageHelper(this));
      //复制监听包变化的事件,安装卸载等
        mChangedPackagesTracker = new ChangedPackagesTracker();
        //非系统app的路径
        mAppInstallDir = new File(Environment.getDataDirectory(), "app");
      //.....中间初始化了各种操作类
        // CHECKSTYLE:OFF IndentationCheck
        synchronized (mInstallLock) {
        // writer
        synchronized (mLock) {
            //获取安装线程的handler,同样是injector中创建的
            mHandler = injector.getHandler();
            mProcessLoggingHandler = new ProcessLoggingHandler();
            Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
            //获取系统的共享库
            ArrayMap<String, SystemConfig.SharedLibraryEntry> libConfig
                    = systemConfig.getSharedLibraries();
            final int builtInLibCount = libConfig.size();
            for (int i = 0; i < builtInLibCount; i++) {
                mSharedLibraries.addBuiltInSharedLibraryLPw(libConfig.valueAt(i));
            }
​
            // Now that we have added all the libraries, iterate again to add dependency
            // information IFF their dependencies are added.
            //添加库的依赖
            long undefinedVersion = SharedLibraryInfo.VERSION_UNDEFINED;
            for (int i = 0; i < builtInLibCount; i++) {
                String name = libConfig.keyAt(i);
                SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
                final int dependencyCount = entry.dependencies.length;
                for (int j = 0; j < dependencyCount; j++) {
                    final SharedLibraryInfo dependency =
                        computer.getSharedLibraryInfo(entry.dependencies[j], undefinedVersion);
                    if (dependency != null) {
                        computer.getSharedLibraryInfo(name, undefinedVersion)
                                .addDependency(dependency);
                    }
                }
            }
​
            SELinuxMMAC.readInstallPolicy();
​
            t.traceBegin("loadFallbacks");
            FallbackCategoryProvider.loadFallbacks();
            t.traceEnd();
​
            t.traceBegin("read user settings");
          //根据/data/system下的XML判断是不是第一次启动
            mFirstBoot = !mSettings.readLPw(computer,
                    mInjector.getUserManagerInternal().getUsers(
                    /* excludePartial= */ true,
                    /* excludeDying= */ false,
                    /* excludePreCreated= */ false));
            t.traceEnd();
​
            if (mFirstBoot) {
                t.traceBegin("setFirstBoot: ");
                try {
                    mInstaller.setFirstBoot();
                } catch (InstallerException e) {
                    Slog.w(TAG, "Could not set First Boot: ", e);
                }
                t.traceEnd();
            }
            //获取系统中定义的权限保存起来
            mPermissionManager.readLegacyPermissionsTEMP(mSettings.mPermissions);
            mPermissionManager.readLegacyPermissionStateTEMP();
​
            if (!mOnlyCore && mFirstBoot) {
              //如果都不是就复制dex
                DexOptHelper.requestCopyPreoptedFiles();
            }

这里做的还都是准备工作,获取了需要的一系列对象和路径,比较重要的点就是创建了Settings。

//frameworks/base/services/core/java/com/android/server/pm/Settings.java
Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence,
            LegacyPermissionDataProvider permissionDataProvider,
            @NonNull DomainVerificationManagerInternal domainVerificationManager,
            @NonNull Handler handler,
            @NonNull PackageManagerTracedLock lock)  {
​
        ...
          
        mSystemDir = new File(dataDir, "system");
        mSystemDir.mkdirs();
        FileUtils.setPermissions(mSystemDir.toString(),
                FileUtils.S_IRWXU|FileUtils.S_IRWXG
                |FileUtils.S_IROTH|FileUtils.S_IXOTH,
                -1, -1);
        mSettingsFilename = new File(mSystemDir, "packages.xml");
        mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
        mPackageListFilename = new File(mSystemDir, "packages.list");
        FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
​
        final File kernelDir = new File("/config/sdcardfs");
        mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;
​
        // Deprecated: Needed for migration
        mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
        mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
        ...
    }

Settings中保存了安装包信息,packages-stopped.xml标记为废弃不关注,主要文件就是/data/system/下的packages.xml,packages-backup.xml,packages.list,packages-backup作为备份文件,其他两个文件的作用是

  • packages.xml: PKMS 扫描完目标文件夹后会创建该文件。当系统进行程序安装、卸载和更新等操作时,均会更新该文件。该文件保存了系统中与 package 相关的一些信息。
  • packages.list:描述系统中存在的所有非系统自带的 APK 的信息。当这些程序有变动时,PKMS 就会更新该文件。
//frameworks/base/services/core/java/com/android/server/pm/Settings.java
 boolean readLPw(@NonNull Computer computer, @NonNull List<UserInfo> users) {
        FileInputStream str = null;
        final ArrayMap<String, Long> originalFirstInstallTimes = new ArrayMap<>();
​
        try {
            if (str == null) {
                if (!mSettingsFilename.exists()) {//如果不存在就返回false,也就是第一次创建
                    mReadMessages.append("No settings file found\n");
                    PackageManagerService.reportSettingsProblem(Log.INFO,
                            "No settings file; creating initial state");
                    // It's enough to just touch version details to create them
                    // with default values
                    findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
                    findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();
                    return false;
                }
                str = new FileInputStream(mSettingsFilename);
            }
            final TypedXmlPullParser parser = Xml.resolvePullParser(str);
          //...如果存在这些xml文件就通过TypedXmlPullParser解析这些xml文件,保存在Settings中
          
​
        return true;
    }

BOOT_PROGRESS_PMS_SYSTEM_SCAN_START

EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
                    startTime);
          //获取bootClassPath和systemServerClassPath下的环境变量
            final String bootClassPath = System.getenv("BOOTCLASSPATH");
            final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
​
            final VersionInfo ver = mSettings.getInternalVersion();
            //判断是否升级了
            mIsUpgrade =
                    !buildFingerprint.equals(ver.fingerprint);
            if (mIsUpgrade) {
                PackageManagerServiceUtils.logCriticalInfo(Log.INFO, "Upgrading from "
                        + ver.fingerprint + " to " + PackagePartitions.FINGERPRINT);
            }
        //初始化app的类
            mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper,
                mInjector.getSystemPartitions());
​
          //如果是android M升级上来的需要修改为运行时权限
            mPromoteSystemApps =
                    mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
​
        // 对于Android N之前版本升级上来的情况,需像首次启动一样处理package
            mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;
            mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
            mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q;
​
            final WatchedArrayMap<String, PackageSetting> packageSettings =
                mSettings.getPackagesLocked();
​
            //扫描之前就存在的系统package的名称,这样就可以确定哪些更新的
            if (isDeviceUpgrading()) {
                mExistingPackages = new ArraySet<>(packageSettings.size());
                for (PackageSetting ps : packageSettings.values()) {
                    mExistingPackages.add(ps.getPackageName());
                }
            }
            //获取缓存目录
            mCacheDir = PackageManagerServiceUtils.preparePackageParserCache(
                    mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion);
​
            final int[] userIds = mUserManager.getUserIds();
            //获取PackageParser2
            PackageParser2 packageParser = mInjector.getScanningCachingPackageParser();
            //扫描系统app
            mOverlayConfig = mInitAppsHelper.initSystemApps(packageParser, packageSettings, userIds,startTime);
            //系统app以外的app
            mInitAppsHelper.initNonSystemApps(packageParser, userIds, startTime);
            packageParser.close();
​
            // Resolve the storage manager.
            mStorageManagerPackage = getStorageManagerPackageName(computer);
​
            // Resolve protected action filters. Only the setup wizard is allowed to
            // have a high priority filter for these actions.
            //只允许安装向导这个应用拥有高级别的action
            mSetupWizardPackage = getSetupWizardPackageNameImpl(computer);
            mComponentResolver.fixProtectedFilterPriorities(mSetupWizardPackage);
          //获取了几个系统默认应用的包名
            mDefaultTextClassifierPackage = ensureSystemPackageName(computer,
                    mContext.getString(R.string.config_servicesExtensionPackage));
            mSystemTextClassifierPackageName = ensureSystemPackageName(computer,
                    mContext.getString(R.string.config_defaultTextClassifierPackage));
            mConfiguratorPackage = ensureSystemPackageName(computer,
                    mContext.getString(R.string.config_deviceConfiguratorPackageName));
            mAppPredictionServicePackage = ensureSystemPackageName(computer,
                    getPackageFromComponentString(R.string.config_defaultAppPredictionService));
            mIncidentReportApproverPackage = ensureSystemPackageName(computer,
                    mContext.getString(R.string.config_incidentReportApproverPackage));
            mRetailDemoPackage = getRetailDemoPackageName();
            mOverlayConfigSignaturePackage = ensureSystemPackageName(computer,
                    mInjector.getSystemConfig().getOverlayConfigSignaturePackage());
            mRecentsPackage = ensureSystemPackageName(computer,
                    getPackageFromComponentString(R.string.config_recentsComponentName));
            mAmbientContextDetectionPackage = ensureSystemPackageName(computer,
                    getPackageFromComponentString(
                            R.string.config_defaultAmbientContextDetectionService));
​
            // Now that we know all of the shared libraries, update all clients to have
            // the correct library paths.
            // 更新客户端以确保持有正确的共享库路径
            mSharedLibraries.updateAllSharedLibrariesLPw(
                    null, null, Collections.unmodifiableMap(mPackages));
​
            for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
                // NOTE: We ignore potential failures here during a system scan (like
                // the rest of the commands above) because there's precious little we
                // can do about it. A settings error is reported, though.
                //如果升级导致abi发生了变化,那么就卸载原来api的代码
                final List<String> changedAbiCodePath =
                        ScanPackageUtils.applyAdjustedAbiToSharedUser(
                                setting, null /*scannedPackage*/,
                                mInjector.getAbiHelper().getAdjustedAbiForSharedUser(
                                        setting.getPackageStates(), null /*scannedPackage*/));
                if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
                    for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
                        final String codePathString = changedAbiCodePath.get(i);
                        try {
                            mInstaller.rmdex(codePathString,
                                    getDexCodeInstructionSet(getPreferredInstructionSet()));
                        } catch (InstallerException ignored) {
                        }
                    }
                }
                // Adjust seInfo to ensure apps which share a sharedUserId are placed in the same
                // SELinux domain.
                setting.fixSeInfoLocked();
                setting.updateProcesses();
            }
​
            // Now that we know all the packages we are keeping,
            // read and update their last usage times.
          // 读取并更新保留的package的上次使用时间
            mPackageUsage.read(packageSettings);
            mCompilerStats.read();

这里最重要的就是通过mInitAppsHelper获取了系统apk和其他的apk的信息。

//frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java
 public OverlayConfig initSystemApps(PackageParser2 packageParser,
            WatchedArrayMap<String, PackageSetting> packageSettings,
            int[] userIds, long startTime) {
        ...
          
        scanSystemDirs(packageParser, mExecutorService);
        // Parse overlay configuration files to set default enable state, mutability, and
        // priority of system overlays.
        final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
        for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
            for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
                apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
            }
        }
       ....
      
        return overlayConfig;
    }

    private void scanSystemDirs(PackageParser2 packageParser, ExecutorService executorService) {
      ///system/framework路径下
        File frameworkDir = new File(Environment.getRootDirectory(), "framework");
​
        scanDirTracedLI(frameworkDir, null,
                mSystemParseFlags,
                mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
                packageParser, executorService);
        //....其他系统app路径也一样就不展示了
    }

scanSystemDirs中就是通过scanDirTracedLI扫描各种路径下的apk。

      private void scanDirTracedLI(File scanDir, List<File> frameworkSplits,
            int parseFlags, int scanFlags,
            PackageParser2 packageParser, ExecutorService executorService) {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
        try {
            if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
                // when scanning apk in apexes, we want to check the maxSdkVersion
                parseFlags |= PARSE_APK_IN_APEX;
            }
            mInstallPackageHelper.installPackagesFromDir(scanDir, frameworkSplits, parseFlags,
                    scanFlags, packageParser, executorService);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

scanDirTracedLI调用了InstallPackageHelper.installPackagesFromDir来安装

//frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java 
public void installPackagesFromDir(File scanDir, List<File> frameworkSplits, int parseFlags,
            int scanFlags, PackageParser2 packageParser,
            ExecutorService executorService) {
        final File[] files = scanDir.listFiles();
       
        ParallelPackageParser parallelPackageParser =
                new ParallelPackageParser(packageParser, executorService, frameworkSplits);
​
        // Submit files for parsing in parallel
        int fileCount = 0;
        for (File file : files) {
            final boolean isPackage = (isApkFile(file) || file.isDirectory())
                    && !PackageInstallerService.isStageName(file.getName());
            if (!isPackage) {
                // Ignore entries which are not packages
                continue;
            }
            if ((scanFlags & SCAN_DROP_CACHE) != 0) {
                final PackageCacher cacher = new PackageCacher(mPm.getCacheDir());
                Log.w(TAG, "Dropping cache of " + file.getAbsolutePath());
                cacher.cleanCachedResult(file);
            }
            parallelPackageParser.submit(file, parseFlags);
            fileCount++;
        }
​
        // Process results one by one
        for (; fileCount > 0; fileCount--) {
            ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
            Throwable throwable = parseResult.throwable;
            int errorCode = PackageManager.INSTALL_SUCCEEDED;
            String errorMsg = null;
​
            if (throwable == null) {
                // TODO(b/194319951): move lower in the scan chain
                // Static shared libraries have synthetic package names
                if (parseResult.parsedPackage.isStaticSharedLibrary()) {
                    PackageManagerService.renameStaticSharedLibraryPackage(
                            parseResult.parsedPackage);
                }
                try {
                    addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
                            null);
                } catch (PackageManagerException e) {
                    errorCode = e.error;
                    errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
                    Slog.w(TAG, errorMsg);
                }
            } else if (throwable instanceof PackageManagerException) {
                PackageManagerException e = (PackageManagerException) throwable;
                errorCode = e.error;
                errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage();
                Slog.w(TAG, errorMsg);
            } else {
                throw new IllegalStateException("Unexpected exception occurred while parsing "
                        + parseResult.scanFile, throwable);
            }
​
          ...
            
        }
    }

通过ParallelPackageParser.submit提交安装任务,ParallelPackageParser.submit.take获取任务结果。

//frameworks/base/services/core/java/com/android/server/pm/ParallelPackageParser.java
public void submit(File scanFile, int parseFlags) {
        mExecutorService.submit(() -> {
            ParseResult pr = new ParseResult();
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
            try {
                pr.scanFile = scanFile;
                pr.parsedPackage = parsePackage(scanFile, parseFlags);
            } catch (Throwable e) {
                pr.throwable = e;
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
            try {
                mQueue.put(pr);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                mInterruptedInThread = Thread.currentThread().getName();
            }
        });
    }
 public ParseResult take() {
        try {
            if (mInterruptedInThread != null) {
                throw new InterruptedException("Interrupted in " + mInterruptedInThread);
            }
            return mQueue.take();
        } catch (InterruptedException e) {
            // We cannot recover from interrupt here
            Thread.currentThread().interrupt();
            throw new IllegalStateException(e);
        }
    }

submit和take就是通过一个 BlockingQueue实现解析和获取任务。解析是parsePackage来实现的。

    protected ParsedPackage parsePackage(File scanFile, int parseFlags)
            throws PackageManagerException {
        return mPackageParser.parsePackage(scanFile, parseFlags, true, mFrameworkSplits);
    }

这里的mPackageParser就是前面的参数PackageParser2

//frameworks/base/services/core/java/com/android/server/pm/parsing/PackageParser2.java
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches,
            List<File> frameworkSplits) throws PackageManagerException {
   //判断缓存是否存在,存在就直接使用缓存
        if (useCaches && mCacher != null) {
            ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags);
            if (parsed != null) {
                return parsed;
            }
        }
​
        long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
        ParseInput input = mSharedResult.get().reset();
      //解析apk文件
        ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags,
                frameworkSplits);
        ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();
        long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
   //添加到缓存
        if (mCacher != null) {
            mCacher.cacheResult(packageFile, flags, parsed);
        }
        return parsed;
    }

又到了parsingUtils.parsePackage

//frameworks/base/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
  public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags,
            List<File> frameworkSplits) {
        if (((flags & PARSE_FRAMEWORK_RES_SPLITS) != 0)
                && frameworkSplits.size() > 0
                && packageFile.getAbsolutePath().endsWith("/framework-res.apk")) {
            return parseClusterPackage(input, packageFile, frameworkSplits, flags);
        } else if (packageFile.isDirectory()) {//是一个目录就使用parseClusterPackage
            return parseClusterPackage(input, packageFile, /* frameworkSplits= */null, flags);
        } else {//是单独一个apk就使用parseMonolithicPackage
            return parseMonolithicPackage(input, packageFile, flags);
        }
    }

终于到了解析看下简单的parseMonolithicPackage

  private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
            int flags) {
    //先通过parseMonolithicPackageLite初步解析这个apk,这里会解析出minsdkversion,versionCode 这些基本信息
        final ParseResult<PackageLite> liteResult =
                ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags);
        if (liteResult.isError()) {
            return input.error(liteResult);
        }
​
        final PackageLite lite = liteResult.getResult();
        if (mOnlyCoreApps && !lite.isCoreApp()) {
            return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
                    "Not a coreApp: " + apkFile);
        }
        //AssetManager和文件信息的封装,用于后面使用AssetManager加载资源
        final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
        try {
          //真正解析apk
            final ParseResult<ParsingPackage> result = parseBaseApk(input,
                    apkFile,
                    apkFile.getCanonicalPath(),
                    assetLoader, flags);
            if (result.isError()) {
                return input.error(result);
            }
​
            return input.success(result.getResult()
                    .setUse32BitAbi(lite.isUse32bitAbi()));
        } catch (IOException e) {
            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Failed to get path: " + apkFile, e);
        } finally {
            IoUtils.closeQuietly(assetLoader);
        }
    }

parseMonolithicPackage先通过parseMonolithicPackageLite来初步处理apk,获取PackageLite。然后调用了parseBaseApk详细处理apk。

private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
            String codePath, SplitAssetLoader assetLoader, int flags) {
  //获取apk文件
        final String apkPath = apkFile.getAbsolutePath();
        final AssetManager assets;
        try {
            assets = assetLoader.getBaseAssetManager();
        } catch (IllegalArgumentException e) {
            return input.error(e.getCause() instanceof IOException ? INSTALL_FAILED_INVALID_APK
                    : INSTALL_PARSE_FAILED_NOT_APK, e.getMessage(), e);
        }
        final int cookie = assets.findCookieForPath(apkPath);
      //创建xml解析对象
        try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
                ANDROID_MANIFEST_FILENAME)) {
            final Resources res = new Resources(assets, mDisplayMetrics, null);
​
            ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
                    parser, flags);
​
            final ParsingPackage pkg = result.getResult();
          
           ....
​
            return input.success(pkg);
        } catch (Exception e) {
            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Failed to read manifest from " + apkPath, e);
        }
    }
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
            String codePath, Resources res, XmlResourceParser parser, int flags)
            throws XmlPullParserException, IOException {
            
            ...
              
            final ParseResult<ParsingPackage> result =
                    parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
            
            ...
            
    }

层层调用到了parseBaseApkTags来解析androidmanifest中的各个标签。

 private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
            TypedArray sa, Resources res, XmlResourceParser parser, int flags)
            throws XmlPullParserException, IOException {
          
          .....
            
        boolean foundApp = false;
        final int depth = parser.getDepth();
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG
                || parser.getDepth() > depth)) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }
​
            String tagName = parser.getName();
            final ParseResult result;
​
            // <application> has special logic, so it's handled outside the general method
            if (TAG_APPLICATION.equals(tagName)) {
                if (foundApp) {
                    if (RIGID_PARSER) {
                        result = input.error("<manifest> has more than one <application>");
                    } else {
                        Slog.w(TAG, "<manifest> has more than one <application>");
                        result = input.success(null);
                    }
                } else {
                    foundApp = true;
                    result = parseBaseApplication(input, pkg, res, parser, flags);
                }
            } else {
                result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
            }
          
        .....
          
        return input.success(pkg);
    }

如果是application标签,那么就调用parseBaseApplication方法,因为四大组件等都在这个标签下面,其他通parseBaseApkTag解析。

    private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
            throws XmlPullParserException, IOException {
        .....
        
       while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG
                || parser.getDepth() > depth)) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }
​
            final ParseResult result;
            String tagName = parser.getName();
            boolean isActivity = false;
            switch (tagName) {
                case "activity":
                    isActivity = true;
                    // fall-through
                case "receiver":
                    ParseResult<ParsedActivity> activityResult =
                            ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
                                    res, parser, flags, sUseRoundIcon, null /*defaultSplitName*/,
                                    input);
​
                    if (activityResult.isSuccess()) {
                        ParsedActivity activity = activityResult.getResult();
                        if (isActivity) {
                            hasActivityOrder |= (activity.getOrder() != 0);
                            pkg.addActivity(activity);
                        } else {
                            hasReceiverOrder |= (activity.getOrder() != 0);
                            pkg.addReceiver(activity);
                        }
                    }
​
                    result = activityResult;
                    break;
               
                .....
                  
    }

这里就通过解析了标签,根据标签调用不同的解析方法。四大组件的信息PMS就都获取到了。
BOOT_PROGRESS_PMS_SCAN_END

 EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                    SystemClock.uptimeMillis()); 
mPermissionManager.onStorageVolumeMounted(
                    StorageManager.UUID_PRIVATE_INTERNAL, mIsUpgrade);
            ver.sdkVersion = mSdkVersion;
​
            // If this is the first boot or an update from pre-M, and it is a normal
            // boot, then we need to initialize the default preferred apps across
            // all defined users.
            //如果是从M升级或者第一次启动,初始化默认应用
            if (!mOnlyCore && (mPromoteSystemApps || mFirstBoot)) {
                for (UserInfo user : mInjector.getUserManagerInternal().getUsers(true)) {
                    mSettings.applyDefaultPreferredAppsLPw(user.id);
                }
            }
​
            //如果是第一次OTA升级之后的启动,那就删除代码缓存的目录
            if (mIsUpgrade && !mOnlyCore) {
                Slog.i(TAG, "Build fingerprint changed; clearing code caches");
                for (int i = 0; i < packageSettings.size(); i++) {
                    final PackageSetting ps = packageSettings.valueAt(i);
                    if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.getVolumeUuid())) {
                        // No apps are running this early, so no need to freeze
                        mAppDataHelper.clearAppDataLIF(ps.getPkg(), UserHandle.USER_ALL,
                                FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
                                        | Installer.FLAG_CLEAR_CODE_CACHE_ONLY
                                        | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
                    }
                }
                ver.fingerprint = PackagePartitions.FINGERPRINT;
            }
​
            // Defer the app data fixup until we are done with app data clearing above.
            mPrepareAppDataFuture = mAppDataHelper.fixAppsDataOnBoot();
​
            // Legacy existing (installed before Q) non-system apps to hide
            // their icons in launcher.
          //安装Q前的非系统应用程序在Launcher中隐藏他们的图标
            if (!mOnlyCore && mIsPreQUpgrade) {
                Slog.i(TAG, "Allowlisting all existing apps to hide their icons");
                int size = packageSettings.size();
                for (int i = 0; i < size; i++) {
                    final PackageSetting ps = packageSettings.valueAt(i);
                    if ((ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0) {
                        continue;
                    }
                    ps.disableComponentLPw(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME,
                            UserHandle.USER_SYSTEM);
                }
            }
​
            // clear only after permissions and other defaults have been updated
            mPromoteSystemApps = false;
​
            // All the changes are done during package scanning.
            ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;
​
            // can downgrade to reader
            t.traceBegin("write settings");
            writeSettingsLPrTEMP();
            t.traceEnd();

BOOT_PROGRESS_PMS_READY

EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                   SystemClock.uptimeMillis());
           ...
​
           //权限控制器
           mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr(computer);
           mSettings.setPermissionControllerVersion(
                   computer.getPackageInfo(mRequiredPermissionControllerPackage, 0,
                           UserHandle.USER_SYSTEM).getLongVersionCode());
​
           //指定系统中用于运行应用程序的 Sandbox 应用程序的包名,低于系统要求的最低版本就要运行在Sandbox              应用程序中
           mRequiredSdkSandboxPackage = getRequiredSdkSandboxPackageName(computer);
​
          ...
​
           final Map<Integer, List<PackageInfo>> userPackages = new HashMap<>();
           for (int userId : userIds) {
               userPackages.put(userId, computer.getInstalledPackages(/*flags*/ 0, userId)
                       .getList());
           }
           //通过mDexManager将用户已经安装的app的dex交给 mDexManager 进行处理
           mDexManager.load(userPackages);
           if (mIsUpgrade) {//如果是升级记录相关信息
               FrameworkStatsLog.write(
                       FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
                       BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME,
                       SystemClock.uptimeMillis() - startTime);
           }
​
           // Rebuild the live computer since some attributes have been rebuilt.
           mLiveComputer = createLiveComputer();
​
       } // synchronized (mLock)
       } // synchronized (mInstallLock)
       // CHECKSTYLE:ON IndentationCheck
​
       mModuleInfoProvider = mInjector.getModuleInfoProvider();
​
       mInjector.getSystemWrapper().enablePackageCaches();
       t.traceBegin("GC");
       //触发gc回收应用资源
       VMRuntime.getRuntime().requestConcurrentGC();
       t.traceEnd();
​
       //圆角资源
       ParsingPackageUtils.readConfigUseRoundIcon(mContext.getResources());
​
       mServiceStartWithDelay = SystemClock.uptimeMillis() + (60 * 1000L);

SystemServer.startOtherServices

//frameworks/base/services/java/com/android/server/SystemServer.java       
private void startOtherServices() {
    ...
    if (!mOnlyCore) {
        ...
        //如果不是只启动核心应用,查看是否更新
        mPackageManagerService.updatePackagesIfNeeded();
    }
    ...
    // 最终执行performFstrim,完成磁盘维护
    mPackageManagerService.performFstrimIfNeeded();
    ...
    //PKMS准备就绪
    mPackageManagerService.systemReady();
    ...
}

到这里PackageManagerService就初始化成功了。


PMS 是学习了解Framework底层原理的核心知识点之一,在Framework底层原理中除了WMS要学习以外,还有Binder、Handler、AMS、PMS……等知识点都要学习,这样才能全面的了解Framework底层原理,为了帮助大家可以更快速的掌握理解这个版块的知识点,这边整理了 《Framework 学习手册》,里面将其Framework中所有的知识点记录在册了,希望能给大家有效的帮助。

《Android Framework学习手册》:https://qr18.cn/AQpN4J

  1. 开机Init 进程
  2. 开机启动 Zygote 进程
  3. 开机启动 SystemServer 进程
  4. Binder 驱动
  5. AMS 的启动过程
  6. PMS 的启动过程
  7. Launcher 的启动过程
  8. Android 四大组件
  9. Android 系统服务 - Input 事件的分发过程
  10. Android 底层渲染 - 屏幕刷新机制源码分析
  11. Android 源码分析实战

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/417768.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023-04-09 有向图及相关算法

有向图及相关算法 1 有向图的实现 有向图的的应用场景 社交网络中的关注互联网连接程序模块的引用任务调度学习计划食物链论文引用无向图是特殊的有向图&#xff0c;即每条边都是双向的 改进Graph和WeightedGraph类使之支持有向图 Graph类的改动WeightedGraph类的改动 2 …

肖 sir_就业课__004项目流程(H模型)

项目流程&#xff1a; 一、面试提问&#xff08;h模型&#xff09; 1、你说下你们公司测试流程&#xff1f; 2、给你一个需求你会怎么做? 3、你讲下你的工作&#xff1f; 4、谈谈你是如何去测试&#xff1f; 答案&#xff1a;h模型 要求第一人称来写 讲解简化文字流程&#x…

基于Python实现的深度学习技术在水文水质领域应用

目录 一、深度学习的基本概念和发展现状 二、Python系列开源软件包基本用法 三、前馈神经网络模型、原理和代码实现 四、前馈神经网络模型在水质、水位预测中的应用 五、卷积神经网络模原理、结构和应用&#xff0c;与支持向量机进行比较 六、循环神经网络原理与结构 七…

web 页面在浏览器运行eval性能分析和优化

公司有个低代码老项目&#xff0c;里面有一些自定义脚本运行使用了大量的eval 动态运行。 分析 网上对eval 的争论也非常激烈&#xff0c;大部分不建议使用&#xff0c;一些人观点是用不好才导致问题。 eval 是否真的存在效率问题&#xff1f; eval is evil 我们知道new Fun…

定时任务-常用的cron表达式

常用cron表达式例子&#xff1a; &#xff08;1&#xff09;0/2 * * * * ? 表示每2秒 执行任务 &#xff08;1&#xff09;0 0/2 * * * ? 表示每2分钟 执行任务 &#xff08;1&#xff09;0 0 2 1 * ? 表示在每月的1日的凌晨2点调整任务 &#xff08;2&#xff09;0 15 1…

宝塔设置PHP定时任务实战记录(定时任务、ajax异步刷新API、shell脚本、访问url)

文章目录项目需求生产环境一、php定时任务二、实战开发1.创建mysql数据表2.入库封装函数&#xff08;1&#xff09;封装入库&#xff08;2&#xff09;入库操作3.定时任务的实现&#xff08;1&#xff09;$ajax异步调用法&#xff08;2&#xff09;宝塔定时访问url&#xff08;…

http请求头部(header)详解

通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息。这两种类型的消息由一个起始行&#xff0c;一个或者多个头域&#xff0c;一个只是头域结束的空行和可 选的消息体组成。HTTP的头域包括通用头&#xff0c;请求头&#xff0c;响应头和实体头四个部分。每个…

OpenShift 4 - 通过 SSH 远程访问 OpenShift Virtualization 的虚拟机

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在支持 OpenShift 4.12 的 OpenShift 环境中验证 在《OpenShift 4 - 用 OpenShift Virtualization 运行容器化虚拟机 &#xff08;视频&#xff09;》一文中使用了 OpenShift 控制台直接访问运行在 OpenSh…

【Python实战】从架构设计到实现:一个Powerful的图书管理系统

&#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是Zeeland&#xff0c;全栈领域优质创作者。&#x1f4dd; CSDN主页&#xff1a;Zeeland&#x1f525;&#x1f4e3; 我的博客&#xff1a;Zeeland&#x1f4da; Github主页: Undertone0809 (Zeeland) (github.com)&…

栈的压入,栈的弹出,最小栈,用队列实现栈,设计循环队列

栈的压入&#xff0c;栈的弹出 输入两个整数序列&#xff0c;第一个序列表示栈的压入序列&#xff0c;判断第二个序列是否可能是该栈的弹出序列&#xff0c;假设压入栈中的所有数字均不相等&#xff0c;如pushA:1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5是某栈的…

day11 有名管道和无名管道

无名管道基础 进程间的通信 概念&#xff1a; 进程间的通信就是进程和进程之间交换信息&#xff1b; 常用方式&#xff1a; 无名管道&#xff08;pipe&#xff09; 有名管道&#xff08;fifo&#xff09; 信号&#xff08;signal&#xff09; 共享内存&#xff08;mmap…

DOS批处理文件---内嵌参数变量扩充功能

1 内嵌参数变量 1.1 介绍 内嵌参数变量指&#xff1a;%i&#xff0c;其中i为大于等于0的整数。如%0&#xff0c; %1&#xff0c; %2&#xff0c;... 1.2 作用 可以客制化功能。 1.3 案例 批处理文件&#xff1a;main_Param.bat echo off && setlocal ENABLEDELA…

游戏分析之引擎的基本概念及常见的游戏引擎介绍

一、引擎的基本概念 关于游戏引擎和中间件 过去开发游戏的时候&#xff0c;游戏机生产商提供的只是硬件的规格书和非常基本的“库”。所以&#xff0c;游戏需要的各种程序功能都需要游戏开发团队自己开发。从PC&#xff0c;FC的8位机时代开始&#xff0c;到SFC&#xff0c;MD的…

Java经典笔试题—day01

Java经典笔试题—day01&#x1f50e;选择题&#x1f50e;编程题&#x1f95d;组队竞赛&#x1f95d;删除公共字符&#x1f50e;结尾&#x1f50e;选择题 (1) 在 Java 中&#xff0c;存放字符串常量的对象属于&#xff08; &#xff09;类对象。 A. Character B. String C. Str…

类ChatGPT项目的部署与微调(下):从ChatGLM-6b到ChatDoctor

前言 随着『GPT4多模态/Microsoft 365 Copilot/Github Copilot X/ChatGPT插件』的推出&#xff0c;绝大部分公司的技术 产品 服务&#xff0c;以及绝大部分人的工作都将被革新一遍 类似iPhone的诞生 大家面向iOS编程 有了App Store现在有了ChatGPT插件/GPT应用商店&#xff…

Faster-RCNN代码解读5:主要文件解读-上

Faster-RCNN代码解读5&#xff1a;主要文件解读-上 前言 ​ 因为最近打算尝试一下Faster-RCNN的复现&#xff0c;不要多想&#xff0c;我还没有厉害到可以一个人复现所有代码。所以&#xff0c;是参考别人的代码&#xff0c;进行自己的解读。 ​ 代码来自于B站的UP主&#xff…

算法模板(3):搜索(2):bfs与图论基础

bfs 在搜索题中&#xff0c;一般来讲&#xff0c;bfs和dfs都有一个最优选择。 基础bfs 走迷宫 注&#xff1a;这个模板具有还原路径的功能。其实&#xff0c;还可以反向搜&#xff08;从终点走到起点&#xff09;&#xff0c;就不用 reverse数组了。其实&#xff0c;bfs是不…

Qt Quick - Dialog

Dialog使用总结一、概述二、对话框标题和按钮三、模态对话框四、非模态的对话框五、standardButtons 属性一、概述 对话框是一种弹出式对话框&#xff0c;主要用于短期任务和与用户的简短交流。与ApplicationWindow和Page类似&#xff0c;Dialog被组织成三个部分:header、cont…

PyTorch深度学习实战 | 基于线性回归、决策树和SVM进行鸢尾花分类

鸢尾花数据集是机器学习领域非常经典的一个分类任务数据集。它的英文名称为Iris Data Set&#xff0c;使用sklearn库可以直接下载并导入该数据集。数据集总共包含150行数据&#xff0c;每一行数据由4个特征值及一个标签组成。标签为三种不同类别的鸢尾花&#xff0c;分别为&…

【AI帮我写代码,上班摸鱼不是梦】手摸手图解CodeWhisperer的安装使用

IDEA插件 除了借助ChatGPT通过问答的方式生成代码&#xff0c;也可以通过IDEA插件在写代码是直接帮助我们生成代码。 目前&#xff0c;IDEA插件有CodeGeeX、CodeWhisperer、Copilot。其中&#xff0c;CodeGeeX和CodeWhisperer是完全免费的&#xff0c;Copilot是收费的&#x…