From 10fa67c77e11699391e27975fc2d276a0b8c7cbb Mon Sep 17 00:00:00 2001 From: Nicolas Prevot Date: Mon, 24 Mar 2014 13:44:38 +0000 Subject: [PATCH] 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 --- api/current.txt | 4 + .../android/app/admin/DevicePolicyManager.java | 43 +++++++ .../android/app/admin/IDevicePolicyManager.aidl | 2 + core/java/android/content/pm/IPackageManager.aidl | 6 + .../internal/app/IntentForwarderActivity.java | 112 ++++++++++++++++++ core/res/AndroidManifest.xml | 20 ++++ core/res/res/drawable-hdpi/personal_icon.png | Bin 0 -> 1560 bytes core/res/res/drawable-hdpi/work_icon.png | Bin 0 -> 1153 bytes core/res/res/values/strings.xml | 6 + .../android/server/pm/ForwardingIntentFilter.java | 102 +++++++++++++++++ .../server/pm/ForwardingIntentResolver.java | 44 ++++++++ .../android/server/pm/PackageManagerService.java | 125 ++++++++++++++++++++- .../core/java/com/android/server/pm/Settings.java | 59 ++++++++++ .../devicepolicy/DevicePolicyManagerService.java | 45 ++++++++ 14 files changed, 567 insertions(+), 1 deletion(-) create mode 100644 core/java/com/android/internal/app/IntentForwarderActivity.java create mode 100644 core/res/res/drawable-hdpi/personal_icon.png create mode 100644 core/res/res/drawable-hdpi/work_icon.png create mode 100644 services/core/java/com/android/server/pm/ForwardingIntentFilter.java create mode 100644 services/core/java/com/android/server/pm/ForwardingIntentResolver.java diff --git a/api/current.txt b/api/current.txt index 54c9d90a7912..605e78695f19 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 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 68ab611fd777..73e5b2aeb82b 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 72b3c205902c..eaf4016d477c 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 488e25fb0dee..cf9a2961ec10 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 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 000000000000..2f743720d2dd --- /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 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 4f093a874598..3d3e86face14 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2681,6 +2681,26 @@ + + + + + + N9)j0@BjI}m z-G*PrAx3Z4Wt%#|(NLm{Xhh5kl{M@C1OGg#8Qv&%>!nWJUrR%bz*v$d_JXQ&RJAxe z+o~zv^b}+IdSMs_0fWEI()c1StVNBxGkXH^vjg2q$^}XMAydEwkhM;b#~a zo?(niTE?MIn>&IvS5mlY7O|lo7>0pg^kmX^yx(zL8;W_dmlxmKS-`3p4-#Y;7+#S~ zNZ-vlYe#*s-+GsvA0{v?q}`9<6Un&mze4F(o%`F_>1l_^c3C^eA|e;7v{90yUZjOLBCe=&*Jy9w%RVYj(jGvPqKVxMuq!Ord)8*{#aiyjcg{*`sMbXkh9ly?OMc zj_!X+vmz1kn{=T?Qrk5h{%*J+S}AMaP|>kN%2_mCewg&|x-xw!NtRPe4MY&CX)6z^ z^!x>tytY7tK|@xQsY#M-mCBv!ErO@(#l3|(apH*bGLsbvidQxRnHq|r@xQvHR+nE#old1k2;W2< ztg*WZ@l43qHM=YJb?uwnv6%Lnx3qB5SpDD+HL|F*AFN;01+6cgryz;9>2&($*zOo#5~{S*RyQFr%RhGel`J}*;xC48Xy0jW5?WMy`J75l z9@eW_8G0fdF>;G8w)RX&*D4d0#*UX02{(aZnD7b+Cnk0bu>%9~0Kh@xjhnPmMb5+^ za=JL^OXV#aM7DqV_wjeyfvN8t^51=(U#!k1Z&xNoyE4HPP2!q7-dH&WYmEhre)JFW z=;YC}_W*ih<%ZSn?(+`t>2zl>nGBEv0jQDkyl0vJlRccR0|;Q?BNK@+aISC9=%(yG zLeiX626Y{Ag~qQ>Z#;-$(V=|47j?%(!-u{jeLLbBMkm{#`1xcN&L^w&7U$0#VPE!2 zHm;e=PamyhLqrg$o+^tSf;&iQtN)yO0txTcnW&!7T(*wn1$(g1%jGXCzr%!K;rNHxK~ ze@kqL2^#OTG7)Bef+6(_`SG0RX?P)tp`KSM-nO2<`y{Y?QV70L3)z^uoz$h%c&dIq z>Ei>q^2aSKd$ooUD>g7Q!UWc@%Fct}QRC>xF1G*t*Tg?##=p9hH#e?ke=QJXrR8pn zbP?6=%5HaPxuT-X6vc;mv5t}~gs2FNfb@6)59UUPr5cn!; z!aQZaeN%23ZMvj`%VubJaJ!fX44R_!ZEx#_Yl2f$A6!?i4*j&~);GasRb@2mS?jyae=751;%10000< KMNUMnLSTaQH3=#J literal 0 HcmV?d00001 diff --git a/core/res/res/drawable-hdpi/work_icon.png b/core/res/res/drawable-hdpi/work_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e90866becc849972bfe3869a588c4df84a95718a GIT binary patch literal 1153 zcmV-{1b+L8P)sMlGRm6E z(P~kD2qo6knQduUZH=L})`k}*FM|ls2pU`r2@t_c#Ykv=u3-fArAj~oXc~*r zIc@c&x)T*0KtS?i2z0zl$R)}Qxq3?qf^iL19cr`52?gZs5B+0*{| zJrrtpY~B$G=|9V8fpoq<1zWwfxKg9U!ilSYk)wHT9dbw5b*Y z4)NIapzA#K@3?R<712KS5JEs2xCxp03QQR1lc}n))5RH&d~zJm1UoVs@0cb7VdukbStoev*fGkl zRx!gB4A1MLPCvqm*X|<_!K~^r>S>V$l6v-^UQbA#V%Y>qT^B!$5M1-tWooGBz?boa zWIId7jpR%d_um7C=5|m^QrnTsPzWZR?B%R^(-W-8^dltOSu|=eK}tc9Sim8}e9Tmh z3G#!Vcqu2A4hZ7QCBvMftzbRSwjX2&0+^+kp?=^C#@l;w&qWnUQrC5u5ipHsl^-)& z_;Fw3G)V)83LYj9%hUaOo8;;r_tFPG+*_|ULk7LmO3gyRJft)%giZiuPnYnfk&9&0 zC!Ir*vCU@~^@=|+)+|T6(+2EX=K=t53Qk6n{{$MUdogmQ64zZ4k@kQLfC1{!DL{C_ z-`(Ek!aH~bzyoTGsSV~inYRqKQGiA{hUhH;m?_Q1iPo9FFC0^bwMb2nLRoVeO~oxR z#AG1eQt=-|#3G2* zua~2){97DYqr{O{PNGY9CrIIexU{vHYxx=lKd2C%wh4ab2v@|UCqUBu4bEItAtG%f zR@s?S_{2YsP_&`&LLbb1os(!jQ;A!SF-Q)(bCYuO_l49w5xM!#!>cU^`@Z@OHvfm) z&tKR1KEnV4fqeUCjg6=8|C|p21cI5}YUSyNS(M>zs_wjVEUzkN{u>+U0x;^mW~z)d z-^r+>J(l8HD4e`5>PT{KfQ3m?LYBg_gO>Zf2$Iu{eAO3(*B;K-jMWaV@=!Pk?-zOL zc2CpGqU Android System + + Personal + + + Work + Services that cost you money 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 000000000000..aba796b230ca --- /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 000000000000..1616395428fd --- /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 { + @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 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 d0412efd3bb8..87c09353690d 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 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 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 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 result = + mActivities.queryIntent(intent, resolvedType, flags, userId); + // Checking if we can forward the intent to another user + List fifs = + getMatchingForwardingIntentFilters(intent, resolvedType, userId); + if (fifs != null) { + ForwardingIntentFilter forwardingIntentFilterWithResult = null; + HashSet alreadyTriedUserIds = new HashSet(); + 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 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 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 set = + new HashSet(fir.filterSet()); + for (ForwardingIntentFilter fif : set) { + fir.removeFilter(fif); + } + mSettings.writePackageRestrictionsLPr(userIdOrig); + } + } + @Override public ComponentName getHomeActivities(List 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 e4dd2d49f69f..fca393302694 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 mPersistentPreferredActivities = new SparseArray(); + // For every user, it is used to find to which other users the intent can be forwarded. + final SparseArray mForwardingIntentResolvers = + new SparseArray(); + final HashMap mSharedUsers = new HashMap(); private final ArrayList mUserIds = new ArrayList(); @@ -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 : " + 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 f1ee280257f4..8e1f82aa38a5 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()); -- 2.11.0