PackageManagerService

五个阶段:

(1)BOOT_PROGRESS_PMS_START

(2)BOOT_PROGRESS_PMS_SYSTEM_SCAN_START

(3)BOOT_PROGRESS_PMS_DATA_SCAN_START

(4)BOOT_PROGRESS_PMS_SCAN_END

(5)BOOT_PROGRESS_PMS_READY

1)BOOT_PROGRESS_PMS_START

工作内容:创建所需的服务,本地服务,多用户管理,加载installer。然后将各种系统标志的uid写入settings中。注册权限改变监听器。

if (mSdkVersion <= 0) {Slog.w(TAG, "**** ro.build.version.sdk not set!");}Slog.w(TAG, " run in PackageManagerService");mContext = context;mFactoryTest = factoryTest;//工厂模式mOnlyCore = onlyCore;//加密模式mMetrics = new DisplayMetrics();//像素mInstaller = installer;//installer服务// Create sub-components that provide services / data. Order here is important.synchronized (mInstallLock) {synchronized (mPackages) {// Expose private service for system components to use.LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());//本地服务sUserManager = new UserManagerService(context, this,new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);//多用户管理mComponentResolver = new ComponentResolver(sUserManager,LocalServices.getService(PackageManagerInternal.class),mPackages);mPermissionManager = PermissionManagerService.create(context,mPackages /*externalLock*/);mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();//权限mSettings = new Settings(Environment.getDataDirectory(),mPermissionManager.getPermissionSettings(), mPackages);//settings环境}}mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);......mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);//将各种UID写入。。。String separateProcesses = SystemProperties.get("debug.separate_processes");if (separateProcesses != null && separateProcesses.length() > 0) {if ("*".equals(separateProcesses)) {mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;mSeparateProcesses = null;Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");} else {mDefParseFlags = 0;mSeparateProcesses = separateProcesses.split(",");Slog.w(TAG, "Running with debug.separate_processes: "+ separateProcesses);}} else {mDefParseFlags = 0;//走这mSeparateProcesses = null;}mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,"*dexopt*");mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock);mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());mViewCompiler = new ViewCompiler(mInstallLock, mInstaller);mOnPermissionChangeListeners = new OnPermissionChangeListeners(FgThread.get().getLooper());//权限监听getDefaultDisplayMetrics(context, mMetrics);//获取默认的显示

packages-backup.xml是否存在。存在,检查packages.xml是否存在,删除packages.xml,然后清除xml的list,检查packages-backup.xml的句柄是否存在,存在删除packages.xml,然后创建,将packages-backup.xml 复制到packages.xml,解析xml文件的内容

Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "get system config");SystemConfig systemConfig = SystemConfig.getInstance();mAvailableFeatures = systemConfig.getAvailableFeatures();//。。。。Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);mProtectedPackages = new ProtectedPackages(mContext);mApexManager = new ApexManager(context);synchronized (mInstallLock) {// writersynchronized (mPackages) {mHandlerThread = new ServiceThread(TAG,Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);mHandlerThread.start();mHandler = new PackageHandler(mHandlerThread.getLooper());mProcessLoggingHandler = new ProcessLoggingHandler();Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);mInstantAppRegistry = new InstantAppRegistry(this);ArrayMap<String, SystemConfig.SharedLibraryEntry> libConfig= systemConfig.getSharedLibraries();//加载共享lib文件final int builtInLibCount = libConfig.size();for (int i = 0; i < builtInLibCount; i++) {String name = libConfig.keyAt(i);SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);addBuiltInSharedLibraryLocked(entry.filename, name);}// 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 =getSharedLibraryInfoLPr(entry.dependencies[j], undefinedVersion);if (dependency != null) {getSharedLibraryInfoLPr(name, undefinedVersion).addDependency(dependency);}}}SELinuxMMAC.readInstallPolicy();//读取安装权限位于vendor system product *permissions.xmlTrace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "loadFallbacks");FallbackCategoryProvider.loadFallbacks();Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "read user settings");mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));mSettings.readmPreInstalledAPKStatus();Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);// Clean up orphaned packages for which the code path doesn't exist// and they are an update to a system app - caused by bug/32321269final int packageSettingCount = mSettings.mPackages.size();for (int i = packageSettingCount - 1; i >= 0; i--) {PackageSetting ps = mSettings.mPackages.valueAt(i);if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists())&& mSettings.getDisabledSystemPkgLPr(ps.name) != null) {mSettings.mPackages.removeAt(i);mSettings.enableSystemPackageLPw(ps.name);}}if (!mOnlyCore && mFirstBoot) {requestCopyPreoptedFiles();}String customResolverActivityName = Resources.getSystem().getString(R.string.config_customResolverActivity);if (!TextUtils.isEmpty(customResolverActivityName)) {mCustomResolverComponentName = ComponentName.unflattenFromString(customResolverActivityName);}

