Android 应用冻结流程分析

Android 的这些组件有两种状态,禁止的和正常激活 ,默认为激活状态。Android应用冻结主要是调用PMS来实现应用冻结。用户可以通过命令 PackageManager接口的方法实现或者通过pm命令来实现。

1.客户端调用PackageManager如下接口:
int getComponentEnabledSetting(in ComponentName componentName); //获取当前组件状态
int getApplicationEnabledSetting(in String packageName);//获取当前包状态
void setComponentEnabledSetting(in ComponentName componentName,in int newState, in int flags);//设置指定组件的状态
void setApplicationEnabledSetting(in String packageName, in int newState, int flags);//设置指定包的状态

2.通过pm命令:
pm enable [–user USER_ID] PACKAGE_OR_COMPONENT 恢复为激活状态
pm disable [–user USER_ID] PACKAGE_OR_COMPONENT 修改为禁用状态
pm list packages -d 查看禁用的列表 (返回的package name 的列表)

最终通过binder进程间通信调用PMS服务的如下方法:

@Override
    public void setApplicationEnabledSetting(String appPackageName,
            int newState, int flags, int userId, String callingPackage) {
        if (!sUserManager.exists(userId)) return;
        if (callingPackage == null) {
            callingPackage = Integer.toString(Binder.getCallingUid());
        }
        setEnabledSetting(appPackageName, null, newState, flags, userId, callingPackage);
    }

    @Override
    public void setComponentEnabledSetting(ComponentName componentName,
            int newState, int flags, int userId) {
        if (!sUserManager.exists(userId)) return;
        setEnabledSetting(componentName.getPackageName(),
                componentName.getClassName(), newState, flags, userId, null);
    }

