Android13 PMS安装浅析

为什么= =

在网上找了很久PMS的流程,原本想偷个懒借鉴一下他人的成果,结果找到的都是之前版本的代码,那么就自己写一篇叭T T

启动

首先还是来到我们的熟悉的老朋友systemserver类中,我们搜索PackageManagerService可以搜到这样几个方法

 Pair<PackageManagerService, IPackageManager> pmsPair = PackageManagerService.main(
                    mSystemContext, installer, domainVerificationService,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
            mPackageManagerService = pmsPair.first;
		......
mPackageManagerService.onAmsAddedtoServiceMgr();
		......
mPackageManagerService.updatePackagesIfNeeded();
		......
mPackageManagerService.performFstrimIfNeeded();
		......
mPackageManagerService.systemReady();
		......
mPackageManagerService.waitForAppDataPrepared();	

PMS自己本身的方法调用大概就是这么多,那么关于它的启动我们首先来看到第一个方法,它调用了PMS的main函数,所以很明显这里应该就是他的入口

PMS#main

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 backgroundThread = new ServiceThread("PackageManagerBg",
                Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
        backgroundThread.start();
        Handler backgroundHandler = new Handler(backgroundThread.getLooper());

        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!");
        }
		//创建PMS,结束trace跟踪
        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"


		//selinux权限变更监听器
        final CompatChange.ChangeListener selinuxChangeListener = packageName -> {
            synchronized (m.mInstallLock) {
                final Computer snapshot = m.snapshotComputer();
                final PackageStateInternal packageState =
                        snapshot.getPackageStateInternal(packageName);
                if (packageState == null) {
                    Slog.e(TAG, "Failed to find package setting " + packageName);
                    return;
                }
                AndroidPackage pkg = packageState.getPkg();
                SharedUserApi sharedUser = snapshot.getSharedUser(
                        packageState.getSharedUserAppId());
                String oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, packageState);

                if (pkg == null) {
                    Slog.e(TAG, "Failed to find package " + packageName);
                    return;
                }
                final String newSeInfo = SELinuxMMAC.getSeInfo(pkg, sharedUser,
                        m.mInjector.getCompatibility());

                if (!newSeInfo.equals(oldSeInfo)) {
                    Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "
                            + oldSeInfo + " to: " + newSeInfo);
                    m.commitPackageStateMutation(null, packageName,
                            state -> state.setOverrideSeInfo(newSeInfo));
                    m.mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
                }
            }
        };
		
		//selinux权限相关
        injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_LATEST_CHANGES,
                selinuxChangeListener);
        injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_R_CHANGES,
                selinuxChangeListener);

		//根据用户类型安装或卸载系统应用安装包
        m.installAllowlistedSystemPackages();
        IPackageManagerImpl iPackageManager = m.new IPackageManagerImpl();
        ServiceManager.addService("package", iPackageManager);
        final PackageManagerNative pmn = new PackageManagerNative(m);
        ServiceManager.addService("package_native", pmn);
        LocalManagerRegistry.addManager(PackageManagerLocal.class, m.new PackageManagerLocalImpl());
        return Pair.create(m, iPackageManager);
    }

根据提示,我们找到PMS#PackageManagerService。PMS这里有两个构造方法,名字都是相同的。我们仔细看会发现其中一个是在启动时进行测试的,防止实例化失败,在实际运用中不会使用到。但是这代码有600多行,我们还是不看了吧。。。。总之内部就是对一些参数或者类的初始化

然后我们看到在启动完成后,systemserver中会有这样一个方法,那就是systemReady。

PMS#systemReady

