diff options
author | Ben Gruver <bgruv@google.com> | 2013-04-03 23:36:22 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-04-03 23:36:22 +0000 |
commit | 8c3ee9d50aadb0140e36000b2217f2070404b07c (patch) | |
tree | 78d4c029ea42691d5cecf757ec0af100beee1b33 | |
parent | 0f8e8b03ebb35040c299079adc1c351f37ce4885 (diff) | |
parent | 4efe9403afb0ba3b83fa647eb82e4f90d29f131b (diff) | |
download | frameworks_base-8c3ee9d50aadb0140e36000b2217f2070404b07c.zip frameworks_base-8c3ee9d50aadb0140e36000b2217f2070404b07c.tar.gz frameworks_base-8c3ee9d50aadb0140e36000b2217f2070404b07c.tar.bz2 |
Merge "Initial implementation of IntentFirewall functionality" into jb-mr2-dev
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, |