android开发浅谈之PackageManagerService(pkms)

2023-10-27

本文基于Android10.0

主要从以下几个方面着手:

PKMS启动
PKMS安装APK
PKMS卸载APK

PKMS启动

这里我简单的将其分为以下几个步骤:

  • 1.SystemServer通过PKMS的main方法启动PKMS
  • 2.PKMS构造方法中初始化Settings
  • 3.扫描各个系统目录下APP相关信息

1.SystemServer通过PKMS的main方法启动PKMS

PKMS启动是在SystemServer.startBootstrapServices:

mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
        mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);

PackageManagerService.main方法:

public static PackageManagerService main(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
	.....
	//构造方法
    PackageManagerService m = new PackageManagerService(context, installer,
            factoryTest, onlyCore);
    m.enableSystemUserPackages();
    ServiceManager.addService("package", m);
    final PackageManagerNative pmn = m.new PackageManagerNative();
    ServiceManager.addService("package_native", pmn);
    return m;
}

2.PKMS构造方法中初始化Settings

PackageManagerService.PackageManagerService

public PackageManagerService(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
    ......
    //Settings的初始化
    mSettings = new Settings(Environment.getDataDirectory(),
                    mPermissionManager.getPermissionSettings(), mPackages);    

mSettings是全局的,它主要协助PMS保存系统中安装APP包名,权限,四大组件等相关信息的存储,Settings会在/data/system目录下创建相关的文件来保存这些APP相关的信息

frameworks\base\services\core\java\com\android\server\pm\Settings.java

查看Settings.Settings方法:

Settings(File dataDir, PermissionSettings permission,
        Object 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");
}

我们大概看一下/data/system生成的文件信息:
packages.xml----主要是包括包的权限信息:

<package name="com.sohu.inputmethod.sogou.oem" codePath="/system/priv-app/SogouInput" nativeLibraryPath="/system/priv-app/SogouInput/lib" primaryCpuAbi="armeabi" publicFlags="945307205" privateFlags="8" ft="179aac28790" it="179aac28790" ut="179aac28790" version="1312" userId="10076" isOrphaned="true">
    <sigs count="1" schemeVersion="1">
        <cert index="0" key="30820248308201b102045a3eccafc07082c70bb30ef4" />
    </sigs>
    <perms>
        <item name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" granted="true" flags="0" />
        <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
......
        <item name="com.android.launcher.permission.INSTALL_SHORTCUT" granted="true" flags="0" />
        <item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
    </perms>
    <proper-signing-keyset identifier="8" />
</package>

packages-backup.xml

packages.list------------------应用列表

com.android.soundrecorder 10135 0 /data/user/0/com.android.soundrecorder platform:targetSdkVersion=29 1023,3003 0 63

packages-stopped.xml
packages-stopped-backup.xml

3.扫描各个系统目录下APP相关信息

PackageManagerService.PackageManagerService:

public PackageManagerService(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
    ......
    // 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(new File(PRODUCT_OVERLAY_DIR),
            mDefParseFlags
            | PackageParser.PARSE_IS_SYSTEM_DIR,
            scanFlags
            | SCAN_AS_SYSTEM
            | SCAN_AS_PRODUCT,
            0);
      ......
       scanDirTracedLI(productServicesAppDir,
         mDefParseFlags
         | PackageParser.PARSE_IS_SYSTEM_DIR,
         scanFlags
         | SCAN_AS_SYSTEM
         | SCAN_AS_PRODUCT_SERVICES,
         0);

从Android 10,0的代码来看,扫描路径的顺序为:

/vendor/overlay
/product/overlay
/product_services/overlay
/odm/overlay
/oem/overlay
/system/framework
/system/priv-app
/system/app
/vendor/priv-app
/vendor/app
/odm/priv-app
/odm/app
/oem/app
/product/priv-app
/product/app
/product_services/priv-app
/product_services/app

PKMS安装APK

adb install命令 安装apk

我们使用adb install 命令来安装apk:

先来看PackageManagerService的消息定义:

static final int SEND_PENDING_BROADCAST = 1;
static final int INIT_COPY = 5;  //此是在安装apk前,copy apk的消息
static final int POST_INSTALL = 9;  //安装前的消息
static final int WRITE_SETTINGS = 13;
static final int WRITE_PACKAGE_RESTRICTIONS = 14;
static final int PACKAGE_VERIFIED = 15;
static final int CHECK_PENDING_VERIFICATION = 16;
static final int START_INTENT_FILTER_VERIFICATIONS = 17;  //intent filter 的校验消息
static final int INTENT_FILTER_VERIFIED = 18;
static final int WRITE_PACKAGE_LIST = 19;
static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20;
static final int ENABLE_ROLLBACK_STATUS = 21;
static final int ENABLE_ROLLBACK_TIMEOUT = 22;
static final int DEFERRED_NO_KILL_POST_DELETE = 23;
static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24;
static final int NEED_UPDATAE_APPLICATION = 25;
static final int NEED_UPDATAE_ACTIVITYINFO = 26;

我们先确认系统是如何发送消息MSG_COMMIT:

我们在PackageInstallerSession.commit添加日志信息:

public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
......
    Slog.v(TAG, "commit " , new Throwable());
    mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
}

