summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ben Gruver <bgruv@google.com> 2013-04-02 21:18:41 -0700
committer Ben Gruver <bgruv@google.com> 2013-04-02 21:18:41 -0700
commit4efe9403afb0ba3b83fa647eb82e4f90d29f131b (patch)
tree6e71f47bd46fb9e110879c1125ef309f151abfd4
parentcdee9727e26721910a44ba67980b39de4b103f7d (diff)
Initial implementation of IntentFirewall functionality
This has the full filter functionality, but is currently only able to block Activity intents. Logging intents, or blocking service/broadcast intents is not yet implemented. Change-Id: Ied3d8dedf982e17bcbdff3e328eeb87477954df7
-rw-r--r--services/java/com/android/server/IntentResolver.java16
-rw-r--r--services/java/com/android/server/IntentResolverOld.java12
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java18
-rw-r--r--services/java/com/android/server/am/ActivityStack.java34
-rw-r--r--services/java/com/android/server/firewall/AndFilter.java47
-rw-r--r--services/java/com/android/server/firewall/CategoryFilter.java58
-rw-r--r--services/java/com/android/server/firewall/Filter.java42
-rw-r--r--services/java/com/android/server/firewall/FilterFactory.java40
-rw-r--r--services/java/com/android/server/firewall/FilterList.java41
-rw-r--r--services/java/com/android/server/firewall/IntentFirewall.java315
-rw-r--r--services/java/com/android/server/firewall/NotFilter.java60
-rw-r--r--services/java/com/android/server/firewall/OrFilter.java47
-rw-r--r--services/java/com/android/server/firewall/PortFilter.java111
-rw-r--r--services/java/com/android/server/firewall/SenderFilter.java115
-rw-r--r--services/java/com/android/server/firewall/SenderPermissionFilter.java58
-rw-r--r--services/java/com/android/server/firewall/StringFilter.java353
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java10
-rw-r--r--services/java/com/android/server/pm/PreferredIntentResolver.java4
18 files changed, 1343 insertions, 38 deletions
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index 9b190082ae2b..35345f5762ee 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -117,7 +117,7 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
boolean printedHeader = false;
F filter;
for (int i=0; i<N && (filter=a[i]) != null; i++) {
- if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+ if (packageName != null && !isPackageForFilter(packageName, filter)) {
continue;
}
if (title != null) {
@@ -357,11 +357,11 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
}
/**
- * Return the package that owns this filter. This must be implemented to
- * provide correct filtering of Intents that have specified a package name
- * they are to be delivered to.
+ * Returns whether this filter is owned by this package. This must be
+ * implemented to provide correct filtering of Intents that have
+ * specified a package name they are to be delivered to.
*/
- protected abstract String packageForFilter(F filter);
+ protected abstract boolean isPackageForFilter(String packageName, F filter);
protected abstract F[] newArray(int size);
@@ -556,7 +556,7 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
}
// Is delivery being limited to filters owned by a particular package?
- if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+ if (packageName != null && !isPackageForFilter(packageName, filter)) {
if (debug) {
Slog.v(TAG, " Filter is not from package " + packageName + "; skipping");
}
@@ -710,8 +710,8 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
}
private final IntentResolverOld<F, R> mOldResolver = new IntentResolverOld<F, R>() {
- @Override protected String packageForFilter(F filter) {
- return IntentResolver.this.packageForFilter(filter);
+ @Override protected boolean isPackageForFilter(String packageName, F filter) {
+ return IntentResolver.this.isPackageForFilter(packageName, filter);
}
@Override protected boolean allowFilterResult(F filter, List<R> dest) {
return IntentResolver.this.allowFilterResult(filter, dest);
diff --git a/services/java/com/android/server/IntentResolverOld.java b/services/java/com/android/server/IntentResolverOld.java
index 4dd77ce0e30b..94a23796cbb0 100644
--- a/services/java/com/android/server/IntentResolverOld.java
+++ b/services/java/com/android/server/IntentResolverOld.java
@@ -106,7 +106,7 @@ public abstract class IntentResolverOld<F extends IntentFilter, R extends Object
boolean printedHeader = false;
for (int i=0; i<N; i++) {
F filter = a.get(i);
- if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+ if (packageName != null && !isPackageForFilter(packageName, filter)) {
continue;
}
if (title != null) {
@@ -336,11 +336,11 @@ public abstract class IntentResolverOld<F extends IntentFilter, R extends Object
}
/**
- * Return the package that owns this filter. This must be implemented to
- * provide correct filtering of Intents that have specified a package name
- * they are to be delivered to.
+ * Returns whether this filter is owned by this package. This must be
+ * implemented to provide correct filtering of Intents that have
+ * specified a package name they are to be delivered to.
*/
- protected abstract String packageForFilter(F filter);
+ protected abstract boolean isPackageForFilter(String packageName, F filter);
@SuppressWarnings("unchecked")
protected R newResult(F filter, int match, int userId) {
@@ -529,7 +529,7 @@ public abstract class IntentResolverOld<F extends IntentFilter, R extends Object
}
// Is delivery being limited to filters owned by a particular package?
- if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+ if (packageName != null && !isPackageForFilter(packageName, filter)) {
if (debug) {
Slog.v(TAG, " Filter is not from package " + packageName + "; skipping");
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 88ef8842b0a5..90a727d30987 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -30,6 +30,7 @@ import com.android.server.ProcessMap;
import com.android.server.SystemServer;
import com.android.server.Watchdog;
import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.firewall.IntentFirewall;
import com.android.server.pm.UserManagerService;
import com.android.server.wm.AppTransition;
import com.android.server.wm.WindowManagerService;
@@ -274,6 +275,8 @@ public final class ActivityManagerService extends ActivityManagerNative
public ActivityStack mMainStack;
+ public IntentFirewall mIntentFirewall;
+
private final boolean mHeadless;
// Whether we should show our dialogs (ANR, crash, etc) or just perform their
@@ -570,8 +573,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
- protected String packageForFilter(BroadcastFilter filter) {
- return filter.packageName;
+ protected boolean isPackageForFilter(String packageName, BroadcastFilter filter) {
+ return packageName.equals(filter.packageName);
}
};
@@ -1467,7 +1470,8 @@ public final class ActivityManagerService extends ActivityManagerNative
m.mContext = context;
m.mFactoryTest = factoryTest;
m.mMainStack = new ActivityStack(m, context, true, thr.mLooper);
-
+ m.mIntentFirewall = new IntentFirewall(m.new IntentFirewallInterface());
+
m.mBatteryStatsService.publish(context);
m.mUsageStatsService.publish(context);
m.mAppOpsService.publish(context);
@@ -4943,6 +4947,14 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ class IntentFirewallInterface implements IntentFirewall.AMSInterface {
+ public int checkComponentPermission(String permission, int pid, int uid,
+ int owningUid, boolean exported) {
+ return ActivityManagerService.this.checkComponentPermission(permission, pid, uid,
+ owningUid, exported);
+ }
+ }
+
/**
* This can be called with or without the global lock held.
*/
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 526b24f052b3..3d7da7bb70ec 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -2489,6 +2489,7 @@ final class ActivityStack {
int err = ActivityManager.START_SUCCESS;
ProcessRecord callerApp = null;
+
if (caller != null) {
callerApp = mService.getRecordForAppLocked(caller);
if (callerApp != null) {
@@ -2592,34 +2593,37 @@ final class ActivityStack {
throw new SecurityException(msg);
}
+ boolean abort = !mService.mIntentFirewall.checkStartActivity(intent,
+ callerApp==null?null:callerApp.info, callingPackage, callingUid, callingPid,
+ resolvedType, aInfo);
+
if (mMainStack) {
if (mService.mController != null) {
- boolean abort = false;
try {
// The Intent we give to the watcher has the extra data
// stripped off, since it can contain private information.
Intent watchIntent = intent.cloneFilter();
- abort = !mService.mController.activityStarting(watchIntent,
+ abort |= !mService.mController.activityStarting(watchIntent,
aInfo.applicationInfo.packageName);
} catch (RemoteException e) {
mService.mController = null;
}
-
- if (abort) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- // We pretend to the caller that it was really started, but
- // they will just get a cancel result.
- mDismissKeyguardOnNextActivity = false;
- ActivityOptions.abort(options);
- return ActivityManager.START_SUCCESS;
- }
}
}
+ if (abort) {
+ if (resultRecord != null) {
+ sendActivityResultLocked(-1,
+ resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ // We pretend to the caller that it was really started, but
+ // they will just get a cancel result.
+ mDismissKeyguardOnNextActivity = false;
+ ActivityOptions.abort(options);
+ return ActivityManager.START_SUCCESS;
+ }
+
ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid, callingPackage,
intent, resolvedType, aInfo, mService.mConfiguration,
resultRecord, resultWho, requestCode, componentSpecified);
diff --git a/services/java/com/android/server/firewall/AndFilter.java b/services/java/com/android/server/firewall/AndFilter.java
new file mode 100644
index 000000000000..cabf00b3b271
--- /dev/null
+++ b/services/java/com/android/server/firewall/AndFilter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class AndFilter extends FilterList {
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ String callerPackage, int callerUid, int callerPid, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ for (int i=0; i<children.size(); i++) {
+ if (!children.get(i).matches(ifw, intent, callerApp, callerPackage, callerUid,
+ callerPid, resolvedType, resolvedApp)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static final FilterFactory FACTORY = new FilterFactory("and") {
+ @Override
+ public Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ return new AndFilter().readFromXml(parser);
+ }
+ };
+}
diff --git a/services/java/com/android/server/firewall/CategoryFilter.java b/services/java/com/android/server/firewall/CategoryFilter.java
new file mode 100644
index 000000000000..d5e9fe843306
--- /dev/null
+++ b/services/java/com/android/server/firewall/CategoryFilter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Set;
+
+class CategoryFilter implements Filter {
+ private static final String ATTR_NAME = "name";
+
+ private final String mCategoryName;
+
+ private CategoryFilter(String categoryName) {
+ mCategoryName = categoryName;
+ }
+
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, String callerPackage,
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+ Set<String> categories = intent.getCategories();
+ if (categories == null) {
+ return false;
+ }
+ return categories.contains(mCategoryName);
+ }
+
+ public static final FilterFactory FACTORY = new FilterFactory("category") {
+ @Override
+ public Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ String categoryName = parser.getAttributeValue(null, ATTR_NAME);
+ if (categoryName == null) {
+ throw new XmlPullParserException("Category name must be specified.",
+ parser, null);
+ }
+ return new CategoryFilter(categoryName);
+ }
+ };
+}
diff --git a/services/java/com/android/server/firewall/Filter.java b/services/java/com/android/server/firewall/Filter.java
new file mode 100644
index 000000000000..76394662572d
--- /dev/null
+++ b/services/java/com/android/server/firewall/Filter.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+
+interface Filter {
+ /**
+ * Does the given intent + context info match this filter?
+ *
+ * @param ifw The IntentFirewall instance
+ * @param intent The intent being started/bound/broadcast
+ * @param callerApp An ApplicationInfo of an application in the caller's process. This may not
+ * be the specific app that is actually sending the intent. This also may be
+ * null, if the caller is the system process, or an unrecognized process (e.g.
+ * am start)
+ * @param callerPackage The package name of the component sending the intent. This value is
+* provided by the caller and might be forged/faked.
+ * @param callerUid
+ * @param callerPid
+ * @param resolvedType The resolved mime type of the intent
+ * @param resolvedApp The application that contains the resolved component that the intent is
+ */
+ boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ String callerPackage, int callerUid, int callerPid, String resolvedType,
+ ApplicationInfo resolvedApp);
+}
diff --git a/services/java/com/android/server/firewall/FilterFactory.java b/services/java/com/android/server/firewall/FilterFactory.java
new file mode 100644
index 000000000000..dea8b4000ccd
--- /dev/null
+++ b/services/java/com/android/server/firewall/FilterFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.firewall;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+public abstract class FilterFactory {
+ private final String mTag;
+
+ protected FilterFactory(String tag) {
+ if (tag == null) {
+ throw new NullPointerException();
+ }
+ mTag = tag;
+ }
+
+ public String getTagName() {
+ return mTag;
+ }
+
+ public abstract Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException;
+}
diff --git a/services/java/com/android/server/firewall/FilterList.java b/services/java/com/android/server/firewall/FilterList.java
new file mode 100644
index 000000000000..d34b2038e5bf
--- /dev/null
+++ b/services/java/com/android/server/firewall/FilterList.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.firewall;
+
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+abstract class FilterList implements Filter {
+ protected final ArrayList<Filter> children = new ArrayList<Filter>();
+
+ public FilterList readFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
+ int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ readChild(parser);
+ }
+ return this;
+ }
+
+ protected void readChild(XmlPullParser parser) throws IOException, XmlPullParserException {
+ Filter filter = IntentFirewall.parseFilter(parser);
+ children.add(filter);
+ }
+}
diff --git a/services/java/com/android/server/firewall/IntentFirewall.java b/services/java/com/android/server/firewall/IntentFirewall.java
new file mode 100644
index 000000000000..ebbbd8632827
--- /dev/null
+++ b/services/java/com/android/server/firewall/IntentFirewall.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.os.ServiceManager;
+import android.util.Slog;
+import android.util.Xml;
+import com.android.internal.util.XmlUtils;
+import com.android.server.IntentResolver;
+import com.android.server.pm.PackageManagerService;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class IntentFirewall {
+ private static final String TAG = "IntentFirewall";
+
+ private static final String RULES_FILENAME = "ifw.xml";
+
+ private static final String TAG_RULES = "rules";
+ private static final String TAG_ACTIVITY = "activity";
+ private static final String TAG_SERVICE = "service";
+ private static final String TAG_BROADCAST = "broadcast";
+
+ private static final HashMap<String, FilterFactory> factoryMap;
+
+ private final AMSInterface mAms;
+
+ private final IntentResolver<FirewallIntentFilter, Rule> mActivityResolver =
+ new FirewallIntentResolver();
+ private final IntentResolver<FirewallIntentFilter, Rule> mServiceResolver =
+ new FirewallIntentResolver();
+ private final IntentResolver<FirewallIntentFilter, Rule> mBroadcastResolver =
+ new FirewallIntentResolver();
+
+ static {
+ 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.PATH,
+ StringFilter.SENDER_PACKAGE,
+ StringFilter.SSP,
+
+ CategoryFilter.FACTORY,
+ SenderFilter.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);
+ }
+ }
+
+ public IntentFirewall(AMSInterface ams) {
+ mAms = ams;
+ File dataSystemDir = new File(Environment.getDataDirectory(), "system");
+ File rulesFile = new File(dataSystemDir, RULES_FILENAME);
+ readRules(rulesFile);
+ }
+
+ public boolean checkStartActivity(Intent intent, ApplicationInfo callerApp,
+ String callerPackage, int callerUid, int callerPid, String resolvedType,
+ ActivityInfo resolvedActivity) {
+ List<Rule> matchingRules = mActivityResolver.queryIntent(intent, resolvedType, false, 0);
+ boolean log = false;
+ boolean block = false;
+
+ for (int i=0; i< matchingRules.size(); i++) {
+ Rule rule = matchingRules.get(i);
+ if (rule.matches(this, intent, callerApp, callerPackage, callerUid, callerPid,
+ resolvedType, resolvedActivity.applicationInfo)) {
+ 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) {
+ // TODO: log info about intent to event log
+ }
+
+ return !block;
+ }
+
+ private void readRules(File rulesFile) {
+ 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)) {
+ IntentResolver<FirewallIntentFilter, Rule> resolver = null;
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_ACTIVITY)) {
+ resolver = mActivityResolver;
+ } else if (tagName.equals(TAG_SERVICE)) {
+ resolver = mServiceResolver;
+ } else if (tagName.equals(TAG_BROADCAST)) {
+ resolver = mBroadcastResolver;
+ }
+
+ if (resolver != null) {
+ Rule rule = new Rule();
+
+ try {
+ rule.readFromXml(parser);
+ } catch (XmlPullParserException ex) {
+ Slog.e(TAG, "Error reading intent firewall rule", ex);
+ continue;
+ } catch (IOException ex) {
+ Slog.e(TAG, "Error reading intent firewall rule", ex);
+ continue;
+ }
+
+ for (int i=0; i<rule.getIntentFilterCount(); i++) {
+ resolver.addFilter(rule.getIntentFilter(i));
+ }
+ }
+ }
+ } catch (XmlPullParserException ex) {
+ Slog.e(TAG, "Error reading intent firewall rules", ex);
+ } catch (IOException ex) {
+ Slog.e(TAG, "Error reading intent firewall rules", ex);
+ } finally {
+ try {
+ fis.close();
+ } catch (IOException ex) {
+ Slog.e(TAG, "Error while closing " + rulesFile, ex);
+ }
+ }
+ }
+
+ static Filter parseFilter(XmlPullParser parser) throws IOException, XmlPullParserException {
+ 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 static class Rule extends AndFilter {
+ private static final String TAG_INTENT_FILTER = "intent-filter";
+
+ private static final String ATTR_BLOCK = "block";
+ private static final String ATTR_LOG = "log";
+
+ private final ArrayList<FirewallIntentFilter> mIntentFilters =
+ new ArrayList<FirewallIntentFilter>(1);
+ private boolean block;
+ private boolean log;
+
+ @Override
+ public Rule readFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
+ block = Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_BLOCK));
+ log = Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_LOG));
+
+ super.readFromXml(parser);
+ return this;
+ }
+
+ @Override
+ protected void readChild(XmlPullParser parser) throws IOException, XmlPullParserException {
+ if (parser.getName().equals(TAG_INTENT_FILTER)) {
+ FirewallIntentFilter intentFilter = new FirewallIntentFilter(this);
+ intentFilter.readFromXml(parser);
+ mIntentFilters.add(intentFilter);
+ } else {
+ super.readChild(parser);
+ }
+ }
+
+ public int getIntentFilterCount() {
+ return mIntentFilters.size();
+ }
+
+ public FirewallIntentFilter getIntentFilter(int index) {
+ return mIntentFilters.get(index);
+ }
+
+ public boolean getBlock() {
+ return block;
+ }
+
+ public boolean getLog() {
+ return log;
+ }
+ }
+
+ private static class FirewallIntentFilter extends IntentFilter {
+ private final Rule rule;
+
+ public FirewallIntentFilter(Rule rule) {
+ this.rule = rule;
+ }
+ }
+
+ private static class FirewallIntentResolver
+ extends IntentResolver<FirewallIntentFilter, Rule> {
+ @Override
+ protected boolean allowFilterResult(FirewallIntentFilter filter, List<Rule> dest) {
+ return !dest.contains(filter.rule);
+ }
+
+ @Override
+ protected boolean isPackageForFilter(String packageName, FirewallIntentFilter filter) {
+ return true;
+ }
+
+ @Override
+ protected FirewallIntentFilter[] newArray(int size) {
+ return new FirewallIntentFilter[size];
+ }
+
+ @Override
+ protected Rule newResult(FirewallIntentFilter filter, int match, int userId) {
+ return filter.rule;
+ }
+
+ @Override
+ protected void sortResults(List<Rule> results) {
+ // there's no need to sort the results
+ return;
+ }
+ }
+
+ /**
+ * This interface contains the methods we need from ActivityManagerService. This allows AMS to
+ * export these methods to us without making them public, and also makes it easier to test this
+ * component.
+ */
+ public interface AMSInterface {
+ int checkComponentPermission(String permission, int pid, int uid,
+ int owningUid, boolean exported);
+ }
+
+ /**
+ * Checks if the caller has access to a component
+ *
+ * @param permission If present, the caller must have this permission
+ * @param pid The pid of the caller
+ * @param uid The uid of the caller
+ * @param owningUid The uid of the application that owns the component
+ * @param exported Whether the component is exported
+ * @return True if the caller can access the described component
+ */
+ boolean checkComponentPermission(String permission, int pid, int uid, int owningUid,
+ boolean exported) {
+ return mAms.checkComponentPermission(permission, pid, uid, owningUid, exported) ==
+ PackageManager.PERMISSION_GRANTED;
+ }
+
+ boolean signaturesMatch(int uid1, int uid2) {
+ PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
+ return pm.checkUidSignatures(uid1, uid2) == PackageManager.SIGNATURE_MATCH;
+ }
+}
diff --git a/services/java/com/android/server/firewall/NotFilter.java b/services/java/com/android/server/firewall/NotFilter.java
new file mode 100644
index 000000000000..2ff108a1db8b
--- /dev/null
+++ b/services/java/com/android/server/firewall/NotFilter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class NotFilter implements Filter {
+ private final Filter mChild;
+
+ private NotFilter(Filter child) {
+ mChild = child;
+ }
+
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ String callerPackage, int callerUid, int callerPid, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ return !mChild.matches(ifw, intent, callerApp, callerPackage, callerUid, callerPid,
+ resolvedType, resolvedApp);
+ }
+
+ public static final FilterFactory FACTORY = new FilterFactory("not") {
+ @Override
+ public Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ Filter child = null;
+ int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ Filter filter = IntentFirewall.parseFilter(parser);
+ if (child == null) {
+ child = filter;
+ } else {
+ throw new XmlPullParserException(
+ "<not> tag can only contain a single child filter.", parser, null);
+ }
+ }
+ return new NotFilter(child);
+ }
+ };
+}
diff --git a/services/java/com/android/server/firewall/OrFilter.java b/services/java/com/android/server/firewall/OrFilter.java
new file mode 100644
index 000000000000..1ed1c85ae1b0
--- /dev/null
+++ b/services/java/com/android/server/firewall/OrFilter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class OrFilter extends FilterList {
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ String callerPackage, int callerUid, int callerPid, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ for (int i=0; i<children.size(); i++) {
+ if (children.get(i).matches(ifw, intent, callerApp, callerPackage, callerUid, callerPid,
+ resolvedType, resolvedApp)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static final FilterFactory FACTORY = new FilterFactory("or") {
+ @Override
+ public Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ return new OrFilter().readFromXml(parser);
+ }
+ };
+}
diff --git a/services/java/com/android/server/firewall/PortFilter.java b/services/java/com/android/server/firewall/PortFilter.java
new file mode 100644
index 000000000000..2b2a19898785
--- /dev/null
+++ b/services/java/com/android/server/firewall/PortFilter.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.net.Uri;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class PortFilter implements Filter {
+ private static final String ATTR_EQUALS = "equals";
+ private static final String ATTR_MIN = "min";
+ private static final String ATTR_MAX = "max";
+
+ private static final int NO_BOUND = -1;
+
+ // both bounds are inclusive
+ private final int mLowerBound;
+ private final int mUpperBound;
+
+ private PortFilter(int lowerBound, int upperBound) {
+ mLowerBound = lowerBound;
+ mUpperBound = upperBound;
+ }
+
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ String callerPackage, int callerUid, int callerPid, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ int port = -1;
+ Uri uri = intent.getData();
+ if (uri != null) {
+ port = uri.getPort();
+ }
+ return port != -1 &&
+ (mLowerBound == NO_BOUND || mLowerBound <= port) &&
+ (mUpperBound == NO_BOUND || mUpperBound >= port);
+ }
+
+ public static final FilterFactory FACTORY = new FilterFactory("port") {
+ @Override
+ public Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ int lowerBound = NO_BOUND;
+ int upperBound = NO_BOUND;
+
+ String equalsValue = parser.getAttributeValue(null, ATTR_EQUALS);
+ if (equalsValue != null) {
+ int value;
+ try {
+ value = Integer.parseInt(equalsValue);
+ } catch (NumberFormatException ex) {
+ throw new XmlPullParserException("Invalid port value: " + equalsValue,
+ parser, null);
+ }
+ lowerBound = value;
+ upperBound = value;
+ }
+
+ String lowerBoundString = parser.getAttributeValue(null, ATTR_MIN);
+ String upperBoundString = parser.getAttributeValue(null, ATTR_MAX);
+ if (lowerBoundString != null || upperBoundString != null) {
+ if (equalsValue != null) {
+ throw new XmlPullParserException(
+ "Port filter cannot use both equals and range filtering",
+ parser, null);
+ }
+
+ if (lowerBoundString != null) {
+ try {
+ lowerBound = Integer.parseInt(lowerBoundString);
+ } catch (NumberFormatException ex) {
+ throw new XmlPullParserException(
+ "Invalid minimum port value: " + lowerBoundString,
+ parser, null);
+ }
+ }
+
+ if (upperBoundString != null) {
+ try {
+ upperBound = Integer.parseInt(upperBoundString);
+ } catch (NumberFormatException ex) {
+ throw new XmlPullParserException(
+ "Invalid maximum port value: " + upperBoundString,
+ parser, null);
+ }
+ }
+ }
+
+ // an empty port filter is explicitly allowed, and checks for the existence of a port
+ return new PortFilter(lowerBound, upperBound);
+ }
+ };
+}
diff --git a/services/java/com/android/server/firewall/SenderFilter.java b/services/java/com/android/server/firewall/SenderFilter.java
new file mode 100644
index 000000000000..0b790bd58ff9
--- /dev/null
+++ b/services/java/com/android/server/firewall/SenderFilter.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.os.Process;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class SenderFilter {
+ private static final String ATTR_TYPE = "type";
+
+ private static final String VAL_SIGNATURE = "signature";
+ private static final String VAL_SYSTEM = "system";
+ private static final String VAL_SYSTEM_OR_SIGNATURE = "system|signature";
+ private static final String VAL_USER_ID = "userId";
+
+ static boolean isSystemApp(ApplicationInfo callerApp, int callerUid, int callerPid) {
+ if (callerUid == Process.SYSTEM_UID ||
+ callerPid == Process.myPid()) {
+ return true;
+ }
+ if (callerApp == null) {
+ return false;
+ }
+ return (callerApp.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ }
+
+ public static final FilterFactory FACTORY = new FilterFactory("sender") {
+ @Override
+ public Filter newFilter(XmlPullParser parser) throws IOException, XmlPullParserException {
+ String typeString = parser.getAttributeValue(null, ATTR_TYPE);
+ if (typeString == null) {
+ throw new XmlPullParserException("type attribute must be specified for <sender>",
+ parser, null);
+ }
+ if (typeString.equals(VAL_SYSTEM)) {
+ return SYSTEM;
+ } else if (typeString.equals(VAL_SIGNATURE)) {
+ return SIGNATURE;
+ } else if (typeString.equals(VAL_SYSTEM_OR_SIGNATURE)) {
+ return SYSTEM_OR_SIGNATURE;
+ } else if (typeString.equals(VAL_USER_ID)) {
+ return USER_ID;
+ }
+ throw new XmlPullParserException(
+ "Invalid type attribute for <sender>: " + typeString, parser, null);
+ }
+ };
+
+ private static final Filter SIGNATURE = new Filter() {
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ String callerPackage, int callerUid, int callerPid, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ if (callerApp == null) {
+ return false;
+ }
+ return ifw.signaturesMatch(callerUid, resolvedApp.uid);
+ }
+ };
+
+ private static final Filter SYSTEM = new Filter() {
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ String callerPackage, int callerUid, int callerPid, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ if (callerApp == null) {
+ // if callerApp is null, the caller is the system process
+ return false;
+ }
+ return isSystemApp(callerApp, callerUid, callerPid);
+ }
+ };
+
+ private static final Filter SYSTEM_OR_SIGNATURE = new Filter() {
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ String callerPackage, int callerUid, int callerPid, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ return isSystemApp(callerApp, callerUid, callerPid) ||
+ ifw.signaturesMatch(callerUid, resolvedApp.uid);
+ }
+ };
+
+ private static final Filter USER_ID = new Filter() {
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ String callerPackage, int callerUid, int callerPid, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ // This checks whether the caller is either the system process, or has the same user id
+ // I.e. the same app, or an app that uses the same shared user id.
+ // This is the same set of applications that would be able to access the component if
+ // it wasn't exported.
+ return ifw.checkComponentPermission(null, callerPid, callerUid, resolvedApp.uid, false);
+ }
+ };
+}
diff --git a/services/java/com/android/server/firewall/SenderPermissionFilter.java b/services/java/com/android/server/firewall/SenderPermissionFilter.java
new file mode 100644
index 000000000000..02d8b15f36ec
--- /dev/null
+++ b/services/java/com/android/server/firewall/SenderPermissionFilter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class SenderPermissionFilter implements Filter {
+ private static final String ATTR_NAME = "name";
+
+ private final String mPermission;
+
+ private SenderPermissionFilter(String permission) {
+ mPermission = permission;
+ }
+
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ String callerPackage, int callerUid, int callerPid, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ // We assume the component is exported here. If the component is not exported, then
+ // ActivityManager would only resolve to this component for callers from the same uid.
+ // In this case, it doesn't matter whether the component is exported or not.
+ return ifw.checkComponentPermission(mPermission, callerPid, callerUid, resolvedApp.uid,
+ true);
+ }
+
+ public static final FilterFactory FACTORY = new FilterFactory("sender-permission") {
+ @Override
+ public Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ String permission = parser.getAttributeValue(null, ATTR_NAME);
+ if (permission == null) {
+ throw new XmlPullParserException("Permission name must be specified.",
+ parser, null);
+ }
+ return new SenderPermissionFilter(permission);
+ }
+ };
+}
diff --git a/services/java/com/android/server/firewall/StringFilter.java b/services/java/com/android/server/firewall/StringFilter.java
new file mode 100644
index 000000000000..de5a69fb8aaa
--- /dev/null
+++ b/services/java/com/android/server/firewall/StringFilter.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.firewall;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.net.Uri;
+import android.os.PatternMatcher;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+abstract class StringFilter implements Filter {
+ private static final String ATTR_EQUALS = "equals";
+ private static final String ATTR_STARTS_WITH = "startsWith";
+ private static final String ATTR_CONTAINS = "contains";
+ private static final String ATTR_PATTERN = "pattern";
+ private static final String ATTR_REGEX = "regex";
+ private static final String ATTR_IS_NULL = "isNull";
+
+ private final ValueProvider mValueProvider;
+
+ private StringFilter(ValueProvider valueProvider) {
+ this.mValueProvider = valueProvider;
+ }
+
+ /**
+ * Constructs a new StringFilter based on the string filter attribute on the current
+ * element, and the given StringValueMatcher.
+ *
+ * The current node should contain exactly 1 string filter attribute. E.g. equals,
+ * contains, etc. Otherwise, an XmlPullParserException will be thrown.
+ *
+ * @param parser An XmlPullParser object positioned at an element that should
+ * contain a string filter attribute
+ * @return This StringFilter object
+ */
+ public static StringFilter readFromXml(ValueProvider valueProvider, XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ StringFilter filter = null;
+
+ for (int i=0; i<parser.getAttributeCount(); i++) {
+ StringFilter newFilter = getFilter(valueProvider, parser, i);
+ if (newFilter != null) {
+ if (filter != null) {
+ throw new XmlPullParserException("Multiple string filter attributes found");
+ }
+ filter = newFilter;
+ }
+ }
+
+ if (filter == null) {
+ // if there are no string filter attributes, we default to isNull="false" so that an
+ // empty filter is equivalent to an existence check
+ filter = new IsNullFilter(valueProvider, false);
+ }
+
+ return filter;
+ }
+
+ private static StringFilter getFilter(ValueProvider valueProvider, XmlPullParser parser,
+ int attributeIndex) {
+ String attributeName = parser.getAttributeName(attributeIndex);
+
+ switch (attributeName.charAt(0)) {
+ case 'e':
+ if (!attributeName.equals(ATTR_EQUALS)) {
+ return null;
+ }
+ return new EqualsFilter(valueProvider, parser.getAttributeValue(attributeIndex));
+ case 'i':
+ if (!attributeName.equals(ATTR_IS_NULL)) {
+ return null;
+ }
+ return new IsNullFilter(valueProvider, parser.getAttributeValue(attributeIndex));
+ case 's':
+ if (!attributeName.equals(ATTR_STARTS_WITH)) {
+ return null;
+ }
+ return new StartsWithFilter(valueProvider,
+ parser.getAttributeValue(attributeIndex));
+ case 'c':
+ if (!attributeName.equals(ATTR_CONTAINS)) {
+ return null;
+ }
+ return new ContainsFilter(valueProvider, parser.getAttributeValue(attributeIndex));
+ case 'p':
+ if (!attributeName.equals(ATTR_PATTERN)) {
+ return null;
+ }
+ return new PatternStringFilter(valueProvider,
+ parser.getAttributeValue(attributeIndex));
+ case 'r':
+ if (!attributeName.equals(ATTR_REGEX)) {
+ return null;
+ }
+ return new RegexFilter(valueProvider, parser.getAttributeValue(attributeIndex));
+ }
+ return null;
+ }
+
+ protected abstract boolean matchesValue(String value);
+
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, String callerPackage,
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+ String value = mValueProvider.getValue(intent, callerApp, callerPackage, resolvedType,
+ resolvedApp);
+ return matchesValue(value);
+ }
+
+ private static abstract class ValueProvider extends FilterFactory {
+ protected ValueProvider(String tag) {
+ super(tag);
+ }
+
+ public Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ return StringFilter.readFromXml(this, parser);
+ }
+
+ public abstract String getValue(Intent intent, ApplicationInfo callerApp,
+ String callerPackage, String resolvedType, ApplicationInfo resolvedApp);
+ }
+
+ private static class EqualsFilter extends StringFilter {
+ private final String mFilterValue;
+
+ public EqualsFilter(ValueProvider valueProvider, String attrValue) {
+ super(valueProvider);
+ mFilterValue = attrValue;
+ }
+
+ @Override
+ public boolean matchesValue(String value) {
+ return value != null && value.equals(mFilterValue);
+ }
+ }
+
+ private static class ContainsFilter extends StringFilter {
+ private final String mFilterValue;
+
+ public ContainsFilter(ValueProvider valueProvider, String attrValue) {
+ super(valueProvider);
+ mFilterValue = attrValue;
+ }
+
+ @Override
+ public boolean matchesValue(String value) {
+ return value != null && value.contains(mFilterValue);
+ }
+ }
+
+ private static class StartsWithFilter extends StringFilter {
+ private final String mFilterValue;
+
+ public StartsWithFilter(ValueProvider valueProvider, String attrValue) {
+ super(valueProvider);
+ mFilterValue = attrValue;
+ }
+
+ @Override
+ public boolean matchesValue(String value) {
+ return value != null && value.startsWith(mFilterValue);
+ }
+ }
+
+ private static class PatternStringFilter extends StringFilter {
+ private final PatternMatcher mPattern;
+
+ public PatternStringFilter(ValueProvider valueProvider, String attrValue) {
+ super(valueProvider);
+ mPattern = new PatternMatcher(attrValue, PatternMatcher.PATTERN_SIMPLE_GLOB);
+ }
+
+ @Override
+ public boolean matchesValue(String value) {
+ return value != null && mPattern.match(value);
+ }
+ }
+
+ private static class RegexFilter extends StringFilter {
+ private final Pattern mPattern;
+
+ public RegexFilter(ValueProvider valueProvider, String attrValue) {
+ super(valueProvider);
+ this.mPattern = Pattern.compile(attrValue);
+ }
+
+ @Override
+ public boolean matchesValue(String value) {
+ return value != null && mPattern.matcher(value).matches();
+ }
+ }
+
+ private static class IsNullFilter extends StringFilter {
+ private final boolean mIsNull;
+
+ public IsNullFilter(ValueProvider valueProvider, String attrValue) {
+ super(valueProvider);
+ mIsNull = Boolean.parseBoolean(attrValue);
+ }
+
+ public IsNullFilter(ValueProvider valueProvider, boolean isNull) {
+ super(valueProvider);
+ mIsNull = isNull;
+ }
+
+ @Override
+ public boolean matchesValue(String value) {
+ return (value == null) == mIsNull;
+ }
+ }
+
+ public static final ValueProvider COMPONENT = new ValueProvider("component") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+ String resolvedType, ApplicationInfo resolvedApp) {
+ ComponentName cn = intent.getComponent();
+ if (cn != null) {
+ return cn.flattenToString();
+ }
+ return null;
+ }
+ };
+
+ public static final ValueProvider COMPONENT_NAME = new ValueProvider("component-name") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+ String resolvedType, ApplicationInfo resolvedApp) {
+ ComponentName cn = intent.getComponent();
+ if (cn != null) {
+ return cn.getClassName();
+ }
+ return null;
+ }
+ };
+
+ public static final ValueProvider COMPONENT_PACKAGE = new ValueProvider("component-package") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+ String resolvedType, ApplicationInfo resolvedApp) {
+ ComponentName cn = intent.getComponent();
+ if (cn != null) {
+ return cn.getPackageName();
+ }
+ return null;
+ }
+ };
+
+ public static final ValueProvider SENDER_PACKAGE = new ValueProvider("sender-package") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+ String resolvedType, ApplicationInfo resolvedApp) {
+ // TODO: We can't trust this value, so maybe should check all packages in the caller process?
+ return callerPackage;
+ }
+ };
+
+
+ public static final FilterFactory ACTION = new ValueProvider("action") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+ String resolvedType, ApplicationInfo resolvedApp) {
+ return intent.getAction();
+ }
+ };
+
+ public static final ValueProvider DATA = new ValueProvider("data") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+ String resolvedType, ApplicationInfo resolvedApp) {
+ Uri data = intent.getData();
+ if (data != null) {
+ return data.toString();
+ }
+ return null;
+ }
+ };
+
+ public static final ValueProvider MIME_TYPE = new ValueProvider("mime-type") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+ String resolvedType, ApplicationInfo resolvedApp) {
+ return resolvedType;
+ }
+ };
+
+ public static final ValueProvider SCHEME = new ValueProvider("scheme") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+ String resolvedType, ApplicationInfo resolvedApp) {
+ Uri data = intent.getData();
+ if (data != null) {
+ return data.getScheme();
+ }
+ return null;
+ }
+ };
+
+ public static final ValueProvider SSP = new ValueProvider("scheme-specific-part") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+ String resolvedType, ApplicationInfo resolvedApp) {
+ Uri data = intent.getData();
+ if (data != null) {
+ return data.getSchemeSpecificPart();
+ }
+ return null;
+ }
+ };
+
+ public static final ValueProvider HOST = new ValueProvider("host") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+ String resolvedType, ApplicationInfo resolvedApp) {
+ Uri data = intent.getData();
+ if (data != null) {
+ return data.getHost();
+ }
+ return null;
+ }
+ };
+
+ public static final ValueProvider PATH = new ValueProvider("path") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+ String resolvedType, ApplicationInfo resolvedApp) {
+ Uri data = intent.getData();
+ if (data != null) {
+ return data.getPath();
+ }
+ return null;
+ }
+ };
+}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index afdd294b4b06..30d98ebb5438 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -5383,8 +5383,9 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@Override
- protected String packageForFilter(PackageParser.ActivityIntentInfo info) {
- return info.activity.owner.packageName;
+ protected boolean isPackageForFilter(String packageName,
+ PackageParser.ActivityIntentInfo info) {
+ return packageName.equals(info.activity.owner.packageName);
}
@Override
@@ -5580,8 +5581,9 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@Override
- protected String packageForFilter(PackageParser.ServiceIntentInfo info) {
- return info.service.owner.packageName;
+ protected boolean isPackageForFilter(String packageName,
+ PackageParser.ServiceIntentInfo info) {
+ return packageName.equals(info.service.owner.packageName);
}
@Override
diff --git a/services/java/com/android/server/pm/PreferredIntentResolver.java b/services/java/com/android/server/pm/PreferredIntentResolver.java
index 3f1e50c82788..7fe6a05aa325 100644
--- a/services/java/com/android/server/pm/PreferredIntentResolver.java
+++ b/services/java/com/android/server/pm/PreferredIntentResolver.java
@@ -27,8 +27,8 @@ public class PreferredIntentResolver
return new PreferredActivity[size];
}
@Override
- protected String packageForFilter(PreferredActivity filter) {
- return filter.mPref.mComponent.getPackageName();
+ protected boolean isPackageForFilter(String packageName, PreferredActivity filter) {
+ return packageName.equals(filter.mPref.mComponent.getPackageName());
}
@Override
protected void dumpFilter(PrintWriter out, String prefix,