summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Gruver <bgruv@google.com>2013-04-03 23:36:22 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2013-04-03 23:36:22 +0000
commit8c3ee9d50aadb0140e36000b2217f2070404b07c (patch)
tree78d4c029ea42691d5cecf757ec0af100beee1b33
parent0f8e8b03ebb35040c299079adc1c351f37ce4885 (diff)
parent4efe9403afb0ba3b83fa647eb82e4f90d29f131b (diff)
downloadframeworks_base-8c3ee9d50aadb0140e36000b2217f2070404b07c.zip
frameworks_base-8c3ee9d50aadb0140e36000b2217f2070404b07c.tar.gz
frameworks_base-8c3ee9d50aadb0140e36000b2217f2070404b07c.tar.bz2
Merge "Initial implementation of IntentFirewall functionality" into jb-mr2-dev
-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 9b19008..35345f5 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 4dd77ce..94a2379 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 97fbb9c..7710f13 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);
}
};
@@ -1472,7 +1475,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);
@@ -4948,6 +4952,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 526b24f..3d7da7b 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 0000000..cabf00b
--- /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 0000000..d5e9fe8
--- /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 0000000..7639466
--- /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 0000000..dea8b40
--- /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 0000000..d34b203
--- /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 0000000..ebbbd86
--- /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 0000000..2ff108a
--- /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 0000000..1ed1c85
--- /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 0000000..2b2a198
--- /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 0000000..0b790bd
--- /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 0000000..02d8b15
--- /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 0000000..de5a69f
--- /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 f95d53c..ca7bba2 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -5410,8 +5410,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
@@ -5607,8 +5608,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 3f1e50c..7fe6a05 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,