这个是日志信息输出:

com.android.server.pm.PackageInstallerSession.commit(PackageInstallerSession.java:920)
android.content.pm.PackageInstaller$Session.commit(PackageInstaller.java:1079)
com.android.server.pm.PackageManagerShellCommand.doCommitSession(PackageManagerShellCommand.java:2851)
com.android.server.pm.PackageManagerShellCommand.runInstall(PackageManagerShellCommand.java:1082)
com.android.server.pm.PackageManagerShellCommand.onCommand(PackageManagerShellCommand.java:169)
android.os.ShellCommand.exec(ShellCommand.java:104)
com.android.server.pm.PackageManagerService.onShellCommand(PackageManagerService.java:22266)
android.os.Binder.shellCommand(Binder.java:881)
android.os.Binder.onTransact(Binder.java:765)
android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:4923)
com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:4141)
android.os.Binder.execTransactInternal(Binder.java:1021)
android.os.Binder.execTransact(Binder.java:994)

查看PackageManagerShellCommand.onCommand:

public int onCommand(String cmd) {
......
    switch(cmd) {
        case "path":
            return runPath();
        case "dump":
            return runDump();
        case "list":
            return runList();
        case "resolve-activity":
            return runResolveActivity();
        case "query-activities":
            return runQueryIntentActivities();
        case "query-services":
            return runQueryIntentServices();
        case "query-receivers":
            return runQueryIntentReceivers();
        case "install":
            return runInstall();//安装apk
  ......                      

可以看出adb shell pm command命令的入口就是在这个方法中添加,而我们执行adb install命令,后面就会来到此处的runInstall方法。

最后,就会来到PackageInstallerSession.commit:

public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
......
    mHandler.obtainMessage(MSG_COMMIT).sendToTarget();//发送MSG_COMMIT消息
}

然后,我们在PackageManagerService$PackageHandler.doHandleMessage就可以接受到消息MSG_COMMIT:

void doHandleMessage(Message msg) {
......
    switch (msg.what) {
        case INIT_COPY: {
            HandlerParams params = (HandlerParams) msg.obj;
            if (params != null) {
                if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
                Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                        System.identityHashCode(params));
                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
                params.startCopy();//执行copy操作
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
            break;
        }

查看日志,我们就知道,会将此apk复现/data/app/vmdl2019169999.tmp:

PackageManager: init_copy: InstallParams{78e22b5 file=/data/app/vmdl2019169999.tmp}
PackageManager: startCopy UserHandle{0}: InstallParams{78e22b5 file=/data/app/vmdl2019169999.tmp}

PackageManagerService$HandlerParams.startCopy

final void startCopy() {
    if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
    handleStartCopy();
    handleReturnCode();
}

再查看日志:

PackageManager: Renaming /data/app/vmdl2019169999.tmp to /data/app/flipboard.cn-MArVEpS9MmEGIfVhBcSfbg==

我们发现有一个对复制过来的文件进行重命名的操作

再查看日志:

PackageManager: installNewPackageLI: Package{2dbf84 flipboard.cn}

在PackageManagerService.preparePackageLI添加日志信息:

private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
        throws PrepareFailure {
    ........
    // Remember this for later, in case we need to rollback this install
    String pkgName1 = pkg.packageName;
    Slog.v(TAG, "preparePackageLI installNewPackageLI" , new Throwable());
    if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);

可以看到日志信息:

com.android.server.pm.PackageManagerService.preparePackageLI(PackageManagerService.java:18268)
com.android.server.pm.PackageManagerService.installPackagesLI(PackageManagerService.java:17328)
com.android.server.pm.PackageManagerService.installPackagesTracedLI(PackageManagerService.java:16675)
com.android.server.pm.PackageManagerService.lambda$processInstallRequestsAsync$13$PackageManagerService(PackageManagerService.java:14919)

再查看,发现日志信息:

PackageManager:   Activities: flipboard.activities.LaunchActivity flipboard.activities.BriefingLaunchFlipboardAlias flipboard.activities.LaunchActivityAlias 
PackageManager:   Receivers: flipboard.receivers.FirstLaunchReminderReceiver flipboard.notifications.NotificationExpirationReceiver 
PackageManager:   Registered content provider: flipboard.remoteservice.feeds.china, className = flipboard.remoteservice.FLFeedItemContentProvider, isSyncable = false

也就是需要添加要安装的应用中四大组件activity,receiver,content provider,services信息:

我们在ComponentResolver.addProvidersLocked添加打印日志信息:

private void addProvidersLocked(PackageParser.Package pkg, boolean chatty) {
    Slog.v(TAG, "addProvidersLocked" , new Throwable());

其打印日志为:

com.android.server.pm.ComponentResolver.addProvidersLocked(ComponentResolver.java:557)
com.android.server.pm.ComponentResolver.addAllComponents(ComponentResolver.java:375)
com.android.server.pm.PackageManagerService.commitPackageSettings(PackageManagerService.java:12483)
com.android.server.pm.PackageManagerService.commitReconciledScanResultLocked(PackageManagerService.java:11366)
com.android.server.pm.PackageManagerService.commitPackagesLocked(PackageManagerService.java:17263)
com.android.server.pm.PackageManagerService.installPackagesLI(PackageManagerService.java:17400)
com.android.server.pm.PackageManagerService.installPackagesTracedLI(PackageManagerService.java:16675)
com.android.server.pm.PackageManagerService.lambda$processInstallRequestsAsync$13$PackageManagerService(PackageManagerService.java:14919)

查看ComponentResolver.addAllComponents:

void addAllComponents(PackageParser.Package pkg, boolean chatty) {
    final ArrayList<PackageParser.ActivityIntentInfo> newIntents = new ArrayList<>();
    synchronized (mLock) {
        addActivitiesLocked(pkg, newIntents, chatty);//添加activity
        addReceiversLocked(pkg, chatty); //添加Receiver
        addProvidersLocked(pkg, chatty);  //添加Provider
        addServicesLocked(pkg, chatty);  //添加Service
    }

其ComponentResolver类中定义四大组件的信息为:

/** All available activities, for your resolving pleasure. */
@GuardedBy("mLock")
private final ActivityIntentResolver mActivities = new ActivityIntentResolver();

/** All available providers, for your resolving pleasure. */
@GuardedBy("mLock")
private final ProviderIntentResolver mProviders = new ProviderIntentResolver();

/** All available receivers, for your resolving pleasure. */
@GuardedBy("mLock")
private final ActivityIntentResolver mReceivers = new ActivityIntentResolver();

/** All available services, for your resolving pleasure. */
@GuardedBy("mLock")
private final ServiceIntentResolver mServices = new ServiceIntentResolver();

再往下查看日志:

PackageManager: New package installed in /data/app/flipboard.cn-MArVEpS9MmEGIfVhBcSfbg==

我们在代码PackageManagerService.updateSettingsInternalLI中添加日志:

private void updateSettingsInternalLI(PackageParser.Package pkg,
        String installerPackageName, int[] allUsers, int[] installedForUsers,
        PackageInstalledInfo res, UserHandle user, int installReason) {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
    final String pkgName = pkg.packageName;
    Slog.v(TAG, "updateSettingsInternalLI " , new Throwable());
    if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.codePath);
    ......

其输出日志信息为:

com.android.server.pm.PackageManagerService.updateSettingsInternalLI(PackageManagerService.java:16574)
com.android.server.pm.PackageManagerService.updateSettingsLI(PackageManagerService.java:16555)
com.android.server.pm.PackageManagerService.commitPackagesLocked(PackageManagerService.java:17264)
com.android.server.pm.PackageManagerService.installPackagesLI(PackageManagerService.java:17400)
com.android.server.pm.PackageManagerService.installPackagesTracedLI(PackageManagerService.java:16675)
com.android.server.pm.PackageManagerService.lambda$processInstallRequestsAsync$13$PackageManagerService(PackageManagerService.java:14919)

其理解为,我们更新上面说的settings数据信息。

我们在代码PackageManagerService$PackageHandler.doHandleMessage添加日志信息:

void doHandleMessage(Message msg) {
    Slog.v(TAG, "PackageHandler doHandleMessage " , new Throwable());
    Slog.v(TAG, "PackageHandler doHandleMessage "  + msg.what);

可以看到,我们会先响应消息START_INTENT_FILTER_VERIFICATIONS:

PackageManager: PackageHandler doHandleMessage 17

PackageManagerService$PackageHandler.doHandleMessage:

case START_INTENT_FILTER_VERIFICATIONS: {
    IFVerificationParams params = (IFVerificationParams) msg.obj;
    verifyIntentFiltersIfNeeded(params.userId, params.verifierUid,
            params.replacing, params.pkg);
    break;
}

再响应消息POST_INSTALL:

PackageManager: PackageHandler doHandleMessage 9

PackageManagerService$PackageHandler.doHandleMessage

case POST_INSTALL: {
    if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);

    PostInstallData data = mRunningInstalls.get(msg.arg1);
    final boolean didRestore = (msg.arg2 != 0);
    mRunningInstalls.delete(msg.arg1);//删除数据

    if (data != null && data.mPostInstallRunnable != null) {
        data.mPostInstallRunnable.run();
    } else if (data != null) {
        InstallArgs args = data.args;
        PackageInstalledInfo parentRes = data.res;

        final boolean grantPermissions = (args.installFlags
                & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
        final boolean killApp = (args.installFlags
                & PackageManager.INSTALL_DONT_KILL_APP) == 0;
        final boolean virtualPreload = ((args.installFlags
                & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
        final String[] grantedPermissions = args.installGrantPermissions;
        final List<String> whitelistedRestrictedPermissions = ((args.installFlags
                & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0
                    && parentRes.pkg != null)
                ? parentRes.pkg.requestedPermissions
                : args.whitelistedRestrictedPermissions;

        // Handle the parent package
        handlePackagePostInstall(parentRes, grantPermissions,
                killApp, virtualPreload, grantedPermissions,
                whitelistedRestrictedPermissions, didRestore,
                args.installerPackageName, args.observer);//handlePackagePostInstall

        // Handle the child packages
        final int childCount = (parentRes.addedChildPackages != null)
                ? parentRes.addedChildPackages.size() : 0;
        for (int i = 0; i < childCount; i++) {
            PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i);
            handlePackagePostInstall(childRes, grantPermissions,
                    killApp, virtualPreload, grantedPermissions,
                    whitelistedRestrictedPermissions, false /*didRestore*/,
                    args.installerPackageName, args.observer);
        }

        // Log tracing if needed
        if (args.traceMethod != null) {
            Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.traceMethod,
                    args.traceCookie);
        }
    } else if (DEBUG_INSTALL) {
        // No post-install when we run restore from installExistingPackageForUser
        Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1);
    }
    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
} break;

那么我们最后安装好apk,什么时候发送广播android.intent.action.PACKAGE_ADDED呢?

在PackageManagerService.sendPackageBroadcast源码中添加日志信息:

public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
        final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
        final int[] userIds, int[] instantUserIds) {
    Slog.v(TAG, "sendPackageBroadcast " , new Throwable());

输出的日志信息:

com.android.server.pm.PackageManagerService.sendPackageBroadcast(PackageManagerService.java:13221)
com.android.server.pm.PackageManagerService.sendPackageAddedForNewUsers(PackageManagerService.java:13513)
com.android.server.pm.PackageManagerService.handlePackagePostInstall(PackageManagerService.java:1984)
com.android.server.pm.PackageManagerService.access$400(PackageManagerService.java:424)
com.android.server.pm.PackageManagerService$PackageHandler.doHandleMessage(PackageManagerService.java:1532)
com.android.server.pm.PackageManagerService$PackageHandler.handleMessage(PackageManagerService.java:1440)

查看:

public void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted,
        boolean includeStopped, int appId, int[] userIds, int[] instantUserIds) {
......
    Bundle extras = new Bundle(1);
    // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
    final int uid = UserHandle.getUid(
            (ArrayUtils.isEmpty(userIds) ? instantUserIds[0] : userIds[0]), appId);
    extras.putInt(Intent.EXTRA_UID, uid);
    sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
            packageName, extras, 0, null, null, userIds, instantUserIds);//发送ACTION_PACKAGE_ADDED广播

其安装UML类图:

安装UML类图(1)----处理消息MSG_COMMIT
在这里插入图片描述

安装UML类图(2)----处理消息START_INTENT_FILTER_VERIFICATIONS和POST_INSTALL
在这里插入图片描述

2.在我们应用商店中下载应用后台静默安装

在PackageInstaller.execInstallCommand中添加日志信息:

private boolean execInstallCommand(Context context, int sessionId, String packageName) {
    Slog.v(TAG, "execInstallCommand " , new Throwable());
    .......

输出日志信息:

android.content.pm.PackageInstaller.execInstallCommand(PackageInstaller.java:2513)
android.content.pm.PackageInstaller.silentInstallApk(PackageInstaller.java:2487)
android.content.pm.PackageInstaller.silentInstallApk(PackageInstaller.java:2423)
java.lang.reflect.Method.invoke(Native Method)
com.xxxx.webappstore.f.q.o(AppUtils.java:7)
com.xxxx.webappstore.f.q.p(AppUtils.java:1)
com.xxxx.webappstore.e.b.c.a.h(BaseDownloadTask.java:3)
com.xxxx.webappstore.e.b.c.b.B(DownloadTask.java:13)
com.xxxx.webappstore.e.b.c.b.run(DownloadTask.java:18)

从这就可以看出这是一个调用PackageInstaller的后台静默安装接口来实现的。

其PackageInstaller.execInstallCommand中实现方式为:

private boolean execInstallCommand(Context context, int sessionId, String packageName) {
    PackageInstaller.Session session = null;
    try {
        session = openSession(sessionId);
        Intent intent = new Intent();
        intent.setAction(ACTION_SILENT_INSTALL_APK);
        intent.putExtra(EXTRA_PACKAGE_NAME, packageName);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, sessionId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        session.commit(pendingIntent.getIntentSender());//调用session.commit接口
        return true;
    } catch (Throwable e) {
        Log.e(TAG, e.getMessage(), e);
    }
    return false;
}
public void commit(@NonNull IntentSender statusReceiver) {
    try {
        mSession.commit(statusReceiver, false);这个同上面的逻辑,最后就是调用PackageInstallerSession.commit接口
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

后面的逻辑,就会执行adb install命令来实现安装完全一样了。

PKMS卸载APK

我们在桌面长按一个应用,选择卸载:

其响应的UninstallUninstalling.onCreate逻辑是:
com.android.packageinstaller.UninstallUninstalling.onCreate(UninstallUninstalling.java:109)

protected void onCreate(@Nullable Bundle savedInstanceState) {
......
      try {
          ActivityThread.getPackageManager().getPackageInstaller().uninstall(
                  new VersionedPackage(mAppInfo.packageName,
                          PackageManager.VERSION_CODE_HIGHEST),
                  getPackageName(), flags, pendingIntent.getIntentSender(),
                  user.getIdentifier());//调用uninstall接口
      } catch (RemoteException e) {
          e.rethrowFromSystemServer();
      }

其调用的是PackageInstallerService.uninstall:

com.android.server.pm.PackageInstallerService.uninstall(PackageInstallerService.java:823)

调用PackageInstallerService.uninstall:

public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
            IntentSender statusReceiver, int userId) {
......
    if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
                == PackageManager.PERMISSION_GRANTED) {
        // Sweet, call straight through!
        mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);//调用此接口

PackageManagerService.deletePackageVersioned

public void deletePackageVersioned(VersionedPackage versionedPackage,
        final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags) {
    deletePackageVersionedOri(versionedPackage, observer, userId, deleteFlags);
}

PackageManagerService.deletePackageVersionedOri

private void deletePackageVersionedOri(VersionedPackage versionedPackage,
        final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags) {
    ......
	returnCode = deletePackageX(internalPackageName, versionCode,
                            userId, deleteFlags);    

PackageManagerService.deletePackageX

int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags) {
......
    if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageX: pkg=" + packageName + " user=" + userId);
    try (PackageFreezer freezer = freezePackageForDelete(packageName, freezeUser,
            deleteFlags, "deletePackageX")) {
        res = deletePackageLIF(packageName, UserHandle.of(removeUser), true, allUsers,
                deleteFlags | PackageManager.DELETE_CHATTY, info, true, null);
    }
......
    if (res) {
        final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0;
        info.sendPackageRemovedBroadcasts(killApp);
        info.sendSystemPackageUpdatedBroadcasts();
        info.sendSystemPackageAppearedBroadcasts();
    }

PackageManagerService$PackageRemovedInfo.sendPackageRemovedBroadcasts

void sendPackageRemovedBroadcasts(boolean killApp) {
    sendPackageRemovedBroadcastInternal(killApp);

PackageManagerService$PackageRemovedInfo.sendPackageRemovedBroadcastInternal

private void sendPackageRemovedBroadcastInternal(boolean killApp) {
......
      if (removedPackage != null) {
          packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
              removedPackage, extras, 0, null /*targetPackage*/, null,
              broadcastUsers, instantUserIds);发送ACTION_PACKAGE_REMOVED
          if (installerPackageName != null) {
              packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
                      removedPackage, extras, 0 /*flags*/,
                      installerPackageName, null, broadcastUsers, instantUserIds);
          }
          if (dataRemoved && !isRemovedPackageSystemUpdate) {
              packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
                  removedPackage, extras,
                  Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
                  null, null, broadcastUsers, instantUserIds);//发送ACTION_PACKAGE_FULLY_REMOVED
              packageSender.notifyPackageRemoved(removedPackage, removedUid);
          }
      }
......
       packageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED,
               null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
           null, null, broadcastUsers, instantUserIds);//发送ACTION_UID_REMOVED

删除应用的UML图:
在这里插入图片描述

PackageManagerService 类图

在这里插入图片描述

参考资料

1.Android PMS相关流程
https://blog.csdn.net/qq_31429205/article/details/105232472
2.Android PMS(二)-Apk安装流程
https://www.jianshu.com/p/3c2ed166e829

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

android开发浅谈之PackageManagerService(pkms) 的相关文章

  • 什么是集群、分布式、集中式、伪分布式

    1 集中式 将项目等部署到同一台机器上 对机器性能要求比较高 一般会用多台机器备份 否则 如果机器出现死机等状况 整个项目将不能运行 eg 就好比你要盖一座房子 你房子就给一个人盖 如果这个人生病或者有事 你还没有合适的人来代替这个人 你的
  • 什么是TCP/IP协议?

    点击上方 程序员小灰 选择 置顶公众号 有趣有内涵的文章第一时间送达 本文转载自公众号 技术特工队 作者整理了一些TCP IP协议簇中需要必知必会的十大问题 既是面试高频问题 又是程序员必备基础素养 一 TCP IP模型 TCP IP协议模
  • vue2+bootstrapvue+Koa2+nodejs实现分页

    1 实现分页思路 1 后端需要接收两个数据 当前页 currentPage 每一页的大小 即每一页要展示多少条数据 pageSize 2 注意 前端每点击一次页码儿都要发送一次请求 2 实现案例 vue2 bootstrapvue前端 Ko
  • 内网安全:WMI协议与SMB协议横向移动

    目录 网络拓扑图 网络环境说明 WMI协议 SMB协议 域内信息收集 WMI协议 横向移动 利用方式一 wmic命令 利用方式一 cscript 利用方式一 impacket SMB协议 横向移动 利用方式一 psexec 利用方式二 ps
  • 1年多经验的 Java 开发,该如何提升自己比较好?

    中级Java开发岗位职责 负责研发公司应用软件的模块设计 开发和交付 负责编码 单元测试 按照功能组件的详细设计 对其他软件工程师的代码进行审核 参与新知识的学习和培训 参与业务相关的需求评审 编写技术文档设计 中级Java开发的岗位要求
  • Vue中的绑定样式

    绑定样式 1 class样式 写法 class xxx xxx可以是字符串 对象 数组 字符串写法适用于 类名不确定 要动态获取 对象写法适用于 要绑定多个样式 个数不确定 名字也不确定 数组写法适用于 要绑定多个样式 个数确定 名字也确定
  • 测试网络连通性

    测试网络连通性的方式 ssh v p 端口 root IP curl ip 端口 nc v IP 端口
  • SQL Server 数据库之变量

    变量 1 变量概述 1 1 常规标识符 1 2 分隔标识符 2 局部变量 2 1 局部变量声明 2 2 局部变量赋值 2 3 变量显示 3 全局变量 3 1 全局变量注意事项 3 2 常用的全局变量 4 注释符 5 运算符 5 1 算术运算
  • 【CV】从 YOLO 到 YOLOv8:追踪目标检测算法的演变

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • 使用Flutter和Arduino控制设备

    硬件 配置Arduino IDE Arduino服务器 首先 让TCP服务器运行并测试其是否正常运行 创建一个WiFi服务器对象 现在让我们填写setup 函数 将串行连接配置为115 200 bps的速度 等待一秒钟以确保串行连接已初始化
  • JMeter压力测试实例操作

    1 脚本录制 脚本录制 JMeter启用WEB代理 浏览器把代理上网设置为JMeter所在的IP地址 自己电脑就是127 0 0 1代理端口默认8080 至于浏览器修改代理上网服务器 不做截图 Jmeter 运行在目录 apache jme
  • Qt 的几个常用部件 -- QFrame

    文章目录 基本信息 公共类型 可用来设置的 详细描述 可以处理的事件 基本信息 头文件 include
  • 微信小程序——订阅消息与微信公众号模板消息

    订阅消息 一次性订阅消息 某个按钮 并且只能是用户自己就手动点击的 操作调起来这个允许授权弹窗 允许通知之后也只能发送一次消息 即便点上 总是保持一上选择 不再询问 也只是在下次点击同一个按钮时默认允许发送消息 不是可以一直发送消息的 使用
  • python文件处理方式

    python文件处理方式 file open D pythonText txt r encoding UTF 8 print file lt io TextIOWrapper name D pythonText txt mode r enc
  • IntelliJ IDEA (Ultimate Edition)2021.1配置javaFX8(Mac Pro) 踩坑

    该文写于2022 1 22日 应该算是比较新的坑吧 仅供参考 转发请注明出处 该版本内置了javaFX模块 创建项目可以在左侧工具栏看到JavaFX选项 创建项目后 intelliJ自动创建的项目结构如下图 打开Main文件 发现一大堆红色
  • iOS: HTTPS 与自签名证书

    不是每个公司都会以数百美金一年的代价向CA购买SSL证书 在企业应用中 付费的SSL证书经常被自签名证书所替代 当然 对于自签名证书iOS是没有能力验证的 Safari遇到这种无法验证的自签名证书的唯一处理方法 就是将问题扔给用户 让用户决
  • zSetOperations=redisTemplate.opsForZSet();redis的Zset数据类型方法大全(zSetOperations常用方法详细)

    zSetOperations redisTemplate opsForZSet 以下是 zSetOperations 接口中定义的所有方法及其详细说明 void add K key V value double score 将一个成员添加到

随机推荐

  • JDBC(The end)—— 数据库连接池示例 (与线程池梦幻联动)

    写在之前 不管以后对于那种类型的池 都需要List接口利用多态的写法来对一系列集合框架类进行实例化对象 以此该对象调用add方法 来装多个此种类型的实例化对象 数据库连接池的设计思路比线程池简单易懂 以下是示例 和之前线程池的设计思想相同之
  • python怎么搭建免费代理IP池

    搭建免费代理 IP 池需要经过以下步骤 1 抓取免费代理 IP 可以通过爬虫抓取免费的代理 IP 例如可以使用 requests 和 BeautifulSoup 库实现 需要注意的是 免费代理 IP 的可用性通常比较低 需要考虑测试代理 I
  • 计算机网络学习笔记--基带(base)信号

    基带 base 保持数据波的原样进行传输称为基带传输或者基带数字信号传输 宽带 broad 在数据通信领域则指数据传输速率超过1Mps的传输系统 宽带信号则是将基带信号进行调制后形成的频分复用模拟信号 基带信号进行调制后 其频谱搬移到较高的
  • Linux更改SSH端口,并解决SSHD服务重启失败的问题

    环境 Linux Centos 7 1 进入sshd配置文件 vi etc ssh sshd config 2 找到 Port 22 这行 删掉注释符 将端口改为 想要变成的端口号 如 2022 3 重启sshd服务 systemctl r
  • 微信小程序后台销毁时间 演变和总结(热启动时间限制)

    小程序启动 这样 小程序启动可以分为两种情况 一种是冷启动 一种是热启动 冷启动 如果用户首次打开 或小程序销毁后被用户再次打开 此时小程序需要重新加载启动 即冷启动 热启动 如果用户已经打开过某小程序 然后在一定时间内再次打开该小程序 此
  • Java实现微信登录

    Java实现微信登录 代码实现微信授权 简单来说 微信授权分为四步 1 授权登录接口 生成一个二维码在页面中 2 用户点击授权成功后自动获取code 3 浏览器自动利用code访问回调接口 4 在回调接口中利用code登录 appId ap
  • Android嵌入式系统程序开发

    Android嵌入式系统程序开发 基本信息 作者 胡文 宁世勇 李明俊 金雪松 丛书名 单片机与嵌入式丛书 出版社 机械工业出版社 ISBN 9787111411697 上架时间 2013 4 2 出版日期 2013 年4月 开本 16开
  • oracle rac io,ORACLE RAC 之I/O分离--hangcheck-timer模块配置

    ORACLE RAC 之I O分离 hangcheck timer模块配置 此文档摘自METALINK 726833 1 所适用的ORACLE版本为 Oracle Server Enterprise Edition Version 9 2
  • git:文件存储方式

    引言 我们知道 git 跟踪文件会经历三个阶段 工作区 暂存区和本地仓库 参考git 理解工作区 暂存区和本地仓库 在这些阶段文件如何被储存 理解 git 文件的存储方式能帮助我们掌握 git 的工作原理 git 对象 在上述三个阶段 文件
  • 基于典型相关分析的故障检测和过程监控算法研究(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 文献来源 本文首先研究了一种基于广义典型相
  • 安装ik中文分词器

    前提是安装了es Elasticsearch安装 p f 的博客 CSDN博客 1 根据es版本下载对应的中文ik分词器zip资源 官方下载地址 Releases medcl elasticsearch analysis ik GitHub
  • 火狐自定义字体失败 downloadable font: no supported format found

    Bootstrap AdminLTE搭起来的服务 突然字体图标都访问不了了 报错如下 解决办法1 FontAwesome官网找解决办法 不引用本地的css 直接引用官网建议的地址 完美解决 图标都出来了 解决办法2 pom打包的代码中添加
  • Vxe Table/Grid 单元格分组合并

    要合并的列 htbm htmc hetd hetdws yjs 一般的合并 不带逻辑 直接就把各自列对应的相同名字的列合并了 如下所示 不是合同的数据 值一样 直接给合并了 加上逻辑区分后的合并 会依次判断前面的字段一致后才合并后面的字段
  • Centos7.3 Openstack-liberty安装部署记录

    一 环境 1 1 安全 本指南会告诉你如何使用 Red Hat Enterprise Linux 7和其衍生的EPEL仓库安装OpenStack 说明 目前统一采用Centos7 3版本进行Openstack liberty版本的安装 测试
  • 【分布式】Zookeeper数据与存储

    一 前言 前面分析了Zookeeper对请求的处理 本篇博文接着分析Zookeeper中如何对底层数据进行存储 数据存储被分为内存数据存储于磁盘数据存储 二 数据与存储 2 1 内存数据 Zookeeper的数据模型是树结构 在内存数据库中
  • Centos7搭建sftp服务,window上filezilla连接

    1 创建sftp数据目录 添加群组用户 mkdir p data sftp sftp的数据目录 chmod 755 R data sftp 给ftp用户进入目录 chown root root R data sftp 需要使用chroot
  • Python 列表在内存中的顺序存储

    列表在内存中的存储空间是连续的 因此在创建列表时 首先向操作系统申请一块固定大小的内存空间 来存储每个列表元素 例如 a 1 2 3 4 由于在 python 中 int 类型的数据占4个字节 因此这条语句会向操作系统申请 4 4 16 b
  • js对象数组根据某一属性查找对象

    js对象数组 name zhangsan age 20 name lisi age 30 如果想查找name是zhangsan的这个对象 传统方法可以根据for if条件遍历 也可以用map重新构造 或者组装新对象 1 组装新对象 list
  • Python实现ARMA模型

    1 导入相关包 查看数据情况 import numpy as np import pandas as pd import matplotlib pyplot as plt plt rcParams font sans serif SimHe
  • android开发浅谈之PackageManagerService(pkms)

    本文基于Android10 0 主要从以下几个方面着手 PKMS启动 PKMS安装APK PKMS卸载APK PKMS启动 这里我简单的将其分为以下几个步骤 1 SystemServer通过PKMS的main方法启动PKMS 2 PKMS构