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