public void systemReady() {
        PackageManagerServiceUtils.enforceSystemOrRoot(
                "Only the system can claim the system is ready");

        final ContentResolver resolver = mContext.getContentResolver();
        if (mReleaseOnSystemReady != null) {
            for (int i = mReleaseOnSystemReady.size() - 1; i >= 0; --i) {
                final File dstCodePath = mReleaseOnSystemReady.get(i);
                F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath);
            }
            mReleaseOnSystemReady = null;
        }
        mSystemReady = true;
        ContentObserver co = new ContentObserver(mHandler) {
            @Override
            public void onChange(boolean selfChange) {
                final boolean ephemeralFeatureDisabled =
                        Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0;
                for (int userId : UserManagerService.getInstance().getUserIds()) {
                    final boolean instantAppsDisabledForUser =
                            ephemeralFeatureDisabled || Secure.getIntForUser(resolver,
                                    Secure.INSTANT_APPS_ENABLED, 1, userId) == 0;
                    mWebInstantAppsDisabled.put(userId, instantAppsDisabledForUser);
                }
            }
        };
        mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
                        .getUriFor(Global.ENABLE_EPHEMERAL_FEATURE),
                false, co, UserHandle.USER_ALL);
        mContext.getContentResolver().registerContentObserver(android.provider.Settings.Secure
                .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL);
        co.onChange(true);

        mAppsFilter.onSystemReady(LocalServices.getService(PackageManagerInternal.class));

		//禁用任何运营商应用程序。我们在启动初期就这样做,以防止应用程序在已经启动后被禁用
        CarrierAppUtils.disableCarrierAppsUntilPrivileged(
                mContext.getOpPackageName(), UserHandle.USER_SYSTEM, mContext);

        disableSkuSpecificApps();

        // 当系统准备就绪时,读取兼容性设置
        boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
                mContext.getContentResolver(),
                android.provider.Settings.Global.COMPATIBILITY_MODE, 1) == 1;
        ParsingPackageUtils.setCompatibilityModeEnabled(compatibilityModeEnabled);

        if (DEBUG_SETTINGS) {
            Log.d(TAG, "compatibility mode:" + compatibilityModeEnabled);
        }

        synchronized (mLock) {
            ArrayList<Integer> changed = mSettings.systemReady(mComponentResolver);
            for (int i = 0; i < changed.size(); i++) {
                mSettings.writePackageRestrictionsLPr(changed.get(i));
            }
        }

        mUserManager.systemReady();

        //注意随着时间推移的外部添加额外卷
        final StorageManager storage = mInjector.getSystemService(StorageManager.class);
        storage.registerListener(mStorageEventHelper);

        mInstallerService.systemReady();
        mPackageDexOptimizer.systemReady();

        // 现在我们大部分都在运行,清理陈旧的用户和应用程序
        mUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
        mStorageEventHelper.reconcileApps(snapshotComputer(), StorageManager.UUID_PRIVATE_INTERNAL);

        mPermissionManager.onSystemReady();

        int[] grantPermissionsUserIds = EMPTY_INT_ARRAY;
        final List<UserInfo> livingUsers = mInjector.getUserManagerInternal().getUsers(
                /* excludePartial= */ true,
                /* excludeDying= */ true,
                /* excludePreCreated= */ false);
        final int livingUserCount = livingUsers.size();
        for (int i = 0; i < livingUserCount; i++) {
            final int userId = livingUsers.get(i).id;
            if (mSettings.isPermissionUpgradeNeeded(userId)) {
                grantPermissionsUserIds = ArrayUtils.appendInt(
                        grantPermissionsUserIds, userId);
            }
        }
        // 如果升级了请在开始前默认授予所有权限
        for (int userId : grantPermissionsUserIds) {
            mLegacyPermissionManager.grantDefaultPermissions(userId);
        }
        if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
            // 如果我们没有授予默认权限,我们会从这个默认权限异常中提前加载
            //以确保在创建新用户时不会击中磁盘
            mLegacyPermissionManager.scheduleReadDefaultPermissionExceptions();
        }

        if (mInstantAppResolverConnection != null) {
            mContext.registerReceiver(new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    mInstantAppResolverConnection.optimisticBind();
                    mContext.unregisterReceiver(this);
                }
            }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
        }

        IntentFilter overlayFilter = new IntentFilter(Intent.ACTION_OVERLAY_CHANGED);
        overlayFilter.addDataScheme("package");
        mContext.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (intent == null) {
                    return;
                }
                Uri data = intent.getData();
                if (data == null) {
                    return;
                }
                String packageName = data.getSchemeSpecificPart();
                if (packageName == null) {
                    return;
                }
                final Computer snapshot = snapshotComputer();
                AndroidPackage pkg = snapshot.getPackage(packageName);
                if (pkg == null) {
                    return;
                }
                sendPackageChangedBroadcast(snapshot, pkg.getPackageName(),
                        true /* dontKillApp */,
                        new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
                        pkg.getUid(),
                        Intent.ACTION_OVERLAY_CHANGED);
            }
        }, overlayFilter);

        mModuleInfoProvider.systemReady();

        // 安装程序服务可能会尝试安装某些已在重新启动时进行安装的软件包。
        //确保这是要调用的最后一个组件,因为安装可能需要其他组件准备就绪
        mInstallerService.restoreAndApplyStagedSessionIfNeeded();

        mExistingPackages = null;

        // 清除在标记上更改了的缓存
        DeviceConfig.addOnPropertiesChangedListener(
                NAMESPACE_PACKAGE_MANAGER_SERVICE, mInjector.getBackgroundExecutor(),
                properties -> {
                    final Set<String> keyset = properties.getKeyset();
                    if (keyset.contains(PROPERTY_INCFS_DEFAULT_TIMEOUTS) || keyset.contains(
                            PROPERTY_KNOWN_DIGESTERS_LIST)) {
                        mPerUidReadTimeoutsCache = null;
                    }
                });

        mBackgroundDexOptService.systemReady();

        // 修剪已缓存一段时间的未使用的静态共享库
        schedulePruneUnusedStaticSharedLibraries(false /* delay */);
    }

