1 /*
2  * Copyright (C) 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.internal.app;
18 
19 import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
20 
21 import android.app.Activity;
22 import android.app.ActivityManagerNative;
23 import android.app.ActivityThread;
24 import android.app.AppGlobals;
25 import android.app.admin.DevicePolicyManager;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.pm.IPackageManager;
29 import android.content.pm.UserInfo;
30 import android.os.Bundle;
31 import android.os.RemoteException;
32 import android.os.UserHandle;
33 import android.os.UserManager;
34 import android.util.Slog;
35 import android.widget.Toast;
36 
37 import java.util.List;
38 
39 /**
40  * This is used in conjunction with
41  * {@link DevicePolicyManager#addCrossProfileIntentFilter} to enable intents to
42  * be passed in and out of a managed profile.
43  */
44 public class IntentForwarderActivity extends Activity  {
45 
46     public static String TAG = "IntentForwarderActivity";
47 
48     public static String FORWARD_INTENT_TO_PARENT
49             = "com.android.internal.app.ForwardIntentToParent";
50 
51     public static String FORWARD_INTENT_TO_MANAGED_PROFILE
52             = "com.android.internal.app.ForwardIntentToManagedProfile";
53 
54     @Override
onCreate(Bundle savedInstanceState)55     protected void onCreate(Bundle savedInstanceState) {
56         super.onCreate(savedInstanceState);
57         Intent intentReceived = getIntent();
58 
59         String className = intentReceived.getComponent().getClassName();
60         final int targetUserId;
61         final int userMessageId;
62 
63         if (className.equals(FORWARD_INTENT_TO_PARENT)) {
64             userMessageId = com.android.internal.R.string.forward_intent_to_owner;
65             targetUserId = getProfileParent();
66         } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
67             userMessageId = com.android.internal.R.string.forward_intent_to_work;
68             targetUserId = getManagedProfile();
69         } else {
70             Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly");
71             userMessageId = -1;
72             targetUserId = UserHandle.USER_NULL;
73         }
74         if (targetUserId == UserHandle.USER_NULL) {
75             // This covers the case where there is no parent / managed profile.
76             finish();
77             return;
78         }
79         Intent newIntent = new Intent(intentReceived);
80         newIntent.setComponent(null);
81         // Apps should not be allowed to target a specific package in the target user.
82         newIntent.setPackage(null);
83         newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
84                 |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
85         int callingUserId = getUserId();
86 
87         if (canForward(newIntent, targetUserId)) {
88             if (Intent.ACTION_CHOOSER.equals(newIntent.getAction())) {
89                 Intent innerIntent = (Intent) newIntent.getParcelableExtra(Intent.EXTRA_INTENT);
90                 // At this point, innerIntent is not null. Otherwise, canForward would have returned
91                 // false.
92                 innerIntent.prepareToLeaveUser(callingUserId);
93             } else {
94                 newIntent.prepareToLeaveUser(callingUserId);
95             }
96 
97             final android.content.pm.ResolveInfo ri = getPackageManager().resolveActivityAsUser(
98                         newIntent, MATCH_DEFAULT_ONLY, targetUserId);
99 
100             // Don't show the disclosure if next activity is ResolverActivity or ChooserActivity
101             // as those will already have shown work / personal as neccesary etc.
102             final boolean shouldShowDisclosure = ri == null || ri.activityInfo == null ||
103                     !"android".equals(ri.activityInfo.packageName) ||
104                     !(ResolverActivity.class.getName().equals(ri.activityInfo.name)
105                     || ChooserActivity.class.getName().equals(ri.activityInfo.name));
106 
107             try {
108                 startActivityAsCaller(newIntent, null, false, targetUserId);
109             } catch (RuntimeException e) {
110                 int launchedFromUid = -1;
111                 String launchedFromPackage = "?";
112                 try {
113                     launchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid(
114                             getActivityToken());
115                     launchedFromPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage(
116                             getActivityToken());
117                 } catch (RemoteException ignored) {
118                 }
119 
120                 Slog.wtf(TAG, "Unable to launch as UID " + launchedFromUid + " package "
121                         + launchedFromPackage + ", while running in "
122                         + ActivityThread.currentProcessName(), e);
123             }
124 
125             if (shouldShowDisclosure) {
126                 Toast.makeText(this, getString(userMessageId), Toast.LENGTH_LONG).show();
127             }
128         } else {
129             Slog.wtf(TAG, "the intent: " + newIntent + " cannot be forwarded from user "
130                     + callingUserId + " to user " + targetUserId);
131         }
132         finish();
133     }
134 
canForward(Intent intent, int targetUserId)135     boolean canForward(Intent intent, int targetUserId)  {
136         IPackageManager ipm = AppGlobals.getPackageManager();
137         if (Intent.ACTION_CHOOSER.equals(intent.getAction())) {
138             // The EXTRA_INITIAL_INTENTS may not be allowed to be forwarded.
139             if (intent.hasExtra(Intent.EXTRA_INITIAL_INTENTS)) {
140                 Slog.wtf(TAG, "An chooser intent with extra initial intents cannot be forwarded to"
141                         + " a different user");
142                 return false;
143             }
144             if (intent.hasExtra(Intent.EXTRA_REPLACEMENT_EXTRAS)) {
145                 Slog.wtf(TAG, "A chooser intent with replacement extras cannot be forwarded to a"
146                         + " different user");
147                 return false;
148             }
149             intent = (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT);
150             if (intent == null) {
151                 Slog.wtf(TAG, "Cannot forward a chooser intent with no extra "
152                         + Intent.EXTRA_INTENT);
153                 return false;
154             }
155         }
156         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
157         if (intent.getSelector() != null) {
158             intent = intent.getSelector();
159         }
160         try {
161             return ipm.canForwardTo(intent, resolvedType, getUserId(),
162                     targetUserId);
163         } catch (RemoteException e) {
164             Slog.e(TAG, "PackageManagerService is dead?");
165             return false;
166         }
167     }
168 
169     /**
170      * Returns the userId of the managed profile for this device or UserHandle.USER_NULL if there is
171      * no managed profile.
172      *
173      * TODO: Remove the assumption that there is only one managed profile
174      * on the device.
175      */
getManagedProfile()176     private int getManagedProfile() {
177         UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
178         List<UserInfo> relatedUsers = userManager.getProfiles(UserHandle.myUserId());
179         for (UserInfo userInfo : relatedUsers) {
180             if (userInfo.isManagedProfile()) return userInfo.id;
181         }
182         Slog.wtf(TAG, FORWARD_INTENT_TO_MANAGED_PROFILE
183                 + " has been called, but there is no managed profile");
184         return UserHandle.USER_NULL;
185     }
186 
187     /**
188      * Returns the userId of the profile parent or UserHandle.USER_NULL if there is
189      * no parent.
190      */
getProfileParent()191     private int getProfileParent() {
192         UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
193         UserInfo parent = userManager.getProfileParent(UserHandle.myUserId());
194         if (parent == null) {
195             Slog.wtf(TAG, FORWARD_INTENT_TO_PARENT
196                     + " has been called, but there is no parent");
197             return UserHandle.USER_NULL;
198         }
199         return parent.id;
200     }
201 }
202