可以发现他们最终调用到
setEnabledSetting(appPackageName, null, newState, flags, userId, callingPackage)方法,下面看看这个方法

 private void setEnabledSetting(final String packageName, String className, int newState,
            final int flags, int userId, String callingPackage) {
        if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT
              || newState == COMPONENT_ENABLED_STATE_ENABLED
              || newState == COMPONENT_ENABLED_STATE_DISABLED
              || newState == COMPONENT_ENABLED_STATE_DISABLED_USER
              || newState == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
            throw new IllegalArgumentException("Invalid new component state: "
                    + newState);
                    //如果netState不是这几个状态,抛出IllegalArgumentException异常 
        }
        PackageSetting pkgSetting;
        final int uid = Binder.getCallingUid();
        final int permission;
        if (uid == Process.SYSTEM_UID) {
        //系统签名
            permission = PackageManager.PERMISSION_GRANTED;
        } else {
            permission = mContext.checkCallingOrSelfPermission(
                    android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
        }
        enforceCrossUserPermission(uid, userId,
                false /* requireFullPermission */, true /* checkShell */, "set enabled");
        final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);//是否有权限修改
        boolean sendNow = false;
        boolean isApp = (className == null);//是包名还是组件
        String componentName = isApp ? packageName : className;
        int packageUid = -1;
        ArrayList<String> components;

        // writer
        synchronized (mPackages) {
            pkgSetting = mSettings.mPackages.get(packageName);//获取PackageSetting对象 
            if (pkgSetting == null) {
                if (className == null) {
                    throw new IllegalArgumentException("Unknown package: " + packageName);
                }
                throw new IllegalArgumentException(
                        "Unknown component: " + packageName + "/" + className);
            }
        }

        // Limit who can change which apps
        if (!UserHandle.isSameApp(uid, pkgSetting.appId)) {
            // Don't allow apps that don't have permission to modify other apps
            if (!allowedByPermission) {
                throw new SecurityException(
                        "Permission Denial: attempt to change component state from pid="
                        + Binder.getCallingPid()
                        + ", uid=" + uid + ", package uid=" + pkgSetting.appId);
            }
            // Don't allow changing protected packages.
            if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
                throw new SecurityException("Cannot disable a protected package: " + packageName);
            }
        }

        synchronized (mPackages) {
            if (uid == Process.SHELL_UID) {
                // Shell can only change whole packages between ENABLED and DISABLED_USER states
                int oldState = pkgSetting.getEnabled(userId);
                if (className == null
                    &&
                    (oldState == COMPONENT_ENABLED_STATE_DISABLED_USER
                     || oldState == COMPONENT_ENABLED_STATE_DEFAULT
                     || oldState == COMPONENT_ENABLED_STATE_ENABLED)
                    &&
                    (newState == COMPONENT_ENABLED_STATE_DISABLED_USER
                     || newState == COMPONENT_ENABLED_STATE_DEFAULT
                     || newState == COMPONENT_ENABLED_STATE_ENABLED)) {
                    // ok
                } else {
                    throw new SecurityException(
                            "Shell cannot change component state for " + packageName + "/"
                            + className + " to " + newState);
                }
            }
            if (className == null) {
            //setApplicationEnabledSetting()方法的className值为null 
                // We're dealing with an application/package level state change
                if (pkgSetting.getEnabled(userId) == newState) {
                    // Nothing to do
                    return;
                }
                if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                    || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
                    // Don't care about who enables an app.
                    callingPackage = null;
                }
                pkgSetting.setEnabled(newState, userId, callingPackage);
                // pkgSetting.pkg.mSetEnabled = newState;
            } else {
            //setComponentEnableSetting()方法执行这里 
                // We're dealing with a component level state change
                // First, verify that this is a valid class name.
                PackageParser.Package pkg = pkgSetting.pkg;
                if (pkg == null || !pkg.hasComponentClassName(className)) {
                    if (pkg != null &&
                            pkg.applicationInfo.targetSdkVersion >=
                                    Build.VERSION_CODES.JELLY_BEAN) {
                        throw new IllegalArgumentException("Component class " + className
                                + " does not exist in " + packageName);
                    } else {
                        Slog.w(TAG, "Failed setComponentEnabledSetting: component class "
                                + className + " does not exist in " + packageName);
                    }
                }
                switch (newState) {
                case COMPONENT_ENABLED_STATE_ENABLED:
                    if (!pkgSetting.enableComponentLPw(className, userId)) {
                        return;
                    }
                    break;
                case COMPONENT_ENABLED_STATE_DISABLED:
                    if (!pkgSetting.disableComponentLPw(className, userId)) {
                        return;
                    }
                    break;
                case COMPONENT_ENABLED_STATE_DEFAULT:
                    if (!pkgSetting.restoreComponentLPw(className, userId)) {
                        return;
                    }
                    break;
                default:
                    Slog.e(TAG, "Invalid new component state: " + newState);
                    return;
                }
            }
            Log.e(TAG," liang changed2 ", new RuntimeException().fillInStackTrace());
            scheduleWritePackageRestrictionsLocked(userId);
            //将/data/system/users/0/package-restrictions.xml文件写入package-restrictions 
            components = mPendingBroadcasts.get(userId, packageName);//获取该包名下的所有组件(包括enabled、disabled状态的组件) 
            final boolean newPackage = components == null;
            if (newPackage) {
                components = new ArrayList<String>();
            }
            if (!components.contains(componentName)) {
                components.add(componentName);
            }
            if ((flags&PackageManager.DONT_KILL_APP) == 0) {
                sendNow = true;
                // Purge entry from pending broadcast list if another one exists already
                // since we are sending one right away.
                mPendingBroadcasts.remove(userId, packageName);//从预发送广播的组件列表中移除 
            } else {
                if (newPackage) {
                    mPendingBroadcasts.put(userId, packageName, components);
                }
                if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
                    // Schedule a message
                    mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, BROADCAST_DELAY);
                }
            }
        }

        long callingId = Binder.clearCallingIdentity();
        try {
            if (sendNow) {
                packageUid = UserHandle.getUid(userId, pkgSetting.appId);
                sendPackageChangedBroadcast(packageName,
                        (flags&PackageManager.DONT_KILL_APP) != 0, components, packageUid);
            }
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }
    }

最后调用sendPackageChangedBroadcast()方法,发送广播。此时,killFlag为false。

private void sendPackageChangedBroadcast(String packageName,  
        boolean killFlag, ArrayList<String> componentNames, int packageUid) {  
    if (DEBUG_INSTALL)  
        Log.v(TAG, "Sending package changed: package=" + packageName + " components="  
                + componentNames);  
    Bundle extras = new Bundle(4);  
    extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0));  
    String nameList[] = new String[componentNames.size()];  
    componentNames.toArray(nameList);  
    extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);  
    extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);//false 
    extras.putInt(Intent.EXTRA_UID, packageUid);  
    sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED,  packageName, extras, null, null,  
            new int[] {UserHandle.getUserId(packageUid)});  
}  

