diff options
-rw-r--r-- | api/current.txt | 4 | ||||
-rw-r--r-- | core/java/android/app/admin/DevicePolicyManager.java | 43 | ||||
-rw-r--r-- | core/java/android/app/admin/IDevicePolicyManager.aidl | 2 | ||||
-rw-r--r-- | core/java/android/content/pm/IPackageManager.aidl | 6 | ||||
-rw-r--r-- | core/java/com/android/internal/app/IntentForwarderActivity.java | 112 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 20 | ||||
-rw-r--r-- | core/res/res/drawable-hdpi/personal_icon.png | bin | 0 -> 1560 bytes | |||
-rw-r--r-- | core/res/res/drawable-hdpi/work_icon.png | bin | 0 -> 1153 bytes | |||
-rw-r--r-- | core/res/res/values/strings.xml | 6 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/ForwardingIntentFilter.java | 102 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/ForwardingIntentResolver.java | 44 | ||||
-rwxr-xr-x | services/core/java/com/android/server/pm/PackageManagerService.java | 125 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/Settings.java | 59 | ||||
-rw-r--r-- | services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java | 45 |
14 files changed, 567 insertions, 1 deletions
diff --git a/api/current.txt b/api/current.txt index 54c9d90..605e786 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4985,8 +4985,10 @@ package android.app.admin { public class DevicePolicyManager { method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName); method public void addUserRestriction(android.content.ComponentName, java.lang.String); + method public void clearForwardingIntentFilters(android.content.ComponentName); method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String); method public void clearUserRestriction(android.content.ComponentName, java.lang.String); + method public void forwardMatchingIntents(android.content.ComponentName, android.content.IntentFilter, int); method public java.util.List<android.content.ComponentName> getActiveAdmins(); method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String); method public boolean getCameraDisabled(android.content.ComponentName); @@ -5046,6 +5048,8 @@ package android.app.admin { field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN"; field public static final java.lang.String EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME = "defaultManagedProfileName"; field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "deviceAdminPackageName"; + field public static int FLAG_TO_MANAGED_PROFILE; + field public static int FLAG_TO_PRIMARY_USER; field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff field public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; // 0x0 field public static final int KEYGUARD_DISABLE_SECURE_CAMERA = 2; // 0x2 diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 68ab611..73e5b2a 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -172,6 +172,16 @@ public class DevicePolicyManager { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD"; + /** + * Flag for {@link #forwardMatchingIntents}: the intents will forwarded to the primary user. + */ + public static int FLAG_TO_PRIMARY_USER = 0x0001; + + /** + * Flag for {@link #forwardMatchingIntents}: the intents will be forwarded to the managed + * profile. + */ + public static int FLAG_TO_MANAGED_PROFILE = 0x0002; /** * Return true if the given administrator component is currently @@ -1953,6 +1963,39 @@ public class DevicePolicyManager { } /** + * Called by a profile owner to forward intents sent from the managed profile to the owner, or + * from the owner to the managed profile. + * If an intent matches this intent filter, then activities belonging to the other user can + * respond to this intent. + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param filter if an intent matches this IntentFilter, then it can be forwarded. + */ + public void forwardMatchingIntents(ComponentName admin, IntentFilter filter, int flags) { + if (mService != null) { + try { + mService.forwardMatchingIntents(admin, filter, flags); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + } + + /** + * Called by a profile owner to remove all the forwarding intent filters from the current user + * and from the owner. + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + */ + public void clearForwardingIntentFilters(ComponentName admin) { + if (mService != null) { + try { + mService.clearForwardingIntentFilters(admin); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + } + + /** * Called by a profile or device owner to get the application restrictions for a given target * application running in the managed profile. * diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 72b3c20..eaf4016 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -121,4 +121,6 @@ interface IDevicePolicyManager { Bundle getApplicationRestrictions(in ComponentName who, in String packageName); void setUserRestriction(in ComponentName who, in String key, boolean enable); + void forwardMatchingIntents(in ComponentName admin, in IntentFilter filter, int flags); + void clearForwardingIntentFilters(in ComponentName admin); } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 488e25f..cf9a296 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -111,6 +111,8 @@ interface IPackageManager { ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags, int userId); + boolean canForwardTo(in Intent intent, String resolvedType, int userIdFrom, int userIdDest); + List<ResolveInfo> queryIntentActivities(in Intent intent, String resolvedType, int flags, int userId); @@ -245,6 +247,10 @@ interface IPackageManager { void clearPackagePersistentPreferredActivities(String packageName, int userId); + void addForwardingIntentFilter(in IntentFilter filter, int userIdOrig, int userIdDest); + + void clearForwardingIntentFilters(int userIdOrig); + /** * Report the set of 'Home' activity candidates, plus (if any) which of them * is the current "always use this one" setting. diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java new file mode 100644 index 0000000..2f74372 --- /dev/null +++ b/core/java/com/android/internal/app/IntentForwarderActivity.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 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.internal.app; + +import android.app.Activity; +import android.app.AppGlobals; +import android.os.Bundle; +import android.content.Context; +import android.content.Intent; +import android.content.pm.IPackageManager; +import android.content.pm.UserInfo; +import android.os.UserHandle; +import android.os.UserManager; +import android.app.ActivityManagerNative; +import android.os.RemoteException; +import android.util.Slog; +import java.util.List; +import java.util.Set; + + + + +/* + * This is used in conjunction with DevicePolicyManager.setForwardingIntents to enable intents to be + * passed in and out of a managed profile. + */ + +public class IntentForwarderActivity extends Activity { + + public static String TAG = "IntentForwarderActivity"; + + public static String FORWARD_INTENT_TO_USER_OWNER + = "com.android.internal.app.ForwardIntentToUserOwner"; + + public static String FORWARD_INTENT_TO_MANAGED_PROFILE + = "com.android.internal.app.ForwardIntentToManagedProfile"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intentReceived = getIntent(); + + String className = intentReceived.getComponent().getClassName(); + final UserHandle userDest; + + if (className.equals(FORWARD_INTENT_TO_USER_OWNER)) { + userDest = UserHandle.OWNER; + } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) { + userDest = getManagedProfile(); + } else { + Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly"); + userDest = null; + } + if (userDest == null) { // This covers the case where there is no managed profile. + finish(); + return; + } + Intent newIntent = new Intent(intentReceived); + newIntent.setComponent(null); + newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT + |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); + int callingUserId = getUserId(); + IPackageManager ipm = AppGlobals.getPackageManager(); + String resolvedType = newIntent.resolveTypeIfNeeded(getContentResolver()); + boolean canForward = false; + try { + canForward = ipm.canForwardTo(newIntent, resolvedType, callingUserId, + userDest.getIdentifier()); + } catch (RemoteException e) { + Slog.e(TAG, "PackageManagerService is dead?"); + } + if (canForward) { + startActivityAsUser(newIntent, userDest); + } else { + Slog.wtf(TAG, "the intent: " + newIntent + "cannot be forwarded from user " + + callingUserId + " to user " + userDest.getIdentifier()); + } + finish(); + } + + /** + * Returns the managed profile for this device or null if there is no managed + * profile. + * + * TODO: Remove the assumption that there is only one managed profile + * on the device. + */ + private UserHandle getManagedProfile() { + UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); + List<UserInfo> relatedUsers = userManager.getProfiles(UserHandle.USER_OWNER); + for (UserInfo userInfo : relatedUsers) { + if (userInfo.isManagedProfile()) return new UserHandle(userInfo.id); + } + Slog.wtf(TAG, FORWARD_INTENT_TO_MANAGED_PROFILE + + " has been called, but there is no managed profile"); + return null; + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 4f093a8..3d3e86f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2681,6 +2681,26 @@ <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> + <activity android:name="com.android.internal.app.IntentForwarderActivity" + android:finishOnCloseSystemDialogs="true" + android:theme="@style/Theme.NoDisplay" + android:excludeFromRecents="true" + android:label="@string/user_owner_label" + android:exported="true" + > + </activity> + <activity-alias android:name="com.android.internal.app.ForwardIntentToUserOwner" + android:targetActivity="com.android.internal.app.IntentForwarderActivity" + android:icon="@drawable/personal_icon" + android:exported="true" + android:label="@string/user_owner_label"> + </activity-alias> + <activity-alias android:name="com.android.internal.app.ForwardIntentToManagedProfile" + android:targetActivity="com.android.internal.app.IntentForwarderActivity" + android:icon="@drawable/work_icon" + android:exported="true" + android:label="@string/managed_profile_label"> + </activity-alias> <activity android:name="com.android.internal.app.HeavyWeightSwitcherActivity" android:theme="@style/Theme.Holo.Dialog" android:label="@string/heavy_weight_switcher_title" diff --git a/core/res/res/drawable-hdpi/personal_icon.png b/core/res/res/drawable-hdpi/personal_icon.png Binary files differnew file mode 100644 index 0000000..8d96b5e --- /dev/null +++ b/core/res/res/drawable-hdpi/personal_icon.png diff --git a/core/res/res/drawable-hdpi/work_icon.png b/core/res/res/drawable-hdpi/work_icon.png Binary files differnew file mode 100644 index 0000000..e90866b --- /dev/null +++ b/core/res/res/drawable-hdpi/work_icon.png diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 57b2c01..97400b2 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -454,6 +454,12 @@ <!-- Label for the Android system components when they are shown to the user. --> <string name="android_system_label">Android System</string> + <!-- Label for the user owner in the intent forwarding app. --> + <string name="user_owner_label">Personal</string> + + <!-- Label for a corporate profile in the intent forwarding app. --> + <string name="managed_profile_label">Work</string> + <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_costMoney">Services that cost you money</string> <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> 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()); |