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