2)BOOT_PROGRESS_PMS_SYSTEM_SCAN_START

工作内容:检查各种系统相关属性,环境变量,目录,sdk版本,准备解析器,然后扫描各个目录下apk,对androidManifest.xml进行解析。如果未进行加密,删除不存在的系统软件包,对disable列表的app进行处理。删除临时文件。

           EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,startTime);final String bootClassPath = System.getenv("BOOTCLASSPATH");final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");if (bootClassPath == null) {Slog.w(TAG, "No BOOTCLASSPATH found!");}if (systemServerClassPath == null) {Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");}File frameworkDir = new File(Environment.getRootDirectory(), "framework");final VersionInfo ver = mSettings.getInternalVersion();mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);if (mIsUpgrade) {logCriticalInfo(Log.INFO,"Upgrading from " + ver.fingerprint + " to " + Build.FINGERPRINT);}// when upgrading from pre-M, promote system app permissions from install to runtimemPromoteSystemApps =mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;// When upgrading from pre-N, we need to handle package extraction like first boot,// as there is no profiling data available.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;appList_Pre = mContext.getResources().getStringArray(com.android.internal.R.array.PreInstalled_Apks);int preUpgradeSdkVersion = ver.sdkVersion;// save off the names of pre-existing system packages prior to scanning; we don't// want to automatically grant runtime permissions for new system appsif (mPromoteSystemApps) {Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();while (pkgSettingIter.hasNext()) {PackageSetting ps = pkgSettingIter.next();if (isSystemApp(ps)) {mExistingSystemPackages.add(ps.name);}}}mCacheDir = preparePackageParserCache();// Set flag to monitor and not change apk file paths when// scanning install directories.int scanFlags = SCAN_BOOTING | SCAN_INITIAL;if (mIsUpgrade || mFirstBoot) {scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;}// Collect vendor/product/product_services overlay packages. (Do this before scanning// any apps.)// For security and version matching reason, only consider overlay packages if they// reside in the right directory.scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),mDefParseFlags| PackageParser.PARSE_IS_SYSTEM_DIR,scanFlags| SCAN_AS_SYSTEM| SCAN_AS_VENDOR,0);......scanDirTracedLI(productServicesAppDir,mDefParseFlags| PackageParser.PARSE_IS_SYSTEM_DIR,scanFlags| SCAN_AS_SYSTEM| SCAN_AS_PRODUCT_SERVICES,0);// Prune any system packages that no longer exist.final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();// Stub packages must either be replaced with full versions in the /data// partition or be disabled.final List<String> stubSystemApps = new ArrayList<>();if (!mOnlyCore) {// do this first before mucking with mPackages for the "expecting better" casefinal Iterator<PackageParser.Package> pkgIterator = mPackages.values().iterator();while (pkgIterator.hasNext()) {final PackageParser.Package pkg = pkgIterator.next();if (pkg.isStub) {stubSystemApps.add(pkg.packageName);}}final Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();while (psit.hasNext()) {PackageSetting ps = psit.next();/** If this is not a system app, it can't be a* disable system app.*/if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {continue;}/** If the package is scanned, it's not erased.*/final PackageParser.Package scannedPkg = mPackages.get(ps.name);if (scannedPkg != null) {/** If the system app is both scanned and in the* disabled packages list, then it must have been* added via OTA. Remove it from the currently* scanned package so the previously user-installed* application can be scanned.*/if (mSettings.isDisabledSystemPackageLPr(ps.name)) {logCriticalInfo(Log.WARN,"Expecting better updated system app for " + ps.name+ "; removing system app.  Last known"+ " codePath=" + ps.codePathString+ ", versionCode=" + ps.versionCode+ "; scanned versionCode=" + scannedPkg.getLongVersionCode());removePackageLI(scannedPkg, true);mExpectingBetter.put(ps.name, ps.codePath);}continue;}if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {psit.remove();logCriticalInfo(Log.WARN, "System package " + ps.name+ " no longer exists; it's data will be wiped");// Actual deletion of code and data will be handled by later// reconciliation step} else {// we still have a disabled system package, but, it still might have// been removed. check the code path still exists and check there's// still a package. the latter can happen if an OTA keeps the same// code path, but, changes the package name.final PackageSetting disabledPs =mSettings.getDisabledSystemPkgLPr(ps.name);if (disabledPs.codePath == null || !disabledPs.codePath.exists()|| disabledPs.pkg == null) {possiblyDeletedUpdatedSystemApps.add(ps.name);} else {// We're expecting that the system app should remain disabled, but add// it to expecting better to recover in case the data version cannot// be scanned.mExpectingBetter.put(disabledPs.name, disabledPs.codePath);}}}}//delete tmp filesdeleteTempPackageFiles();final int cachedSystemApps = PackageParser.sCachedPackageReadCount.get();// Remove any shared userIDs that have no associated packagesmSettings.pruneSharedUsersLPw();final long systemScanTime = SystemClock.uptimeMillis() - startTime;final int systemPackagesCount = mPackages.size();Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime+ " ms, packageCount: " + systemPackagesCount+ " , timePerPackage: "+ (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)+ " , cached: " + cachedSystemApps);if (mIsUpgrade && systemPackagesCount > 0) {MetricsLogger.histogram(null, "ota_package_manager_system_app_avg_scan_time",((int) systemScanTime) / systemPackagesCount);}