在这个函数中调用了 disableSkuSpecificApps()

PMS#disableSkuSpecificApps

private void disableSkuSpecificApps() {
        String[] apkList = mContext.getResources().getStringArray(
                R.array.config_disableApksUnlessMatchedSku_apk_list);
        String[] skuArray = mContext.getResources().getStringArray(
                R.array.config_disableApkUnlessMatchedSku_skus_list);
        if (ArrayUtils.isEmpty(apkList)) {
           return;
        }
        String sku = SystemProperties.get("ro.boot.hardware.sku");
        if (!TextUtils.isEmpty(sku) && ArrayUtils.contains(skuArray, sku)) {
            return;
        }
        final Computer snapshot = snapshotComputer();
        for (String packageName : apkList) {
            setSystemAppHiddenUntilInstalled(snapshot, packageName, true);
            for (UserInfo user : mInjector.getUserManagerInternal().getUsers(false)) {
                setSystemAppInstallState(snapshot, packageName, false, user.id);
            }
        }
    }

在用户状态下,会设置系统app安装状态,往下走对系统应用进行安装

PMS#setSystemAppInstallState

boolean setSystemAppInstallState(@NonNull Computer snapshot, String packageName,
            boolean installed, int userId) {
        final int callingUid = Binder.getCallingUid();
        final boolean calledFromSystemOrPhone = callingUid == Process.PHONE_UID
                || callingUid == Process.SYSTEM_UID;
        if (!calledFromSystemOrPhone) {
            mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
                    "setSystemAppHiddenUntilInstalled");
        }

        final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
        // 目标应用程序应始终在系统中
        if (packageState == null || !packageState.isSystem() || packageState.getPkg() == null) {
            return false;
        }
        if (packageState.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
            throw new SecurityException("Only system or phone callers can modify core apps");
        }
        // 如果在统一安装状态下进行检查
        if (packageState.getUserStateOrDefault(userId).isInstalled() == installed) {
            return false;
        }

        final long callingId = Binder.clearCallingIdentity();
        try {
            if (installed) {
                // 未安装情况下进行安装
                mInstallPackageHelper.installExistingPackageAsUser(
                        packageName,
                        userId,
                        PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
                        PackageManager.INSTALL_REASON_DEVICE_SETUP,
                        null,
                        null);
                return true;
            }

            // 卸载
            deletePackageVersioned(
                    new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
                    new PackageManager.LegacyPackageDeleteObserver(null).getBinder(),
                    userId,
                    PackageManager.DELETE_SYSTEM_APP);
            return true;
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }
    }

根据代码判断,将会对应用进行安装,调用installExistingPackageAsUser方法

