1 /* 2 * Copyright 2014, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.pm; 18 19 import android.annotation.IntDef; 20 import android.content.Intent; 21 import android.content.IntentFilter; 22 import android.os.UserHandle; 23 import android.util.Log; 24 25 import com.android.internal.util.XmlUtils; 26 import com.android.modules.utils.TypedXmlPullParser; 27 import com.android.modules.utils.TypedXmlSerializer; 28 import com.android.server.utils.SnapshotCache; 29 30 import org.xmlpull.v1.XmlPullParser; 31 import org.xmlpull.v1.XmlPullParserException; 32 33 import java.io.IOException; 34 import java.lang.annotation.Retention; 35 import java.lang.annotation.RetentionPolicy; 36 37 /** 38 * The {@link PackageManagerService} maintains some {@link CrossProfileIntentFilter}s for each user. 39 * If an {@link Intent} matches the {@link CrossProfileIntentFilter}, then activities in the user 40 * {@link #mTargetUserId} can access it. 41 */ 42 class CrossProfileIntentFilter extends WatchedIntentFilter { 43 private static final String ATTR_TARGET_USER_ID = "targetUserId"; 44 private static final String ATTR_FLAGS = "flags"; 45 private static final String ATTR_OWNER_PACKAGE = "ownerPackage"; 46 private static final String ATTR_FILTER = "filter"; 47 private static final String ATTR_ACCESS_CONTROL = "accessControl"; 48 49 //flag to decide if intent needs to be resolved cross profile if pkgName is already defined 50 public static final int FLAG_IS_PACKAGE_FOR_FILTER = 0x00000008; 51 52 /* 53 This flag, denotes if further cross profile resolution is allowed, e.g. if profile#0 is linked 54 to profile#1 and profile#2 . When intent resolution from profile#1 is started we resolve it in 55 profile#1 and profile#0. The profile#0 is also linked to profile#2, we will only resolve in 56 profile#2 if CrossProfileIntentFilter between profile#1 and profile#0 have set flag 57 FLAG_ALLOW_CHAINED_RESOLUTION. 58 */ 59 public static final int FLAG_ALLOW_CHAINED_RESOLUTION = 0x00000010; 60 61 private static final String TAG = "CrossProfileIntentFilter"; 62 63 /** 64 * AccessControlLevel provides level of access for user to create/modify 65 * {@link CrossProfileIntentFilter} in {@link com.android.server.pm.Settings}. 66 * Each AccessControlLevel have value assigned, the higher the value 67 * implies higher restriction for creation/modification. 68 * AccessControlLevel allows us to protect against malicious changes in user's 69 * {@link CrossProfileIntentFilter}s, which might add/remove {@link CrossProfileIntentFilter} 70 * leading to unprecedented results. 71 */ 72 @IntDef(prefix = {"ACCESS_LEVEL_"}, value = { 73 ACCESS_LEVEL_ALL, 74 ACCESS_LEVEL_SYSTEM, 75 ACCESS_LEVEL_SYSTEM_ADD_ONLY, 76 }) 77 @Retention(RetentionPolicy.SOURCE) 78 public @interface AccessControlLevel { 79 } 80 81 /** 82 * ACCESS_LEVEL_ALL signifies that irrespective of user we would allow 83 * access(addition/modification/removal) for CrossProfileIntentFilter. 84 * This is the default access control level. 85 */ 86 public static final int ACCESS_LEVEL_ALL = 0; 87 88 /** 89 * ACCESS_LEVEL_SYSTEM signifies that only system/root user would be able to 90 * access(addition/modification/removal) CrossProfileIntentFilter. 91 */ 92 public static final int ACCESS_LEVEL_SYSTEM = 10; 93 94 /** 95 * ACCESS_LEVEL_SYSTEM_ADD_ONLY signifies that only system/root user would be able to add 96 * CrossProfileIntentFilter but not modify/remove. Once added, it cannot be modified or removed. 97 */ 98 public static final int ACCESS_LEVEL_SYSTEM_ADD_ONLY = 20; 99 100 // If the intent matches the IntentFilter, then it can be forwarded to this userId. 101 final int mTargetUserId; 102 final String mOwnerPackage; // packageName of the app. 103 final int mFlags; 104 final int mAccessControlLevel; 105 106 // The cache for snapshots, so they are not rebuilt if the base object has not 107 // changed. 108 final SnapshotCache<CrossProfileIntentFilter> mSnapshot; 109 makeCache()110 private SnapshotCache makeCache() { 111 return new SnapshotCache<CrossProfileIntentFilter>(this, this) { 112 @Override 113 public CrossProfileIntentFilter createSnapshot() { 114 CrossProfileIntentFilter s = new CrossProfileIntentFilter(mSource); 115 s.seal(); 116 return s; 117 }}; 118 } 119 120 CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId, 121 int flags) { 122 this(filter, ownerPackage, targetUserId, flags, ACCESS_LEVEL_ALL); 123 } 124 125 CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId, 126 int flags, @AccessControlLevel int accessControlLevel) { 127 super(filter); 128 mTargetUserId = targetUserId; 129 mOwnerPackage = ownerPackage; 130 mFlags = flags; 131 mAccessControlLevel = accessControlLevel; 132 mSnapshot = makeCache(); 133 } 134 135 CrossProfileIntentFilter(WatchedIntentFilter filter, String ownerPackage, int targetUserId, 136 int flags) { 137 this(filter.mFilter, ownerPackage, targetUserId, flags); 138 } 139 140 CrossProfileIntentFilter(WatchedIntentFilter filter, String ownerPackage, int targetUserId, 141 int flags, @AccessControlLevel int accessControlLevel) { 142 this(filter.mFilter, ownerPackage, targetUserId, flags, accessControlLevel); 143 } 144 145 // Copy constructor used only to create a snapshot. 146 private CrossProfileIntentFilter(CrossProfileIntentFilter f) { 147 super(f); 148 mTargetUserId = f.mTargetUserId; 149 mOwnerPackage = f.mOwnerPackage; 150 mFlags = f.mFlags; 151 mAccessControlLevel = f.mAccessControlLevel; 152 mSnapshot = new SnapshotCache.Sealed(); 153 } 154 155 public int getTargetUserId() { 156 return mTargetUserId; 157 } 158 159 public int getFlags() { 160 return mFlags; 161 } 162 163 public String getOwnerPackage() { 164 return mOwnerPackage; 165 } 166 167 public @AccessControlLevel int getAccessControlLevel() { 168 return mAccessControlLevel; 169 } 170 171 CrossProfileIntentFilter(TypedXmlPullParser parser) throws XmlPullParserException, IOException { 172 mTargetUserId = parser.getAttributeInt(null, ATTR_TARGET_USER_ID, UserHandle.USER_NULL); 173 mOwnerPackage = getStringFromXml(parser, ATTR_OWNER_PACKAGE, ""); 174 mAccessControlLevel = parser.getAttributeInt(null, ATTR_ACCESS_CONTROL, ACCESS_LEVEL_ALL); 175 mFlags = parser.getAttributeInt(null, ATTR_FLAGS, 0); 176 mSnapshot = makeCache(); 177 178 int outerDepth = parser.getDepth(); 179 String tagName = parser.getName(); 180 int type; 181 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 182 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 183 tagName = parser.getName(); 184 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 185 continue; 186 } else if (type == XmlPullParser.START_TAG) { 187 if (tagName.equals(ATTR_FILTER)) { 188 break; 189 } else { 190 String msg = "Unknown element under " 191 + Settings.TAG_CROSS_PROFILE_INTENT_FILTERS + ": " + tagName + " at " 192 + parser.getPositionDescription(); 193 PackageManagerService.reportSettingsProblem(Log.WARN, msg); 194 XmlUtils.skipCurrentTag(parser); 195 } 196 } 197 } 198 if (tagName.equals(ATTR_FILTER)) { 199 mFilter.readFromXml(parser); 200 } else { 201 String msg = "Missing element under " + TAG + ": " + ATTR_FILTER + 202 " at " + parser.getPositionDescription(); 203 PackageManagerService.reportSettingsProblem(Log.WARN, msg); 204 XmlUtils.skipCurrentTag(parser); 205 } 206 } 207 208 private String getStringFromXml(TypedXmlPullParser parser, String attribute, 209 String defaultValue) { 210 String value = parser.getAttributeValue(null, attribute); 211 if (value == null) { 212 String msg = "Missing element under " + TAG +": " + attribute + " at " + 213 parser.getPositionDescription(); 214 PackageManagerService.reportSettingsProblem(Log.WARN, msg); 215 return defaultValue; 216 } else { 217 return value; 218 } 219 } 220 221 public void writeToXml(TypedXmlSerializer serializer) throws IOException { 222 serializer.attributeInt(null, ATTR_TARGET_USER_ID, mTargetUserId); 223 serializer.attributeInt(null, ATTR_FLAGS, mFlags); 224 serializer.attribute(null, ATTR_OWNER_PACKAGE, mOwnerPackage); 225 serializer.attributeInt(null, ATTR_ACCESS_CONTROL, mAccessControlLevel); 226 serializer.startTag(null, ATTR_FILTER); 227 mFilter.writeToXml(serializer); 228 serializer.endTag(null, ATTR_FILTER); 229 } 230 231 @Override 232 public String toString() { 233 return "CrossProfileIntentFilter{0x" + Integer.toHexString(System.identityHashCode(this)) 234 + " " + Integer.toString(mTargetUserId) + "}"; 235 } 236 237 boolean equalsIgnoreFilter(CrossProfileIntentFilter other) { 238 return mTargetUserId == other.mTargetUserId 239 && mOwnerPackage.equals(other.mOwnerPackage) 240 && mFlags == other.mFlags 241 && mAccessControlLevel == other.mAccessControlLevel; 242 } 243 244 public CrossProfileIntentFilter snapshot() { 245 return mSnapshot.snapshot(); 246 } 247 } 248