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 package com.android.managedprovisioning.task; 17 18 import static android.app.admin.DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED; 19 import static android.content.pm.PackageManager.ONLY_IF_NO_MATCH_FOUND; 20 import static android.content.pm.PackageManager.SKIP_CURRENT_PROFILE; 21 import static android.speech.RecognizerIntent.ACTION_RECOGNIZE_SPEECH; 22 import static com.android.internal.util.Preconditions.checkNotNull; 23 24 import android.app.admin.DevicePolicyManager; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.pm.PackageManager; 29 import android.content.pm.UserInfo; 30 import android.hardware.usb.UsbManager; 31 import android.os.UserHandle; 32 import android.os.UserManager; 33 import android.provider.AlarmClock; 34 import android.provider.MediaStore; 35 36 import com.android.internal.annotations.VisibleForTesting; 37 import com.android.managedprovisioning.common.ProvisionLogger; 38 import com.android.managedprovisioning.task.CrossProfileIntentFilter.Direction; 39 40 import java.util.Arrays; 41 import java.util.List; 42 43 /** 44 * Class to set CrossProfileIntentFilters during managed profile creation, and reset them after an 45 * ota. 46 */ 47 public class CrossProfileIntentFiltersSetter { 48 49 // Intents from profile to parent user 50 51 /** Emergency call intent with mime type is always resolved by primary user. */ 52 private static final CrossProfileIntentFilter EMERGENCY_CALL_MIME = 53 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, SKIP_CURRENT_PROFILE, false) 54 .addAction(Intent.ACTION_CALL_EMERGENCY) 55 .addAction(Intent.ACTION_CALL_PRIVILEGED) 56 .addCategory(Intent.CATEGORY_DEFAULT) 57 .addCategory(Intent.CATEGORY_BROWSABLE) 58 .addDataType("vnd.android.cursor.item/phone") 59 .addDataType("vnd.android.cursor.item/phone_v2") 60 .addDataType("vnd.android.cursor.item/person") 61 .addDataType("vnd.android.cursor.dir/calls") 62 .addDataType("vnd.android.cursor.item/calls") 63 .build(); 64 65 /** Emergency call intent with data schemes is always resolved by primary user. */ 66 private static final CrossProfileIntentFilter EMERGENCY_CALL_DATA = 67 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, SKIP_CURRENT_PROFILE, false) 68 .addAction(Intent.ACTION_CALL_EMERGENCY) 69 .addAction(Intent.ACTION_CALL_PRIVILEGED) 70 .addCategory(Intent.CATEGORY_DEFAULT) 71 .addCategory(Intent.CATEGORY_BROWSABLE) 72 .addDataScheme("tel") 73 .addDataScheme("sip") 74 .addDataScheme("voicemail") 75 .build(); 76 77 /** Dial intent with mime type can be handled by either managed profile or its parent user. */ 78 private static final CrossProfileIntentFilter DIAL_MIME = 79 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, ONLY_IF_NO_MATCH_FOUND, false) 80 .addAction(Intent.ACTION_DIAL) 81 .addAction(Intent.ACTION_VIEW) 82 .addCategory(Intent.CATEGORY_DEFAULT) 83 .addCategory(Intent.CATEGORY_BROWSABLE) 84 .addDataType("vnd.android.cursor.item/phone") 85 .addDataType("vnd.android.cursor.item/phone_v2") 86 .addDataType("vnd.android.cursor.item/person") 87 .addDataType("vnd.android.cursor.dir/calls") 88 .addDataType("vnd.android.cursor.item/calls") 89 .build(); 90 91 /** Dial intent with data scheme can be handled by either managed profile or its parent user. */ 92 private static final CrossProfileIntentFilter DIAL_DATA = 93 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, ONLY_IF_NO_MATCH_FOUND, false) 94 .addAction(Intent.ACTION_DIAL) 95 .addAction(Intent.ACTION_VIEW) 96 .addCategory(Intent.CATEGORY_DEFAULT) 97 .addCategory(Intent.CATEGORY_BROWSABLE) 98 .addDataScheme("tel") 99 .addDataScheme("sip") 100 .addDataScheme("voicemail") 101 .build(); 102 103 /** 104 * Dial intent with no data scheme or type can be handled by either managed profile or its 105 * parent user. 106 */ 107 private static final CrossProfileIntentFilter DIAL_RAW = 108 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, ONLY_IF_NO_MATCH_FOUND, false) 109 .addAction(Intent.ACTION_DIAL) 110 .addCategory(Intent.CATEGORY_DEFAULT) 111 .addCategory(Intent.CATEGORY_BROWSABLE) 112 .build(); 113 114 /** Pressing the call button can be handled by either managed profile or its parent user. */ 115 private static final CrossProfileIntentFilter CALL_BUTTON = 116 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, ONLY_IF_NO_MATCH_FOUND, false) 117 .addAction(Intent.ACTION_CALL_BUTTON) 118 .addCategory(Intent.CATEGORY_DEFAULT) 119 .build(); 120 121 /** SMS and MMS are exclusively handled by the primary user. */ 122 private static final CrossProfileIntentFilter SMS_MMS = 123 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, SKIP_CURRENT_PROFILE, false) 124 .addAction(Intent.ACTION_VIEW) 125 .addAction(Intent.ACTION_SENDTO) 126 .addCategory(Intent.CATEGORY_DEFAULT) 127 .addCategory(Intent.CATEGORY_BROWSABLE) 128 .addDataScheme("sms") 129 .addDataScheme("smsto") 130 .addDataScheme("mms") 131 .addDataScheme("mmsto") 132 .build(); 133 134 /** Mobile network settings is always shown in the primary user. */ 135 private static final CrossProfileIntentFilter MOBILE_NETWORK_SETTINGS = 136 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, SKIP_CURRENT_PROFILE, false) 137 .addAction(android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS) 138 .addAction(android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS) 139 .addCategory(Intent.CATEGORY_DEFAULT) 140 .build(); 141 142 /** HOME intent is always resolved by the primary user. */ 143 @VisibleForTesting 144 static final CrossProfileIntentFilter HOME = 145 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, SKIP_CURRENT_PROFILE, false) 146 .addAction(Intent.ACTION_MAIN) 147 .addCategory(Intent.CATEGORY_DEFAULT) 148 .addCategory(Intent.CATEGORY_HOME) 149 .build(); 150 151 /** Get content can be forwarded to parent user. */ 152 private static final CrossProfileIntentFilter GET_CONTENT = 153 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, 0, true) 154 .addAction(Intent.ACTION_GET_CONTENT) 155 .addCategory(Intent.CATEGORY_DEFAULT) 156 .addCategory(Intent.CATEGORY_OPENABLE) 157 .addDataType("*/*") 158 .build(); 159 160 /** Open document intent can be forwarded to parent user. */ 161 private static final CrossProfileIntentFilter OPEN_DOCUMENT = 162 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, 0, true) 163 .addAction(Intent.ACTION_OPEN_DOCUMENT) 164 .addCategory(Intent.CATEGORY_DEFAULT) 165 .addCategory(Intent.CATEGORY_OPENABLE) 166 .addDataType("*/*") 167 .build(); 168 169 /** Pick for any data type can be forwarded to parent user. */ 170 private static final CrossProfileIntentFilter ACTION_PICK_DATA = 171 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, 0, true) 172 .addAction(Intent.ACTION_PICK) 173 .addCategory(Intent.CATEGORY_DEFAULT) 174 .addDataType("*/*") 175 .build(); 176 177 /** Pick without data type can be forwarded to parent user. */ 178 private static final CrossProfileIntentFilter ACTION_PICK_RAW = 179 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, 0, true) 180 .addAction(Intent.ACTION_PICK) 181 .addCategory(Intent.CATEGORY_DEFAULT) 182 .build(); 183 184 /** Speech recognition can be performed by primary user. */ 185 private static final CrossProfileIntentFilter RECOGNIZE_SPEECH = 186 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, 0, false) 187 .addAction(ACTION_RECOGNIZE_SPEECH) 188 .addCategory(Intent.CATEGORY_DEFAULT) 189 .build(); 190 191 /** Media capture can be performed by primary user. */ 192 private static final CrossProfileIntentFilter MEDIA_CAPTURE = 193 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, 0, true) 194 .addAction(MediaStore.ACTION_IMAGE_CAPTURE) 195 .addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE) 196 .addAction(MediaStore.ACTION_VIDEO_CAPTURE) 197 .addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION) 198 .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA) 199 .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE) 200 .addAction(MediaStore.INTENT_ACTION_VIDEO_CAMERA) 201 .addCategory(Intent.CATEGORY_DEFAULT) 202 .build(); 203 204 /** Alarm setting can be performed by primary user. */ 205 private static final CrossProfileIntentFilter SET_ALARM = 206 new CrossProfileIntentFilter.Builder(Direction.TO_PARENT, 0, false) 207 .addAction(AlarmClock.ACTION_SET_ALARM) 208 .addAction(AlarmClock.ACTION_SHOW_ALARMS) 209 .addAction(AlarmClock.ACTION_SET_TIMER) 210 .addCategory(Intent.CATEGORY_DEFAULT) 211 .build(); 212 213 // Intents from parent to profile user 214 215 /** ACTION_SEND can be forwarded to the managed profile on user's choice. */ 216 @VisibleForTesting 217 static final CrossProfileIntentFilter ACTION_SEND = 218 new CrossProfileIntentFilter.Builder(Direction.TO_PROFILE, 0, true) 219 .addAction(Intent.ACTION_SEND) 220 .addAction(Intent.ACTION_SEND_MULTIPLE) 221 .addCategory(Intent.CATEGORY_DEFAULT) 222 .addDataType("*/*") 223 .build(); 224 225 /** USB devices attached can get forwarded to the profile. */ 226 private static final CrossProfileIntentFilter USB_DEVICE_ATTACHED = 227 new CrossProfileIntentFilter.Builder(Direction.TO_PROFILE, 0, false) 228 .addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED) 229 .addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED) 230 .addCategory(Intent.CATEGORY_DEFAULT) 231 .build(); 232 233 @VisibleForTesting 234 static final List<CrossProfileIntentFilter> FILTERS = Arrays.asList( 235 EMERGENCY_CALL_MIME, 236 EMERGENCY_CALL_DATA, 237 DIAL_MIME, 238 DIAL_DATA, 239 DIAL_RAW, 240 CALL_BUTTON, 241 SMS_MMS, 242 SET_ALARM, 243 MEDIA_CAPTURE, 244 RECOGNIZE_SPEECH, 245 ACTION_PICK_RAW, 246 ACTION_PICK_DATA, 247 OPEN_DOCUMENT, 248 GET_CONTENT, 249 USB_DEVICE_ATTACHED, 250 ACTION_SEND, 251 HOME, 252 MOBILE_NETWORK_SETTINGS); 253 254 /** 255 * Broadcast receiver for DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED 256 */ 257 public static class RestrictionChangedReceiver extends BroadcastReceiver { 258 @Override onReceive(Context context, Intent intent)259 public void onReceive(Context context, Intent intent) { 260 if (!ACTION_DATA_SHARING_RESTRICTION_CHANGED.equals(intent.getAction())) { 261 return; 262 } 263 int profileUser = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL); 264 if (profileUser == UserHandle.USER_NULL) { 265 return; 266 } 267 UserInfo parent = context.getSystemService(UserManager.class) 268 .getProfileParent(profileUser); 269 if (parent == null) { 270 return; 271 } 272 // Always call resetFilters() on the parent user, which handles cross profile 273 // intent filters between the parent and its profiles. 274 ProvisionLogger.logd("Resetting cross-profile intent filters on restriction change"); 275 new CrossProfileIntentFiltersSetter(context).resetFilters(parent.id); 276 context.sendBroadcastAsUser(new Intent( 277 DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED), 278 UserHandle.of(profileUser)); 279 } 280 } 281 282 private final PackageManager mPackageManager; 283 private final UserManager mUserManager; 284 CrossProfileIntentFiltersSetter(Context context)285 public CrossProfileIntentFiltersSetter(Context context) { 286 this(context.getPackageManager(), 287 (UserManager) context.getSystemService(Context.USER_SERVICE)); 288 } 289 290 @VisibleForTesting CrossProfileIntentFiltersSetter(PackageManager packageManager, UserManager userManager)291 CrossProfileIntentFiltersSetter(PackageManager packageManager, UserManager userManager) { 292 mPackageManager = checkNotNull(packageManager); 293 mUserManager = checkNotNull(userManager); 294 } 295 296 /** 297 * Sets all default cross profile intent filters from {@code parentUserId} to 298 * {@code managedProfileUserId}. 299 */ setFilters(int parentUserId, int managedProfileUserId)300 public void setFilters(int parentUserId, int managedProfileUserId) { 301 ProvisionLogger.logd("Setting cross-profile intent filters"); 302 boolean disallowSharingIntoProfile = mUserManager.hasUserRestriction( 303 UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, 304 UserHandle.of(managedProfileUserId)); 305 for (CrossProfileIntentFilter filter : FILTERS) { 306 // Skip filters that allow data to be shared into the profile, if admin has disabled 307 // it. 308 if (disallowSharingIntoProfile && filter.letsPersonalDataIntoProfile) { 309 continue; 310 } 311 if (filter.direction == Direction.TO_PARENT) { 312 mPackageManager.addCrossProfileIntentFilter(filter.filter, managedProfileUserId, 313 parentUserId, filter.flags); 314 } else { 315 mPackageManager.addCrossProfileIntentFilter(filter.filter, parentUserId, 316 managedProfileUserId, filter.flags); 317 } 318 } 319 } 320 321 /** 322 * Reset the cross profile intent filters between {@code userId} and all of its managed profiles 323 * if any. 324 */ resetFilters(int userId)325 public void resetFilters(int userId) { 326 List<UserInfo> profiles = mUserManager.getProfiles(userId); 327 if (profiles.size() <= 1) { 328 return; 329 } 330 331 // Removes cross profile intent filters from the parent to all the managed profiles. 332 mPackageManager.clearCrossProfileIntentFilters(userId); 333 334 // For each managed profile reset cross profile intent filters 335 for (UserInfo profile : profiles) { 336 if (!profile.isManagedProfile()) { 337 continue; 338 } 339 mPackageManager.clearCrossProfileIntentFilters(profile.id); 340 setFilters(userId, profile.id); 341 } 342 } 343 344 } 345