接下来看下sendPackageBroadcast()方法是如何调用的。此时,action=Intent.ACTION_PACKAGE_CHANGED,targetPkg和finishedReceiver都为null。

static final void sendPackageBroadcast(String action, String pkg,  
        Bundle extras, String targetPkg, IIntentReceiver finishedReceiver,  
        int[] userIds) {  
    IActivityManager am = ActivityManagerNative.getDefault();  
    if (am != null) {  
        try {  
            if (userIds == null) {  
                userIds = am.getRunningUserIds();  
            }  
            for (int id : userIds) {  
                final Intent intent = new Intent(action,  
                        pkg != null ? Uri.fromParts("package", pkg, null) : null);  
                if (extras != null) {  
                    intent.putExtras(extras);  
                }  
                if (targetPkg != null) {  
                    intent.setPackage(targetPkg);  
                }  
                // Modify the UID when posting to other users 
                int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);  
                if (uid > 0 && UserHandle.getUserId(uid) != id) {  
                    uid = UserHandle.getUid(id, UserHandle.getAppId(uid));  
                    intent.putExtra(Intent.EXTRA_UID, uid);  
                }  
                intent.putExtra(Intent.EXTRA_USER_HANDLE, id);  
                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
                if (DEBUG_BROADCASTS) {  
                    RuntimeException here = new RuntimeException("here");  
                    here.fillInStackTrace();  
                    Slog.d(TAG, "Sending to user " + id + ": "  
                            + intent.toShortString(false, true, false, false)  
                            + " " + intent.getExtras(), here);  
                }  
                am.broadcastIntent(null, intent, null, finishedReceiver,  
                        0, null, null, null, android.app.AppOpsManager.OP_NONE,  
                        finishedReceiver != null, false, id);  
            }  
        } catch (RemoteException ex) {  
        }  
    }  
}  

调用ActivityManager发送广播。

case Intent.ACTION_PACKAGE_CHANGED:  
                            Uri data = intent.getData();  
                            String ssp;  
                            if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {  
                                boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(action);//false 
                                boolean fullUninstall = removed &&  
                                        !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);//false 
                                if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) {//true 
                                    forceStopPackageLocked(ssp, UserHandle.getAppId(  
                                            intent.getIntExtra(Intent.EXTRA_UID, -1)),  
                                            false, true, true, false, fullUninstall, userId,  
                                            removed ? "pkg removed" : "pkg changed");调用forceStopPackaageLocked()方法  
                                }  
                                if (removed) {  
                                    sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED,  
                                            new String[] {ssp}, userId);  
                                    if (fullUninstall) {  
                                        mAppOpsService.packageRemoved(  
                                                intent.getIntExtra(Intent.EXTRA_UID, -1), ssp);  

                                        // Remove all permissions granted from/to this package 
                                        removeUriPermissionsForPackageLocked(ssp, userId, true);  

                                        removeTasksByPackageNameLocked(ssp, userId);  
                                        if (userId == UserHandle.USER_OWNER) {  
                                            mTaskPersister.removeFromPackageCache(ssp);  
                                        }  
                                    }  
                                } else {  
                                    removeTasksByRemovedPackageComponentsLocked(ssp, userId);  
                                    if (userId == UserHandle.USER_OWNER) {  
                                        mTaskPersister.addOtherDeviceTasksToRecentsLocked(ssp);  
                                    }  
                                }  
                            }  
                            break;  
                    }  
                    break;  

此时调用forceStopPackageLocked()方法用来停止组件运行。

最后开机的时候是怎么获取组件的状态了?
这里我初步分析了一下,系统在启动的时候会启动系统进程的PMS服务,PMS服务在初始化阶段会调用setting.java(pm里面)里面的readPackageRestrictionsLPr(int userId)方法读取刚才那个xml文件并且存储

ArraySet<String> enabledComponents = null;
ArraySet<String> disabledComponents = null;

这样开机的时候通过获取pm接口可以获取组件的状态了。

    原文作者:一抹夕阳815
    原文地址: https://blog.csdn.net/linliang815/article/details/76179405
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