InstallPackageHelper#installExistingPackageAsUser

 public int installExistingPackageAsUser(@Nullable String packageName, @UserIdInt int userId,
            @PackageManager.InstallFlags int installFlags,
            @PackageManager.InstallReason int installReason,
            @Nullable List<String> allowlistedRestrictedPermissions,
            @Nullable IntentSender intentSender) {
        if (DEBUG_INSTALL) {
            Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId
                    + " installFlags=" + installFlags + " installReason=" + installReason
                    + " allowlistedRestrictedPermissions=" + allowlistedRestrictedPermissions);
        }

        final int callingUid = Binder.getCallingUid();
        //检测权限,Manifest中的安装许可权限和应用本身或者被呼出的许可
        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
                != PackageManager.PERMISSION_GRANTED
                && mContext.checkCallingOrSelfPermission(
                android.Manifest.permission.INSTALL_EXISTING_PACKAGES)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Neither user " + callingUid + " nor current process has "
                    + android.Manifest.permission.INSTALL_PACKAGES + ".");
        }
        //初始化PackageSetting,为我们知道的特定包设置数据
        PackageSetting pkgSetting;
        final Computer preLockSnapshot = mPm.snapshotComputer();
        //强制跨过用户权限
        preLockSnapshot.enforceCrossUserPermission(callingUid, userId,
                true /* requireFullPermission */, true /* checkShell */,
                "installExistingPackage for user " + userId);
        if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
            return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
        }

		//首先清除呼叫身份
        final long callingId = Binder.clearCallingIdentity();
        try {
        	//是否已经安装默认设置为false
            boolean installed = false;
            //判断app类型
            final boolean instantApp =
                    (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
            final boolean fullApp =
                    (installFlags & PackageManager.INSTALL_FULL_APP) != 0;

            // writer
            synchronized (mPm.mLock) {
                final Computer snapshot = mPm.snapshotComputer();
                pkgSetting = mPm.mSettings.getPackageLPr(packageName);
                if (pkgSetting == null) {
                    return PackageManager.INSTALL_FAILED_INVALID_URI;
                }
                if (!snapshot.canViewInstantApps(callingUid, UserHandle.getUserId(callingUid))) {
                    // 仅当安装为完整包时才允许使用现有包
                    // 至少一个用户
                    boolean installAllowed = false;
                    for (int checkUserId : mPm.mUserManager.getUserIds()) {
                        installAllowed = !pkgSetting.getInstantApp(checkUserId);
                        if (installAllowed) {
                            break;
                        }
                    }
                    if (!installAllowed) {
                        return PackageManager.INSTALL_FAILED_INVALID_URI;
                    }
                }
                if (!pkgSetting.getInstalled(userId)) {
                    pkgSetting.setInstalled(true, userId);
                    pkgSetting.setHidden(false, userId);
                    pkgSetting.setInstallReason(installReason, userId);
                    pkgSetting.setUninstallReason(PackageManager.UNINSTALL_REASON_UNKNOWN, userId);
                    pkgSetting.setFirstInstallTime(System.currentTimeMillis(), userId);
                    mPm.mSettings.writePackageRestrictionsLPr(userId);
                    mPm.mSettings.writeKernelMappingLPr(pkgSetting);
                    installed = true;
                } else if (fullApp && pkgSetting.getInstantApp(userId)) {
                    //将应用程序从即时升级到完整;我们不允许应用降级
                    installed = true;
                }
                ScanPackageUtils.setInstantAppForUser(mPm.mInjector, pkgSetting, userId, instantApp,
                        fullApp);
            }

            if (installed) {
                if (pkgSetting.getPkg() != null) {
                    final PermissionManagerServiceInternal.PackageInstalledParams.Builder
                            permissionParamsBuilder =
                            new PermissionManagerServiceInternal.PackageInstalledParams.Builder();
                    if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS)
                            != 0) {
                        permissionParamsBuilder.setAllowlistedRestrictedPermissions(
                                pkgSetting.getPkg().getRequestedPermissions());
                    }
                    mPm.mPermissionManager.onPackageInstalled(pkgSetting.getPkg(),
                            Process.INVALID_UID /* previousAppId */,
                            permissionParamsBuilder.build(), userId);

                    synchronized (mPm.mInstallLock) {
                        //不用去为一个全新的安装冻结
                        mAppDataHelper.prepareAppDataAfterInstallLIF(pkgSetting.getPkg());
                    }
                }
                mPm.sendPackageAddedForUser(mPm.snapshotComputer(), packageName, pkgSetting, userId,
                        DataLoaderType.NONE);
                //更新序列号(带锁防止watchdog超时)
                synchronized (mPm.mLock) {
                    mPm.updateSequenceNumberLP(pkgSetting, new int[]{ userId });
                }
                //自从我们安装完毕之后无需等待安装之后即可异步启动恢复
                // start async restore with no post-install since we finish install here
                PackageInstalledInfo res = new PackageInstalledInfo(
                        PackageManager.INSTALL_SUCCEEDED);
                //获取包
                res.mPkg = pkgSetting.getPkg();
                res.mNewUsers = new int[]{ userId };

                PostInstallData postInstallData =
                        new PostInstallData(null, res, () -> {
                            mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
                                    userId);
                            if (intentSender != null) {
                                onRestoreComplete(res.mReturnCode, mContext, intentSender);
                            }
                        });
                //
                //只有在data不为空的时候才会去执行
                restoreAndPostInstall(userId, res, postInstallData);
            }
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }

        return PackageManager.INSTALL_SUCCEEDED;
    }

函数结束后将会发送一个message,为PackageManager.INSTALL_SUCCEEDED,实际上还是会调用回PackageInstaller.STATUS_SUCCESS发送这个message

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值