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