/* ** DroidPlugin Project ** ** Copyright(c) 2015 Andy Zhang <[email protected]> ** ** This file is part of DroidPlugin. ** ** DroidPlugin is free software: you can redistribute it and/or ** modify it under the terms of the GNU Lesser General Public ** License as published by the Free Software Foundation, either ** version 3 of the License, or (at your option) any later version. ** ** DroidPlugin is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** Lesser General Public License for more details. ** ** You should have received a copy of the GNU Lesser General Public ** License along with DroidPlugin. If not, see <http://www.gnu.org/licenses/lgpl.txt> ** **/ package com.morgoo.droidplugin.am; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.os.RemoteException; import android.text.TextUtils; import com.morgoo.droidplugin.pm.PluginManager; import com.morgoo.helper.Log; import java.text.Collator; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; /** * 正在运行的进程列表 * Created by Andy Zhang([email protected]) on 2015/3/10. */ class RunningProcessList { private static final Collator sCollator = Collator.getInstance(); private static final String TAG = RunningProcessList.class.getSimpleName(); private static Comparator sComponentInfoComparator = new Comparator<ComponentInfo>() { @Override public int compare(ComponentInfo lhs, ComponentInfo rhs) { return sCollator.compare(lhs.name, rhs.name); } }; private static Comparator sProviderInfoComparator = new Comparator<ProviderInfo>() { @Override public int compare(ProviderInfo lhs, ProviderInfo rhs) { return sCollator.compare(lhs.authority, rhs.authority); } }; public String getStubProcessByTarget(ComponentInfo targetInfo) { for (ProcessItem processItem : items.values()) { if (processItem.pkgs.contains(targetInfo.packageName) && TextUtils.equals(processItem.targetProcessName, targetInfo.processName)) { return processItem.stubProcessName; } else { try { boolean signed = false; for (String pkg : processItem.pkgs) { if (PluginManager.getInstance().checkSignatures(targetInfo.packageName, pkg) == PackageManager.SIGNATURE_MATCH) { signed = true; break; } } if (signed && TextUtils.equals(processItem.targetProcessName, targetInfo.processName)) { return processItem.stubProcessName; } } catch (Exception e) { Log.e(TAG, "getStubProcessByTarget:error", e); } } } return null; } public boolean isPersistentApplication(int pid) { //是否是持久化的app。 for (ProcessItem processItem : items.values()) { if (processItem.pid == pid) { if (processItem.pkgs != null && processItem.pkgs.size() > 0) { for (String pkg : processItem.pkgs) { if (isPersistentApp(pkg)) { return true; } } } if (processItem.targetActivityInfos != null && processItem.targetActivityInfos.size() > 0) { for (ActivityInfo info : processItem.targetActivityInfos.values()) { if ((info.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0) { return true; } else if (isPersistentApp(info.packageName)) { return true; } } } if (processItem.targetProviderInfos != null && processItem.targetProviderInfos.size() > 0) { for (ProviderInfo info : processItem.targetProviderInfos.values()) { if ((info.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0) { return true; } else if (isPersistentApp(info.packageName)) { return true; } } } if (processItem.targetServiceInfos != null && processItem.targetServiceInfos.size() > 0) { for (ServiceInfo info : processItem.targetServiceInfos.values()) { if ((info.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0) { return true; } else if (isPersistentApp(info.packageName)) { return true; } } } } } return false; } private boolean isPersistentApp(String packageName) { try { PackageInfo info = mHostContext.getPackageManager().getPackageInfo(packageName, PackageManager.GET_META_DATA); if (info != null && info.applicationInfo.metaData != null && info.applicationInfo.metaData.containsKey(PluginManager.EXTRA_APP_PERSISTENT)) { if ((info.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0) { return true; } boolean isPersistentApp = info.applicationInfo.metaData.getBoolean(PluginManager.EXTRA_APP_PERSISTENT); return isPersistentApp; } } catch (Exception e) { Log.e(TAG, "isPersistentApp:error", e); } return false; } private Context mHostContext; public void setContext(Context context) { this.mHostContext = context; } /** * 正在运行的进程item * <p/> * Created by Andy Zhang([email protected]) on 2015/3/10. */ private class ProcessItem { private String stubProcessName; private String targetProcessName; private int pid; private int uid; private long startTime; private List<String> pkgs = new ArrayList<String>(1); //正在运行的插件ActivityInfo //key=ActivityInfo.name, value=插件的ActivityInfo, private Map<String, ActivityInfo> targetActivityInfos = new HashMap<String, ActivityInfo>(4); //正在运行的插件ProviderInfo //key=ProviderInfo.authority, value=插件的ProviderInfo private Map<String, ProviderInfo> targetProviderInfos = new HashMap<String, ProviderInfo>(1); //正在运行的插件ServiceInfo //key=ServiceInfo.name, value=插件的ServiceInfo private Map<String, ServiceInfo> targetServiceInfos = new HashMap<String, ServiceInfo>(1); //正在运行的插件ActivityInfo与代理ActivityInfo的映射 //key=代理ActivityInfo.name, value=插件的ActivityInfo.name, private Map<String, Set<ActivityInfo>> activityInfosMap = new HashMap<String, Set<ActivityInfo>>(4); //正在运行的插件ProviderInfo与代理ProviderInfo的映射 //key=代理ProviderInfo.authority, value=插件的ProviderInfo.authority, private Map<String, Set<ProviderInfo>> providerInfosMap = new HashMap<String, Set<ProviderInfo>>(4); //正在运行的插件ServiceInfo与代理ServiceInfo的映射 //key=代理ServiceInfo.name, value=插件的ServiceInfo.name, private Map<String, Set<ServiceInfo>> serviceInfosMap = new HashMap<String, Set<ServiceInfo>>(4); private void updatePkgs() { ArrayList<String> newList = new ArrayList<String>(); for (ActivityInfo info : targetActivityInfos.values()) { newList.add(info.packageName); } for (ServiceInfo info : targetServiceInfos.values()) { newList.add(info.packageName); } for (ProviderInfo info : targetProviderInfos.values()) { newList.add(info.packageName); } pkgs.clear(); pkgs.addAll(newList); } private void addActivityInfo(String stubActivityName, ActivityInfo info) { if (!targetActivityInfos.containsKey(info.name)) { targetActivityInfos.put(info.name, info); } //pkgs if (!pkgs.contains(info.packageName)) { pkgs.add(info.packageName); } //stub map to activity info Set<ActivityInfo> list = activityInfosMap.get(stubActivityName); if (list == null) { list = new TreeSet<ActivityInfo>(sComponentInfoComparator); list.add(info); activityInfosMap.put(stubActivityName, list); } else { list.add(info); } } void removeActivityInfo(String stubActivityName, ActivityInfo targetInfo) { targetActivityInfos.remove(targetInfo.name); //remove form map if (stubActivityName == null) { for (Set<ActivityInfo> set : activityInfosMap.values()) { set.remove(targetInfo); } } else { Set<ActivityInfo> list = activityInfosMap.get(stubActivityName); if (list != null) { list.remove(targetInfo); } } updatePkgs(); } private void addServiceInfo(String stubServiceName, ServiceInfo info) { if (!targetServiceInfos.containsKey(info.name)) { targetServiceInfos.put(info.name, info); if (!pkgs.contains(info.packageName)) { pkgs.add(info.packageName); } //stub map to activity info Set<ServiceInfo> list = serviceInfosMap.get(stubServiceName); if (list == null) { list = new TreeSet<ServiceInfo>(sComponentInfoComparator); list.add(info); serviceInfosMap.put(stubServiceName, list); } else { list.add(info); } } } void removeServiceInfo(String stubServiceName, ServiceInfo targetInfo) { targetServiceInfos.remove(targetInfo.name); //remove form map if (stubServiceName == null) { for (Set<ServiceInfo> set : serviceInfosMap.values()) { set.remove(targetInfo); } } else { Set<ServiceInfo> list = serviceInfosMap.get(stubServiceName); if (list != null) { list.remove(targetInfo); } } updatePkgs(); } private void addProviderInfo(String stubAuthority, ProviderInfo info) { if (!targetProviderInfos.containsKey(info.authority)) { targetProviderInfos.put(info.authority, info); if (!pkgs.contains(info.packageName)) { pkgs.add(info.packageName); } //stub map to activity info Set<ProviderInfo> list = providerInfosMap.get(stubAuthority); if (list == null) { list = new TreeSet<ProviderInfo>(sProviderInfoComparator); list.add(info); providerInfosMap.put(stubAuthority, list); } else { list.add(info); } } } void removeProviderInfo(String stubAuthority, ProviderInfo targetInfo) { targetProviderInfos.remove(targetInfo.authority); //remove form map if (stubAuthority == null) { for (Set<ProviderInfo> set : providerInfosMap.values()) { set.remove(targetInfo); } } else { Set<ProviderInfo> list = providerInfosMap.get(stubAuthority); if (list != null) { list.remove(targetInfo); } } updatePkgs(); } } //key=pid, value=ProcessItem; private Map<Integer, ProcessItem> items = new HashMap<Integer, ProcessItem>(5); ProcessItem removeByPid(int pid) { return items.remove(pid); } List<String> getStubServiceByPid(int pid) { ProcessItem item = items.get(pid); if (item != null && item.serviceInfosMap != null && item.serviceInfosMap.size() > 0) { return new ArrayList<String>(item.serviceInfosMap.keySet()); } return null; } void addActivityInfo(int pid, int uid, ActivityInfo stubInfo, ActivityInfo targetInfo) { ProcessItem item = items.get(pid); if (TextUtils.isEmpty(targetInfo.processName)) { targetInfo.processName = targetInfo.packageName; } if (item == null) { item = new ProcessItem(); item.pid = pid; item.uid = uid; items.put(pid, item); } item.stubProcessName = stubInfo.processName; if (!item.pkgs.contains(targetInfo.packageName)) { item.pkgs.add(targetInfo.packageName); } item.targetProcessName = targetInfo.processName; item.addActivityInfo(stubInfo.name, targetInfo); } void removeActivityInfo(int pid, int uid, ActivityInfo stubInfo, ActivityInfo targetInfo) { ProcessItem item = items.get(pid); if (TextUtils.isEmpty(targetInfo.processName)) { targetInfo.processName = targetInfo.packageName; } if (item != null) { item.removeActivityInfo(stubInfo.name, targetInfo); } } void addServiceInfo(int pid, int uid, ServiceInfo stubInfo, ServiceInfo targetInfo) { ProcessItem item = items.get(pid); if (TextUtils.isEmpty(targetInfo.processName)) { targetInfo.processName = targetInfo.packageName; } if (item == null) { item = new ProcessItem(); item.pid = pid; item.uid = uid; items.put(pid, item); } item.stubProcessName = stubInfo.processName; if (!item.pkgs.contains(targetInfo.packageName)) { item.pkgs.add(targetInfo.packageName); } item.targetProcessName = targetInfo.processName; item.addServiceInfo(stubInfo.name, targetInfo); } void removeServiceInfo(int pid, int uid, ServiceInfo stubInfo, ServiceInfo targetInfo) { ProcessItem item = items.get(pid); if (TextUtils.isEmpty(targetInfo.processName)) { targetInfo.processName = targetInfo.packageName; } if (item != null) { if (stubInfo != null) { item.removeServiceInfo(stubInfo.name, targetInfo); } else { item.removeServiceInfo(null, targetInfo); } } } void addProviderInfo(int pid, int uid, ProviderInfo stubInfo, ProviderInfo targetInfo) { ProcessItem item = items.get(pid); if (TextUtils.isEmpty(targetInfo.processName)) { targetInfo.processName = targetInfo.packageName; } if (item == null) { item = new ProcessItem(); item.pid = pid; item.uid = uid; items.put(pid, item); } item.stubProcessName = stubInfo.processName; if (!item.pkgs.contains(targetInfo.packageName)) { item.pkgs.add(targetInfo.packageName); } item.targetProcessName = targetInfo.processName; item.addProviderInfo(stubInfo.authority, targetInfo); } void addItem(int pid, int uid) { ProcessItem item = items.get(pid); if (item == null) { item = new ProcessItem(); item.pid = pid; item.uid = uid; item.startTime = System.currentTimeMillis(); items.put(pid, item); } else { item.pid = pid; item.uid = uid; item.startTime = System.currentTimeMillis(); } } boolean isProcessRunning(String stubProcessName) { for (ProcessItem processItem : items.values()) { if (TextUtils.equals(stubProcessName, processItem.stubProcessName)) { return true; } } return false; } boolean isPkgCanRunInProcess(String packageName, String stubProcessName, String targetProcessName) throws RemoteException { for (ProcessItem item : items.values()) { if (TextUtils.equals(stubProcessName, item.stubProcessName)) { if (!TextUtils.isEmpty(item.targetProcessName) && !TextUtils.equals(item.targetProcessName, targetProcessName)) { continue; } if (item.pkgs.contains(packageName)) { return true; } boolean signed = false; for (String pkg : item.pkgs) { if (PluginManager.getInstance().checkSignatures(packageName, pkg) == PackageManager.SIGNATURE_MATCH) { signed = true; break; } } if (signed) { return true; } } } return false; } boolean isPkgEmpty(String stubProcessName) { for (ProcessItem item : items.values()) { if (TextUtils.equals(stubProcessName, item.stubProcessName)) { return item.pkgs.size() <= 0; } } return true; } boolean isStubInfoUsed(ProviderInfo stubInfo) { //TODO return false; } boolean isStubInfoUsed(ServiceInfo stubInfo) { //TODO return false; } boolean isStubInfoUsed(ActivityInfo stubInfo, ActivityInfo targetInfo, String stubProcessName) { for (Integer pid : items.keySet()) { ProcessItem item = items.get(pid); if (TextUtils.equals(item.stubProcessName, stubProcessName)) { Set<ActivityInfo> infos = item.activityInfosMap.get(stubInfo.name); if (infos != null && infos.size() > 0) { for (ActivityInfo info : infos) { if (TextUtils.equals(info.name, targetInfo.name) && TextUtils.equals(info.packageName, targetInfo.packageName)) { return false; } } return true; } return false; } } return false; } List<String> getPackageNameByPid(int pid) { ProcessItem item = items.get(pid); return item != null ? item.pkgs : new ArrayList<String>(); } String getTargetProcessNameByPid(int pid) { ProcessItem item = items.get(pid); return item != null ? item.targetProcessName : null; } public String getStubProcessNameByPid(int pid) { ProcessItem item = items.get(pid); return item != null ? item.stubProcessName : null; } void setTargetProcessName(ComponentInfo stubInfo, ComponentInfo targetInfo) { for (ProcessItem item : items.values()) { if (TextUtils.equals(item.stubProcessName, stubInfo.processName)) { if (!item.pkgs.contains(targetInfo.packageName)) { item.pkgs.add(targetInfo.packageName); } item.targetProcessName = targetInfo.processName; } } } int getActivityCountByPid(int pid) { ProcessItem item = items.get(pid); return item != null ? item.targetActivityInfos.size() : 0; } int getServiceCountByPid(int pid) { ProcessItem item = items.get(pid); return item != null ? item.targetServiceInfos.size() : 0; } int getProviderCountByPid(int pid) { ProcessItem item = items.get(pid); return item != null ? item.targetProviderInfos.size() : 0; } void setProcessName(int pid, String stubProcessName, String targetProcessName, String targetPkg) { ProcessItem item = items.get(pid); if (item != null) { if (!item.pkgs.contains(targetPkg)) { item.pkgs.add(targetPkg); } item.targetProcessName = targetProcessName; item.stubProcessName = stubProcessName; } } void onProcessDied(int pid, int uid) { //进程死掉的时候,移除相关item items.remove(pid); } void clear() { items.clear(); } boolean isPlugin(int pid) { ProcessItem item = items.get(pid); if (item != null) { return !TextUtils.isEmpty(item.stubProcessName) && !TextUtils.isEmpty(item.targetProcessName); } return false; } void dump(String msg) { StringBuilder sb = new StringBuilder("\r\n\r\ndump[" + msg + "]RunningProcess["); for (Integer pid : items.keySet()) { ProcessItem item = items.get(pid); sb.append(" pid:").append(pid).append("\r\n"); sb.append(" Item[\r\n"); sb.append(" pid:").append(item.pid).append("\r\n"); sb.append(" uid:").append(item.uid).append("\r\n"); sb.append(" stubProcessName:").append(item.stubProcessName).append("\r\n"); sb.append(" targetProcessName:").append(item.targetProcessName).append("\r\n"); sb.append(" pkgs:").append(Arrays.toString(item.pkgs.toArray())).append("\r\n"); sb.append(" targetActivityInfos:[\r\n"); for (String name : item.targetActivityInfos.keySet()) { sb.append(" " + name + ":" + item.targetActivityInfos.get(name).name); } sb.append(" ]\r\n"); sb.append(" targetServiceInfos:[\r\n"); for (String name : item.targetServiceInfos.keySet()) { sb.append(" " + name + ":" + item.targetServiceInfos.get(name).name); } sb.append(" ]\r\n"); sb.append(" targetProviderInfos:[\r\n"); for (String name : item.targetProviderInfos.keySet()) { sb.append(" " + name + ":" + item.targetProviderInfos.get(name).name); } sb.append(" ]\r\n"); sb.append(" activityInfosMap:[\r\n"); for (String name : item.activityInfosMap.keySet()) { Set<ActivityInfo> infos = item.activityInfosMap.get(name); sb.append(" " + name + ":[\r\n"); for (ActivityInfo info : infos) { sb.append(" " + info.name + ":\r\n"); } sb.append(" ]\r\n"); } sb.append(" ]\r\n"); sb.append(" serviceInfosMap:[\r\n"); for (String name : item.serviceInfosMap.keySet()) { Set<ServiceInfo> infos = item.serviceInfosMap.get(name); sb.append(" " + name + ":[\r\n"); for (ServiceInfo info : infos) { sb.append(" " + info.name + ":\r\n"); } sb.append(" ]\r\n"); } sb.append(" ]\r\n"); sb.append(" activityInfosMap:[\r\n"); for (String name : item.providerInfosMap.keySet()) { Set<ProviderInfo> infos = item.providerInfosMap.get(name); sb.append(" " + name + ":[\r\n"); for (ProviderInfo info : infos) { sb.append(" " + info.authority + ":\r\n"); } sb.append(" ]\r\n"); } sb.append(" ]\r\n"); } sb.append("] \r\n"); Log.e(TAG, sb.toString()); } }