/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.app; import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.AppGlobals; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.Intent; import android.content.pm.IPackageManager; import android.content.pm.UserInfo; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.util.Slog; import android.widget.Toast; import java.util.List; /** * This is used in conjunction with * {@link DevicePolicyManager#addCrossProfileIntentFilter} to enable intents to * be passed in and out of a managed profile. */ public class IntentForwarderActivity extends Activity { public static String TAG = "IntentForwarderActivity"; public static String FORWARD_INTENT_TO_PARENT = "com.android.internal.app.ForwardIntentToParent"; public static String FORWARD_INTENT_TO_MANAGED_PROFILE = "com.android.internal.app.ForwardIntentToManagedProfile"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intentReceived = getIntent(); String className = intentReceived.getComponent().getClassName(); final int targetUserId; final int userMessageId; if (className.equals(FORWARD_INTENT_TO_PARENT)) { userMessageId = com.android.internal.R.string.forward_intent_to_owner; targetUserId = getProfileParent(); } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) { userMessageId = com.android.internal.R.string.forward_intent_to_work; targetUserId = getManagedProfile(); } else { Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly"); userMessageId = -1; targetUserId = UserHandle.USER_NULL; } if (targetUserId == UserHandle.USER_NULL) { // This covers the case where there is no parent / managed profile. finish(); return; } Intent newIntent = new Intent(intentReceived); newIntent.setComponent(null); // Apps should not be allowed to target a specific package in the target user. newIntent.setPackage(null); newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); int callingUserId = getUserId(); if (canForward(newIntent, targetUserId)) { if (Intent.ACTION_CHOOSER.equals(newIntent.getAction())) { Intent innerIntent = (Intent) newIntent.getParcelableExtra(Intent.EXTRA_INTENT); // At this point, innerIntent is not null. Otherwise, canForward would have returned // false. innerIntent.prepareToLeaveUser(callingUserId); } else { newIntent.prepareToLeaveUser(callingUserId); } final android.content.pm.ResolveInfo ri = getPackageManager().resolveActivityAsUser( newIntent, MATCH_DEFAULT_ONLY, targetUserId); // Don't show the disclosure if next activity is ResolverActivity or ChooserActivity // as those will already have shown work / personal as neccesary etc. final boolean shouldShowDisclosure = ri == null || ri.activityInfo == null || !"android".equals(ri.activityInfo.packageName) || !(ResolverActivity.class.getName().equals(ri.activityInfo.name) || ChooserActivity.class.getName().equals(ri.activityInfo.name)); try { startActivityAsCaller(newIntent, null, false, targetUserId); } catch (RuntimeException e) { int launchedFromUid = -1; String launchedFromPackage = "?"; try { launchedFromUid = ActivityManager.getService().getLaunchedFromUid( getActivityToken()); launchedFromPackage = ActivityManager.getService().getLaunchedFromPackage( getActivityToken()); } catch (RemoteException ignored) { } Slog.wtf(TAG, "Unable to launch as UID " + launchedFromUid + " package " + launchedFromPackage + ", while running in " + ActivityThread.currentProcessName(), e); } if (shouldShowDisclosure) { Toast.makeText(this, getString(userMessageId), Toast.LENGTH_LONG).show(); } } else { Slog.wtf(TAG, "the intent: " + newIntent + " cannot be forwarded from user " + callingUserId + " to user " + targetUserId); } finish(); } boolean canForward(Intent intent, int targetUserId) { IPackageManager ipm = AppGlobals.getPackageManager(); if (Intent.ACTION_CHOOSER.equals(intent.getAction())) { // The EXTRA_INITIAL_INTENTS may not be allowed to be forwarded. if (intent.hasExtra(Intent.EXTRA_INITIAL_INTENTS)) { Slog.wtf(TAG, "An chooser intent with extra initial intents cannot be forwarded to" + " a different user"); return false; } if (intent.hasExtra(Intent.EXTRA_REPLACEMENT_EXTRAS)) { Slog.wtf(TAG, "A chooser intent with replacement extras cannot be forwarded to a" + " different user"); return false; } intent = (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT); if (intent == null) { Slog.wtf(TAG, "Cannot forward a chooser intent with no extra " + Intent.EXTRA_INTENT); return false; } } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); if (intent.getSelector() != null) { intent = intent.getSelector(); } try { return ipm.canForwardTo(intent, resolvedType, getUserId(), targetUserId); } catch (RemoteException e) { Slog.e(TAG, "PackageManagerService is dead?"); return false; } } /** * Returns the userId of the managed profile for this device or UserHandle.USER_NULL if there is * no managed profile. * * TODO: Remove the assumption that there is only one managed profile * on the device. */ private int getManagedProfile() { UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); List relatedUsers = userManager.getProfiles(UserHandle.myUserId()); for (UserInfo userInfo : relatedUsers) { if (userInfo.isManagedProfile()) return userInfo.id; } Slog.wtf(TAG, FORWARD_INTENT_TO_MANAGED_PROFILE + " has been called, but there is no managed profile"); return UserHandle.USER_NULL; } /** * Returns the userId of the profile parent or UserHandle.USER_NULL if there is * no parent. */ private int getProfileParent() { UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); UserInfo parent = userManager.getProfileParent(UserHandle.myUserId()); if (parent == null) { Slog.wtf(TAG, FORWARD_INTENT_TO_PARENT + " has been called, but there is no parent"); return UserHandle.USER_NULL; } return parent.id; } }