3)BOOT_PROGRESS_PMS_DATA_SCAN_START

工作内容:当处于普通模式时,对data目录进行处理,

           if (!mOnlyCore) {EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,SystemClock.uptimeMillis());scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);// Remove disable package settings for updated system apps that were// removed via an OTA. If the update is no longer present, remove the// app completely. Otherwise, revoke their system privileges.//删除禁止列表apk的数据for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {final String packageName = possiblyDeletedUpdatedSystemApps.get(i);final PackageParser.Package pkg = mPackages.get(packageName);final String msg;// remove from the disabled system list; do this first so any future// scans of this package are performed without this statemSettings.removeDisabledSystemPackageLPw(packageName);if (pkg == null) {// should have found an update, but, we didn't; remove everythingmsg = "Updated system package " + packageName+ " no longer exists; removing its data";// Actual deletion of code and data will be handled by later// reconciliation step} else {// found an update; revoke system privilegesmsg = "Updated system package " + packageName+ " no longer exists; rescanning package on data";// NOTE: We don't do anything special if a stub is removed from the// system image. But, if we were [like removing the uncompressed// version from the /data partition], this is where it'd be done.// remove the package from the system and re-scan it without any// special privilegesremovePackageLI(pkg, true);try {final File codePath = new File(pkg.applicationInfo.getCodePath());scanPackageTracedLI(codePath, 0, scanFlags, 0, null);} catch (PackageManagerException e) {Slog.e(TAG, "Failed to parse updated, ex-system package: "+ e.getMessage());}}// one final check. if we still have a package setting [ie. it was// previously scanned and known to the system], but, we don't have// a package [ie. there was an error scanning it from the /data// partition], completely remove the package data.final PackageSetting ps = mSettings.mPackages.get(packageName);if (ps != null && mPackages.get(packageName) == null) {removePackageDataLIF(ps, null, null, 0, false);}logCriticalInfo(Log.WARN, msg);}/** Make sure all system apps that we expected to appear on* the userdata partition actually showed up. If they never* appeared, crawl back and revive the system version.*/for (int i = 0; i < mExpectingBetter.size(); i++) {final String packageName = mExpectingBetter.keyAt(i);if (!mPackages.containsKey(packageName)) {final File scanFile = mExpectingBetter.valueAt(i);logCriticalInfo(Log.WARN, "Expected better " + packageName+ " but never showed up; reverting to system");final @ParseFlags int reparseFlags;final @ScanFlags int rescanFlags;if (FileUtils.contains(privilegedAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM| SCAN_AS_PRIVILEGED;} else if (FileUtils.contains(systemAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM;} else if (FileUtils.contains(privilegedVendorAppDir, scanFile)|| FileUtils.contains(privilegedOdmAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM| SCAN_AS_VENDOR| SCAN_AS_PRIVILEGED;} else if (FileUtils.contains(vendorAppDir, scanFile)|| FileUtils.contains(odmAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags//| SCAN_AS_SYSTEM| SCAN_AS_VENDOR;} else if (FileUtils.contains(oemAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM| SCAN_AS_OEM;} else if (FileUtils.contains(privilegedProductAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM| SCAN_AS_PRODUCT| SCAN_AS_PRIVILEGED;} else if (FileUtils.contains(productAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM| SCAN_AS_PRODUCT;} else if (FileUtils.contains(privilegedProductServicesAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM| SCAN_AS_PRODUCT_SERVICES| SCAN_AS_PRIVILEGED;} else if (FileUtils.contains(productServicesAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM| SCAN_AS_PRODUCT_SERVICES;} else {Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);continue;}mSettings.enableSystemPackageLPw(packageName);try {scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);} catch (PackageManagerException e) {Slog.e(TAG, "Failed to parse original system package: "+ e.getMessage());}}}// Uncompress and install any stubbed system applications.// This must be done last to ensure all stubs are replaced or disabled.installSystemStubPackages(stubSystemApps, scanFlags);final int cachedNonSystemApps = PackageParser.sCachedPackageReadCount.get()- cachedSystemApps;final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;final int dataPackagesCount = mPackages.size() - systemPackagesCount;Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime+ " ms, packageCount: " + dataPackagesCount+ " , timePerPackage: "+ (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)+ " , cached: " + cachedNonSystemApps);if (mIsUpgrade && dataPackagesCount > 0) {MetricsLogger.histogram(null, "ota_package_manager_data_app_avg_scan_time",((int) dataScanTime) / dataPackagesCount);}}mExpectingBetter.clear();

