IntentFirewallpublic class IntentFirewall extends Object
Fields Summary |
---|
static final String | TAG | private static final File | RULES_DIR | private static final int | LOG_PACKAGES_MAX_LENGTH | private static final int | LOG_PACKAGES_SUFFICIENT_LENGTH | private static final String | TAG_RULES | private static final String | TAG_ACTIVITY | private static final String | TAG_SERVICE | private static final String | TAG_BROADCAST | private static final int | TYPE_ACTIVITY | private static final int | TYPE_BROADCAST | private static final int | TYPE_SERVICE | private static final HashMap | factoryMap | private final AMSInterface | mAms | private final RuleObserver | mObserver | private FirewallIntentResolver | mActivityResolver | private FirewallIntentResolver | mBroadcastResolver | private FirewallIntentResolver | mServiceResolver | final FirewallHandler | mHandler |
Constructors Summary |
---|
public IntentFirewall(AMSInterface ams, android.os.Handler handler)
FilterFactory[] factories = new FilterFactory[] {
AndFilter.FACTORY,
OrFilter.FACTORY,
NotFilter.FACTORY,
StringFilter.ACTION,
StringFilter.COMPONENT,
StringFilter.COMPONENT_NAME,
StringFilter.COMPONENT_PACKAGE,
StringFilter.DATA,
StringFilter.HOST,
StringFilter.MIME_TYPE,
StringFilter.SCHEME,
StringFilter.PATH,
StringFilter.SSP,
CategoryFilter.FACTORY,
SenderFilter.FACTORY,
SenderPackageFilter.FACTORY,
SenderPermissionFilter.FACTORY,
PortFilter.FACTORY
};
// load factor ~= .75
factoryMap = new HashMap<String, FilterFactory>(factories.length * 4 / 3);
for (int i=0; i<factories.length; i++) {
FilterFactory factory = factories[i];
factoryMap.put(factory.getTagName(), factory);
}
mAms = ams;
mHandler = new FirewallHandler(handler.getLooper());
File rulesDir = getRulesDir();
rulesDir.mkdirs();
readRulesDir(rulesDir);
mObserver = new RuleObserver(rulesDir);
mObserver.startWatching();
|
Methods Summary |
---|
public boolean | checkBroadcast(android.content.Intent intent, int callerUid, int callerPid, java.lang.String resolvedType, int receivingUid)
return checkIntent(mBroadcastResolver, intent.getComponent(), TYPE_BROADCAST, intent,
callerUid, callerPid, resolvedType, receivingUid);
| boolean | checkComponentPermission(java.lang.String permission, int pid, int uid, int owningUid, boolean exported)Checks if the caller has access to a component
return mAms.checkComponentPermission(permission, pid, uid, owningUid, exported) ==
PackageManager.PERMISSION_GRANTED;
| public boolean | checkIntent(com.android.server.firewall.IntentFirewall$FirewallIntentResolver resolver, android.content.ComponentName resolvedComponent, int intentType, android.content.Intent intent, int callerUid, int callerPid, java.lang.String resolvedType, int receivingUid)
boolean log = false;
boolean block = false;
// For the first pass, find all the rules that have at least one intent-filter or
// component-filter that matches this intent
List<Rule> candidateRules;
candidateRules = resolver.queryIntent(intent, resolvedType, false, 0);
if (candidateRules == null) {
candidateRules = new ArrayList<Rule>();
}
resolver.queryByComponent(resolvedComponent, candidateRules);
// For the second pass, try to match the potentially more specific conditions in each
// rule against the intent
for (int i=0; i<candidateRules.size(); i++) {
Rule rule = candidateRules.get(i);
if (rule.matches(this, resolvedComponent, intent, callerUid, callerPid, resolvedType,
receivingUid)) {
block |= rule.getBlock();
log |= rule.getLog();
// if we've already determined that we should both block and log, there's no need
// to continue trying rules
if (block && log) {
break;
}
}
}
if (log) {
logIntent(intentType, intent, callerUid, resolvedType);
}
return !block;
| public boolean | checkService(android.content.ComponentName resolvedService, android.content.Intent intent, int callerUid, int callerPid, java.lang.String resolvedType, android.content.pm.ApplicationInfo resolvedApp)
return checkIntent(mServiceResolver, resolvedService, TYPE_SERVICE, intent, callerUid,
callerPid, resolvedType, resolvedApp.uid);
| public boolean | checkStartActivity(android.content.Intent intent, int callerUid, int callerPid, java.lang.String resolvedType, android.content.pm.ApplicationInfo resolvedApp)This is called from ActivityManager to check if a start activity intent should be allowed.
It is assumed the caller is already holding the global ActivityManagerService lock.
return checkIntent(mActivityResolver, intent.getComponent(), TYPE_ACTIVITY, intent,
callerUid, callerPid, resolvedType, resolvedApp.uid);
| public static java.io.File | getRulesDir()
return RULES_DIR;
| private static java.lang.String | joinPackages(java.lang.String[] packages)Joins a list of package names such that the resulting string is no more than
LOG_PACKAGES_MAX_LENGTH.
Only full package names will be added to the result, unless every package is longer than the
limit, in which case one of the packages will be truncated and added. In this case, an
additional '-' character will be added to the end of the string, to denote the truncation.
If it encounters a package that won't fit in the remaining space, it will continue on to the
next package, unless the total length of the built string so far is greater than
LOG_PACKAGES_SUFFICIENT_LENGTH, in which case it will stop and return what it has.
boolean first = true;
StringBuilder sb = new StringBuilder();
for (int i=0; i<packages.length; i++) {
String pkg = packages[i];
// + 1 length for the comma. This logic technically isn't correct for the first entry,
// but it's not critical.
if (sb.length() + pkg.length() + 1 < LOG_PACKAGES_MAX_LENGTH) {
if (!first) {
sb.append(',");
} else {
first = false;
}
sb.append(pkg);
} else if (sb.length() >= LOG_PACKAGES_SUFFICIENT_LENGTH) {
return sb.toString();
}
}
if (sb.length() == 0 && packages.length > 0) {
String pkg = packages[0];
// truncating from the end - the last part of the package name is more likely to be
// interesting/unique
return pkg.substring(pkg.length() - LOG_PACKAGES_MAX_LENGTH + 1) + '-";
}
return null;
| private static void | logIntent(int intentType, android.content.Intent intent, int callerUid, java.lang.String resolvedType)
// The component shouldn't be null, but let's double check just to be safe
ComponentName cn = intent.getComponent();
String shortComponent = null;
if (cn != null) {
shortComponent = cn.flattenToShortString();
}
String callerPackages = null;
int callerPackageCount = 0;
IPackageManager pm = AppGlobals.getPackageManager();
if (pm != null) {
try {
String[] callerPackagesArray = pm.getPackagesForUid(callerUid);
if (callerPackagesArray != null) {
callerPackageCount = callerPackagesArray.length;
callerPackages = joinPackages(callerPackagesArray);
}
} catch (RemoteException ex) {
Slog.e(TAG, "Remote exception while retrieving packages", ex);
}
}
EventLogTags.writeIfwIntentMatched(intentType, shortComponent, callerUid,
callerPackageCount, callerPackages, intent.getAction(), resolvedType,
intent.getDataString(), intent.getFlags());
| static Filter | parseFilter(org.xmlpull.v1.XmlPullParser parser)
String elementName = parser.getName();
FilterFactory factory = factoryMap.get(elementName);
if (factory == null) {
throw new XmlPullParserException("Unknown element in filter list: " + elementName);
}
return factory.newFilter(parser);
| private void | readRules(java.io.File rulesFile, com.android.server.firewall.IntentFirewall$FirewallIntentResolver[] resolvers)Reads rules from the given file and add them to the given resolvers
// some temporary lists to hold the rules while we parse the xml file, so that we can
// add the rules all at once, after we know there weren't any major structural problems
// with the xml file
List<List<Rule>> rulesByType = new ArrayList<List<Rule>>(3);
for (int i=0; i<3; i++) {
rulesByType.add(new ArrayList<Rule>());
}
FileInputStream fis;
try {
fis = new FileInputStream(rulesFile);
} catch (FileNotFoundException ex) {
// Nope, no rules. Nothing else to do!
return;
}
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(fis, null);
XmlUtils.beginDocument(parser, TAG_RULES);
int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
int ruleType = -1;
String tagName = parser.getName();
if (tagName.equals(TAG_ACTIVITY)) {
ruleType = TYPE_ACTIVITY;
} else if (tagName.equals(TAG_BROADCAST)) {
ruleType = TYPE_BROADCAST;
} else if (tagName.equals(TAG_SERVICE)) {
ruleType = TYPE_SERVICE;
}
if (ruleType != -1) {
Rule rule = new Rule();
List<Rule> rules = rulesByType.get(ruleType);
// if we get an error while parsing a particular rule, we'll just ignore
// that rule and continue on with the next rule
try {
rule.readFromXml(parser);
} catch (XmlPullParserException ex) {
Slog.e(TAG, "Error reading an intent firewall rule from " + rulesFile, ex);
continue;
}
rules.add(rule);
}
}
} catch (XmlPullParserException ex) {
// if there was an error outside of a specific rule, then there are probably
// structural problems with the xml file, and we should completely ignore it
Slog.e(TAG, "Error reading intent firewall rules from " + rulesFile, ex);
return;
} catch (IOException ex) {
Slog.e(TAG, "Error reading intent firewall rules from " + rulesFile, ex);
return;
} finally {
try {
fis.close();
} catch (IOException ex) {
Slog.e(TAG, "Error while closing " + rulesFile, ex);
}
}
for (int ruleType=0; ruleType<rulesByType.size(); ruleType++) {
List<Rule> rules = rulesByType.get(ruleType);
FirewallIntentResolver resolver = resolvers[ruleType];
for (int ruleIndex=0; ruleIndex<rules.size(); ruleIndex++) {
Rule rule = rules.get(ruleIndex);
for (int i=0; i<rule.getIntentFilterCount(); i++) {
resolver.addFilter(rule.getIntentFilter(i));
}
for (int i=0; i<rule.getComponentFilterCount(); i++) {
resolver.addComponentFilter(rule.getComponentFilter(i), rule);
}
}
}
| private void | readRulesDir(java.io.File rulesDir)Reads rules from all xml files (*.xml) in the given directory, and replaces our set of rules
with the newly read rules.
We only check for files ending in ".xml", to allow for temporary files that are atomically
renamed to .xml
All calls to this method from the file observer come through a handler and are inherently
serialized
FirewallIntentResolver[] resolvers = new FirewallIntentResolver[3];
for (int i=0; i<resolvers.length; i++) {
resolvers[i] = new FirewallIntentResolver();
}
File[] files = rulesDir.listFiles();
if (files != null) {
for (int i=0; i<files.length; i++) {
File file = files[i];
if (file.getName().endsWith(".xml")) {
readRules(file, resolvers);
}
}
}
Slog.i(TAG, "Read new rules (A:" + resolvers[TYPE_ACTIVITY].filterSet().size() +
" B:" + resolvers[TYPE_BROADCAST].filterSet().size() +
" S:" + resolvers[TYPE_SERVICE].filterSet().size() + ")");
synchronized (mAms.getAMSLock()) {
mActivityResolver = resolvers[TYPE_ACTIVITY];
mBroadcastResolver = resolvers[TYPE_BROADCAST];
mServiceResolver = resolvers[TYPE_SERVICE];
}
| boolean | signaturesMatch(int uid1, int uid2)
try {
IPackageManager pm = AppGlobals.getPackageManager();
return pm.checkUidSignatures(uid1, uid2) == PackageManager.SIGNATURE_MATCH;
} catch (RemoteException ex) {
Slog.e(TAG, "Remote exception while checking signatures", ex);
return false;
}
|
|