diff options
author | Nicolas Prevot <nprevot@google.com> | 2014-03-24 13:44:38 +0000 |
---|---|---|
committer | Nicolas Prevot <nprevot@google.com> | 2014-04-30 19:24:52 +0100 |
commit | 10fa67c77e11699391e27975fc2d276a0b8c7cbb (patch) | |
tree | 8a3349d2424f13e5c89ea710da84aaec81a42d70 /services | |
parent | dfacf855fd91eb2d40891721ad4b92ed65d0b46d (diff) | |
download | frameworks_base-10fa67c77e11699391e27975fc2d276a0b8c7cbb.zip frameworks_base-10fa67c77e11699391e27975fc2d276a0b8c7cbb.tar.gz frameworks_base-10fa67c77e11699391e27975fc2d276a0b8c7cbb.tar.bz2 |
Introduce forwarding intents across profiles.
The package manager service maintains, for some user ids, a list of forwarding intent filters.
A forwarding intent filter is an intent filter with a destination (a user id).
If an intent matches the forwarding intent filter, then activities in the destination can also respond to the intent.
When the package manager service is asked for components that resolve an intent:
If the intent matches the forwarding intent filter, and at least one activity in the destination user can respond to the intent:
The package manager service also returns the IntentForwarderActivity.
This activity will forward the intent to the destination.
Change-Id: Id8957de3e4a4fdbc1e0dea073eadb45e04ef985a
Diffstat (limited to 'services')
5 files changed, 374 insertions, 1 deletions
diff --git a/services/core/java/com/android/server/pm/ForwardingIntentFilter.java b/services/core/java/com/android/server/pm/ForwardingIntentFilter.java new file mode 100644 index 0000000..aba796b --- /dev/null +++ b/services/core/java/com/android/server/pm/ForwardingIntentFilter.java @@ -0,0 +1,102 @@ +/* + * Copyright 2014, 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.pm; + +import com.android.internal.util.XmlUtils; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; +import android.content.IntentFilter; +import android.util.Log; +import java.io.IOException; +import android.os.UserHandle; + +/** + * The {@link PackageManagerService} maintains some {@link ForwardingIntentFilter}s for every user. + * If an {@link Intent} matches the {@link ForwardingIntentFilter}, then it can be forwarded to the + * {@link #mUserIdDest}. + */ +class ForwardingIntentFilter extends IntentFilter { + private static final String ATTR_USER_ID_DEST = "userIdDest"; + private static final String ATTR_FILTER = "filter"; + + private static final String TAG = "ForwardingIntentFilter"; + + // If the intent matches the IntentFilter, then it can be forwarded to this userId. + final int mUserIdDest; + + ForwardingIntentFilter(IntentFilter filter, int userIdDest) { + super(filter); + mUserIdDest = userIdDest; + } + + public int getUserIdDest() { + return mUserIdDest; + } + + ForwardingIntentFilter(XmlPullParser parser) throws XmlPullParserException, IOException { + String userIdDestString = parser.getAttributeValue(null, ATTR_USER_ID_DEST); + if (userIdDestString == null) { + String msg = "Missing element under " + TAG +": " + ATTR_USER_ID_DEST + " at " + + parser.getPositionDescription(); + PackageManagerService.reportSettingsProblem(Log.WARN, msg); + mUserIdDest = UserHandle.USER_NULL; + } else { + mUserIdDest = Integer.parseInt(userIdDestString); + } + int outerDepth = parser.getDepth(); + String tagName = parser.getName(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + tagName = parser.getName(); + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } else if (type == XmlPullParser.START_TAG) { + if (tagName.equals(ATTR_FILTER)) { + break; + } else { + String msg = "Unknown element under " + Settings.TAG_FORWARDING_INTENT_FILTERS + + ": " + tagName + " at " + parser.getPositionDescription(); + PackageManagerService.reportSettingsProblem(Log.WARN, msg); + XmlUtils.skipCurrentTag(parser); + } + } + } + if (tagName.equals(ATTR_FILTER)) { + readFromXml(parser); + } else { + String msg = "Missing element under " + TAG + ": " + ATTR_FILTER + + " at " + parser.getPositionDescription(); + PackageManagerService.reportSettingsProblem(Log.WARN, msg); + XmlUtils.skipCurrentTag(parser); + } + } + + public void writeToXml(XmlSerializer serializer) throws IOException { + serializer.attribute(null, ATTR_USER_ID_DEST, Integer.toString(mUserIdDest)); + serializer.startTag(null, ATTR_FILTER); + super.writeToXml(serializer); + serializer.endTag(null, ATTR_FILTER); + } + + @Override + public String toString() { + return "ForwardingIntentFilter{0x" + Integer.toHexString(System.identityHashCode(this)) + + " " + Integer.toString(mUserIdDest) + "}"; + } +} diff --git a/services/core/java/com/android/server/pm/ForwardingIntentResolver.java b/services/core/java/com/android/server/pm/ForwardingIntentResolver.java new file mode 100644 index 0000000..1616395 --- /dev/null +++ b/services/core/java/com/android/server/pm/ForwardingIntentResolver.java @@ -0,0 +1,44 @@ +/* + * Copyright 2014, 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.pm; + + +import java.io.PrintWriter; +import com.android.server.IntentResolver; +import java.util.List; + +/** + * Used to find a list of {@link ForwardingIntentFilter}s that match an intent. + */ +class ForwardingIntentResolver + extends IntentResolver<ForwardingIntentFilter, ForwardingIntentFilter> { + @Override + protected ForwardingIntentFilter[] newArray(int size) { + return new ForwardingIntentFilter[size]; + } + + @Override + protected boolean isPackageForFilter(String packageName, ForwardingIntentFilter filter) { + return false; + } + + @Override + protected void sortResults(List<ForwardingIntentFilter> results) { + //We don't sort the results + } +} diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index d0412ef..87c0935 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -28,6 +28,8 @@ import static android.system.OsConstants.S_IRGRP; import static android.system.OsConstants.S_IXGRP; import static android.system.OsConstants.S_IROTH; import static android.system.OsConstants.S_IXOTH; +import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER; +import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE; import static com.android.internal.util.ArrayUtils.appendInt; import static com.android.internal.util.ArrayUtils.removeInt; @@ -3124,6 +3126,33 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + /* + * Returns if intent can be forwarded from the userId from to dest + */ + @Override + public boolean canForwardTo(Intent intent, String resolvedType, int userIdFrom, int userIdDest) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); + List<ForwardingIntentFilter> matches = + getMatchingForwardingIntentFilters(intent, resolvedType, userIdFrom); + if (matches != null) { + int size = matches.size(); + for (int i = 0; i < size; i++) { + if (matches.get(i).getUserIdDest() == userIdDest) return true; + } + } + return false; + } + + private List<ForwardingIntentFilter> getMatchingForwardingIntentFilters(Intent intent, + String resolvedType, int userId) { + ForwardingIntentResolver fir = mSettings.mForwardingIntentResolvers.get(userId); + if (fir != null) { + return fir.queryIntent(intent, resolvedType, false, userId); + } + return null; + } + @Override public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) { @@ -3152,7 +3181,38 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { final String pkgName = intent.getPackage(); if (pkgName == null) { - return mActivities.queryIntent(intent, resolvedType, flags, userId); + List<ResolveInfo> result = + mActivities.queryIntent(intent, resolvedType, flags, userId); + // Checking if we can forward the intent to another user + List<ForwardingIntentFilter> fifs = + getMatchingForwardingIntentFilters(intent, resolvedType, userId); + if (fifs != null) { + ForwardingIntentFilter forwardingIntentFilterWithResult = null; + HashSet<Integer> alreadyTriedUserIds = new HashSet<Integer>(); + for (ForwardingIntentFilter fif : fifs) { + int userIdDest = fif.getUserIdDest(); + // Two {@link ForwardingIntentFilter}s can have the same userIdDest and + // match the same an intent. For performance reasons, it is better not to + // run queryIntent twice for the same userId + if (!alreadyTriedUserIds.contains(userIdDest)) { + List<ResolveInfo> resultUser = mActivities.queryIntent(intent, + resolvedType, flags, userIdDest); + if (resultUser != null) { + forwardingIntentFilterWithResult = fif; + // As soon as there is a match in another user, we add the + // intentForwarderActivity to the list of ResolveInfo. + break; + } + alreadyTriedUserIds.add(userIdDest); + } + } + if (forwardingIntentFilterWithResult != null) { + ResolveInfo forwardingResolveInfo = createForwardingResolveInfo( + forwardingIntentFilterWithResult, userId); + result.add(forwardingResolveInfo); + } + } + return result; } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { @@ -3163,6 +3223,28 @@ public class PackageManagerService extends IPackageManager.Stub { } } + private ResolveInfo createForwardingResolveInfo(ForwardingIntentFilter fif, int userIdFrom) { + String className; + int userIdDest = fif.getUserIdDest(); + if (userIdDest == UserHandle.USER_OWNER) { + className = FORWARD_INTENT_TO_USER_OWNER; + } else { + className = FORWARD_INTENT_TO_MANAGED_PROFILE; + } + ComponentName forwardingActivityComponentName = new ComponentName( + mAndroidApplication.packageName, className); + ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0, + userIdFrom); + ResolveInfo forwardingResolveInfo = new ResolveInfo(); + forwardingResolveInfo.activityInfo = forwardingActivityInfo; + forwardingResolveInfo.priority = 0; + forwardingResolveInfo.preferredOrder = 0; + forwardingResolveInfo.match = 0; + forwardingResolveInfo.isDefault = true; + forwardingResolveInfo.filter = fif; + return forwardingResolveInfo; + } + @Override public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, @@ -10817,6 +10899,47 @@ public class PackageManagerService extends IPackageManager.Stub { } } + /* + * For filters that are added with this method: + * if an intent for the user whose id is userIdOrig matches the filter, then this intent can + * also be resolved in the user whose id is userIdDest. + */ + @Override + public void addForwardingIntentFilter(IntentFilter filter, int userIdOrig, int userIdDest) { + int callingUid = Binder.getCallingUid(); + if (callingUid != Process.SYSTEM_UID) { + throw new SecurityException( + "addForwardingIntentFilter can only be run by the system"); + } + if (filter.countActions() == 0) { + Slog.w(TAG, "Cannot set a forwarding intent filter with no filter actions"); + return; + } + synchronized (mPackages) { + mSettings.editForwardingIntentResolverLPw(userIdOrig).addFilter( + new ForwardingIntentFilter(filter, userIdDest)); + mSettings.writePackageRestrictionsLPr(userIdOrig); + } + } + + @Override + public void clearForwardingIntentFilters(int userIdOrig) { + int callingUid = Binder.getCallingUid(); + if (callingUid != Process.SYSTEM_UID) { + throw new SecurityException( + "clearForwardingIntentFilter can only be run by the system"); + } + synchronized (mPackages) { + ForwardingIntentResolver fir = mSettings.editForwardingIntentResolverLPw(userIdOrig); + HashSet<ForwardingIntentFilter> set = + new HashSet<ForwardingIntentFilter>(fir.filterSet()); + for (ForwardingIntentFilter fif : set) { + fir.removeFilter(fif); + } + mSettings.writePackageRestrictionsLPr(userIdOrig); + } + } + @Override public ComponentName getHomeActivities(List<ResolveInfo> allHomeCandidates) { Intent intent = new Intent(Intent.ACTION_MAIN); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index e4dd2d4..fca3933 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -130,6 +130,8 @@ final class Settings { private static final String TAG_PACKAGE = "pkg"; private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES = "persistent-preferred-activities"; + static final String TAG_FORWARDING_INTENT_FILTERS = + "forwarding-intent-filters"; private static final String ATTR_NAME = "name"; private static final String ATTR_USER = "user"; @@ -184,6 +186,10 @@ final class Settings { final SparseArray<PersistentPreferredIntentResolver> mPersistentPreferredActivities = new SparseArray<PersistentPreferredIntentResolver>(); + // For every user, it is used to find to which other users the intent can be forwarded. + final SparseArray<ForwardingIntentResolver> mForwardingIntentResolvers = + new SparseArray<ForwardingIntentResolver>(); + final HashMap<String, SharedUserSetting> mSharedUsers = new HashMap<String, SharedUserSetting>(); private final ArrayList<Object> mUserIds = new ArrayList<Object>(); @@ -831,6 +837,15 @@ final class Settings { return ppir; } + ForwardingIntentResolver editForwardingIntentResolverLPw(int userId) { + ForwardingIntentResolver fir = mForwardingIntentResolvers.get(userId); + if (fir == null) { + fir = new ForwardingIntentResolver(); + mForwardingIntentResolvers.put(userId, fir); + } + return fir; + } + private File getUserPackagesStateFile(int userId) { return new File(Environment.getUserSystemDirectory(userId), "package-restrictions.xml"); } @@ -946,6 +961,28 @@ final class Settings { } } + private void readForwardingIntentFiltersLPw(XmlPullParser parser, int userId) + throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + String tagName = parser.getName(); + if (tagName.equals(TAG_ITEM)) { + ForwardingIntentFilter fif = new ForwardingIntentFilter(parser); + editForwardingIntentResolverLPw(userId).addFilter(fif); + } else { + String msg = "Unknown element under " + TAG_FORWARDING_INTENT_FILTERS + ": " + + parser.getName(); + PackageManagerService.reportSettingsProblem(Log.WARN, msg); + XmlUtils.skipCurrentTag(parser); + } + } + } + void readPackageRestrictionsLPr(int userId) { if (DEBUG_MU) { Log.i(TAG, "Reading package restrictions for user=" + userId); @@ -1074,6 +1111,8 @@ final class Settings { readPreferredActivitiesLPw(parser, userId); } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) { readPersistentPreferredActivitiesLPw(parser, userId); + } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)) { + readForwardingIntentFiltersLPw(parser, userId); } else { Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: " + parser.getName()); @@ -1151,6 +1190,20 @@ final class Settings { serializer.endTag(null, TAG_PERSISTENT_PREFERRED_ACTIVITIES); } + void writeForwardingIntentFiltersLPr(XmlSerializer serializer, int userId) + throws IllegalArgumentException, IllegalStateException, IOException { + serializer.startTag(null, TAG_FORWARDING_INTENT_FILTERS); + ForwardingIntentResolver fir = mForwardingIntentResolvers.get(userId); + if (fir != null) { + for (final ForwardingIntentFilter fif : fir.filterSet()) { + serializer.startTag(null, TAG_ITEM); + fif.writeToXml(serializer); + serializer.endTag(null, TAG_ITEM); + } + } + serializer.endTag(null, TAG_FORWARDING_INTENT_FILTERS); + } + void writePackageRestrictionsLPr(int userId) { if (DEBUG_MU) { Log.i(TAG, "Writing package restrictions for user=" + userId); @@ -1249,6 +1302,8 @@ final class Settings { writePersistentPreferredActivitiesLPr(serializer, userId); + writeForwardingIntentFiltersLPr(serializer, userId); + serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS); serializer.endDocument(); @@ -1866,6 +1921,10 @@ final class Settings { // TODO: check whether this is okay! as it is very // similar to how preferred-activities are treated readPersistentPreferredActivitiesLPw(parser, 0); + } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)) { + // TODO: check whether this is okay! as it is very + // similar to how preferred-activities are treated + readForwardingIntentFiltersLPw(parser, 0); } else if (tagName.equals("updated-package")) { readDisabledSysPackageLPw(parser); } else if (tagName.equals("cleaning-package")) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index f1ee280..8e1f82a 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3099,6 +3099,51 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + public void forwardMatchingIntents(ComponentName who, IntentFilter filter, int flags) { + int callingUserId = UserHandle.getCallingUserId(); + synchronized (this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + + IPackageManager pm = AppGlobals.getPackageManager(); + long id = Binder.clearCallingIdentity(); + try { + if ((flags & DevicePolicyManager.FLAG_TO_PRIMARY_USER) != 0) { + pm.addForwardingIntentFilter(filter, callingUserId, UserHandle.USER_OWNER); + } + if ((flags & DevicePolicyManager.FLAG_TO_MANAGED_PROFILE) != 0) { + pm.addForwardingIntentFilter(filter, UserHandle.USER_OWNER, callingUserId); + } + } catch (RemoteException re) { + // Shouldn't happen + } finally { + restoreCallingIdentity(id); + } + } + } + + public void clearForwardingIntentFilters(ComponentName who) { + int callingUserId = UserHandle.getCallingUserId(); + synchronized (this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + IPackageManager pm = AppGlobals.getPackageManager(); + long id = Binder.clearCallingIdentity(); + try { + pm.clearForwardingIntentFilters(callingUserId); + pm.clearForwardingIntentFilters(UserHandle.USER_OWNER); + } catch (RemoteException re) { + // Shouldn't happen + } finally { + restoreCallingIdentity(id); + } + } + } + @Override public Bundle getApplicationRestrictions(ComponentName who, String packageName) { final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId()); |