4)BOOT_PROGRESS_PMS_SCAN_END

PackageManagerService相关推荐

  1. PackageManagerService详解

    2019独角兽企业重金招聘Python工程师标准>>> 本篇主要分析了系统启动阶段包管理服务的启动流程,其中的几个接口在 apk 安装时也会被调用.包管理服务启动时主要做的工作大致有 ...

  2. 安卓高手之路之PackageManagerservice

    源码位置:frameworks/base/core/java/android/content/pm/PackageParser.java 源文件路径:android\frameworks\base\s ...

  3. Android 10.0 PackageManagerService(三)APK扫描-[Android取经之路]

    摘要:上一节讲解了PKMS的 权限扫描,扫描/system/etc/permissions中的xml,存入相应的结构体中,供之后权限管理使用. 这一节主要来讲讲APK的扫描. 阅读本文大约需要花费15 ...

  4. Android 10.0 PackageManagerService(二)权限扫描-[Android取经之路]

    摘要:PackageManagerService在systemReady()后,进行了/system/etc/permissions中的各种xml进行扫描,进行相应的权限存储,供以后使用 阅读本文大约 ...

  5. Android 10.0 PackageManagerService(一)工作原理及启动流程-[Android取经之路]

    摘要:PackageManagerService是Android系统核心服务之一,在Android中的非常重要,主要负责APK.jar包等的管理. 阅读本文大约需要花费50分钟. 文章的内容主要还是从 ...

  6. android7.1 动态申请权限改为默认授权,修改PackageManagerService.java下的grantPermissions为true

    系统默认情况下,如果需要访问external storage.audio record权限,需要动态申请,对话框举例如下: 如果不需要弹出此对话框来手动授予权限,而是默认授权,可修改framework ...

  7. Android system server之PackageManagerService详细分析

    概要 本篇主要分析了系统启动阶段包管理服务的启动流程,其中的几个接口在 apk 安装时也会被调用.包管理服务启动时主要做的工作大致有如下几方面: 1. 建立 java 层的 installer 与 c ...

  8. PackageManagerService启动过程

    1.PackageManagerService服务启动过程:[Android的所有Java服务都是通过SystemServer进程启动的,并且驻留在SystemServer进程中] public fi ...

  9. Android PackageManagerService分析三:卸载APK

    为什么80%的码农都做不了架构师?>>>    这一章我们介绍APK的卸载过程,从前一章分析安装APK的过程,我们应该大致了解这里的卸载的过程如下: 1.从PMS的内部结构上删除ac ...

  10. PMS(PackageManagerService)原理简单介绍,启动过程源码简单解析

    文章目录 前言 1. PMS 2. 源码和关键方法 SystemServer PackageManagerService ParallelPackageParser PackageParser 3. ...

最新文章

  1. 如何成为android开发工程师,android开发工程师薪资 如何成为一名合格的android开发工程师?...
  2. MCtalk对话学霸君:在线教育“1对多小班化”是个伪命题
  3. sql select...for update是锁行还是锁表
  4. 如何在当前目录快速打开cmd
  5. VC2008 ATL控件 去掉运行库依赖
  6. 阅读《C陷阱与缺陷》的知识增量
  7. 不同笔记本电脑BIOS的进入方法:
  8. Python学习教程(Python学习路线):Day13-进程和线程
  9. VS2012下基于Glut glRotatef glTranslatef示例程序:
  10. 车牌识别算法及其MATLAB实现
  11. 搜索引擎的基本工作原理
  12. m1136能支持哪些服务器,实测惠普M1136无线一体机,成就精英效率!
  13. 冰桶大作战怎么玩_开源领导者接受冰桶挑战
  14. Dalvik与ART的介绍及区别(一)
  15. Dubbo——服务暴露过程分析
  16. 服务器运维事项,云服务器的运维工作要注意的事项
  17. graythresh
  18. 斜率、弧度、角度的转换
  19. python中实现微信登录
  20. 判断用户输入的数是正数还是负数

热门文章

  1. sgsn与ggsn的区别与联系
  2. GGSN - SCP 业务控制点
  3. LoadLibraryEx(DONT_RESOLVE_DLL_REFERENCES)的缺陷
  4. python爬虫selenium和bs4_python爬虫――selenium+bs4爬取选股宝‘利好‘or’利空'股票信息...
  5. 机器学习和数据挖掘(7):VC维
  6. html网页肯德基设计代码作业,AI快速制作一幅肯德基广告单页教程
  7. uIP TCP Server 运行机制分析
  8. ADOConnection 打开EXCEL
  9. 【PTA 7-9】剥洋葱
  10. EEPROM,NAND,NOR,QSPI FLASH的区别