Loading core/java/com/android/internal/app/IntentForwarderActivity.java +91 −39 Original line number Diff line number Diff line Loading @@ -16,16 +16,14 @@ 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.PackageManager; import android.content.pm.UserInfo; import android.os.Bundle; import android.os.RemoteException; Loading @@ -34,8 +32,12 @@ import android.os.UserManager; import android.util.Slog; import android.widget.Toast; import com.android.internal.annotations.VisibleForTesting; import java.util.List; import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; /** * This is used in conjunction with * {@link DevicePolicyManager#addCrossProfileIntentFilter} to enable intents to Loading @@ -51,15 +53,17 @@ public class IntentForwarderActivity extends Activity { public static String FORWARD_INTENT_TO_MANAGED_PROFILE = "com.android.internal.app.ForwardIntentToManagedProfile"; private Injector mInjector; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intentReceived = getIntent(); mInjector = createInjector(); 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(); Loading @@ -76,17 +80,12 @@ public class IntentForwarderActivity extends Activity { 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)) { final int callingUserId = getUserId(); final Intent newIntent = canForward(intentReceived, targetUserId); if (newIntent != null) { if (Intent.ACTION_CHOOSER.equals(newIntent.getAction())) { Intent innerIntent = (Intent) newIntent.getParcelableExtra(Intent.EXTRA_INTENT); Intent innerIntent = newIntent.getParcelableExtra(Intent.EXTRA_INTENT); // At this point, innerIntent is not null. Otherwise, canForward would have returned // false. innerIntent.prepareToLeaveUser(callingUserId); Loading @@ -94,8 +93,11 @@ public class IntentForwarderActivity extends Activity { newIntent.prepareToLeaveUser(callingUserId); } final android.content.pm.ResolveInfo ri = getPackageManager().resolveActivityAsUser( newIntent, MATCH_DEFAULT_ONLY, targetUserId); final android.content.pm.ResolveInfo ri = mInjector.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. Loading Loading @@ -126,44 +128,56 @@ public class IntentForwarderActivity extends Activity { Toast.makeText(this, getString(userMessageId), Toast.LENGTH_LONG).show(); } } else { Slog.wtf(TAG, "the intent: " + newIntent + " cannot be forwarded from user " Slog.wtf(TAG, "the intent: " + intentReceived + " 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())) { /** * Check whether the intent can be forwarded to target user. Return the intent used for * forwarding if it can be forwarded, {@code null} otherwise. */ Intent canForward(Intent incomingIntent, int targetUserId) { Intent forwardIntent = new Intent(incomingIntent); forwardIntent.addFlags( Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); sanitizeIntent(forwardIntent); Intent intentToCheck = forwardIntent; if (Intent.ACTION_CHOOSER.equals(forwardIntent.getAction())) { // The EXTRA_INITIAL_INTENTS may not be allowed to be forwarded. if (intent.hasExtra(Intent.EXTRA_INITIAL_INTENTS)) { if (forwardIntent.hasExtra(Intent.EXTRA_INITIAL_INTENTS)) { Slog.wtf(TAG, "An chooser intent with extra initial intents cannot be forwarded to" + " a different user"); return false; return null; } if (intent.hasExtra(Intent.EXTRA_REPLACEMENT_EXTRAS)) { if (forwardIntent.hasExtra(Intent.EXTRA_REPLACEMENT_EXTRAS)) { Slog.wtf(TAG, "A chooser intent with replacement extras cannot be forwarded to a" + " different user"); return false; return null; } intent = (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT); if (intent == null) { intentToCheck = forwardIntent.getParcelableExtra(Intent.EXTRA_INTENT); if (intentToCheck == null) { Slog.wtf(TAG, "Cannot forward a chooser intent with no extra " + Intent.EXTRA_INTENT); return false; return null; } } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); if (intent.getSelector() != null) { intent = intent.getSelector(); if (forwardIntent.getSelector() != null) { intentToCheck = forwardIntent.getSelector(); } String resolvedType = intentToCheck.resolveTypeIfNeeded(getContentResolver()); sanitizeIntent(intentToCheck); try { return ipm.canForwardTo(intent, resolvedType, getUserId(), targetUserId); if (mInjector.getIPackageManager(). canForwardTo(intentToCheck, resolvedType, getUserId(), targetUserId)) { return forwardIntent; } } catch (RemoteException e) { Slog.e(TAG, "PackageManagerService is dead?"); return false; } return null; } /** Loading @@ -174,8 +188,7 @@ public class IntentForwarderActivity extends Activity { * on the device. */ private int getManagedProfile() { UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); List<UserInfo> relatedUsers = userManager.getProfiles(UserHandle.myUserId()); List<UserInfo> relatedUsers = mInjector.getUserManager().getProfiles(UserHandle.myUserId()); for (UserInfo userInfo : relatedUsers) { if (userInfo.isManagedProfile()) return userInfo.id; } Loading @@ -189,8 +202,7 @@ public class IntentForwarderActivity extends Activity { * no parent. */ private int getProfileParent() { UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); UserInfo parent = userManager.getProfileParent(UserHandle.myUserId()); UserInfo parent = mInjector.getUserManager().getProfileParent(UserHandle.myUserId()); if (parent == null) { Slog.wtf(TAG, FORWARD_INTENT_TO_PARENT + " has been called, but there is no parent"); Loading @@ -198,4 +210,44 @@ public class IntentForwarderActivity extends Activity { } return parent.id; } /** * Sanitize the intent in place. */ private void sanitizeIntent(Intent intent) { // Apps should not be allowed to target a specific package/ component in the target user. intent.setPackage(null); intent.setComponent(null); } @VisibleForTesting protected Injector createInjector() { return new InjectorImpl(); } private class InjectorImpl implements Injector { @Override public IPackageManager getIPackageManager() { return AppGlobals.getPackageManager(); } @Override public UserManager getUserManager() { return getSystemService(UserManager.class); } @Override public PackageManager getPackageManager() { return IntentForwarderActivity.this.getPackageManager(); } } public interface Injector { IPackageManager getIPackageManager(); UserManager getUserManager(); PackageManager getPackageManager(); } } core/tests/coretests/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -1172,6 +1172,7 @@ </activity> <activity android:name="com.android.internal.app.ChooserWrapperActivity"/> <activity android:name="com.android.internal.app.ResolverWrapperActivity"/> <activity android:name="com.android.internal.app.IntentForwarderActivityTest$IntentForwarderWrapperActivity"/> <receiver android:name="android.app.activity.AbortReceiver"> <intent-filter android:priority="1"> Loading core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java 0 → 100644 +296 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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 android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.support.test.InstrumentationRegistry; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import android.util.Log; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(AndroidJUnit4.class) public class IntentForwarderActivityTest { private static final ComponentName FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME = new ComponentName( "android", IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE ); private static final String TYPE_PLAIN_TEXT = "text/plain"; private static UserInfo MANAGED_PROFILE_INFO = new UserInfo(); static { MANAGED_PROFILE_INFO.id = 10; MANAGED_PROFILE_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE; } private static UserInfo CURRENT_USER_INFO = new UserInfo(); static { CURRENT_USER_INFO.id = UserHandle.myUserId(); CURRENT_USER_INFO.flags = 0; } private static IntentForwarderActivity.Injector sInjector; private static ComponentName sComponentName; @Mock private IPackageManager mIPm; @Mock private PackageManager mPm; @Mock private UserManager mUserManager; @Rule public ActivityTestRule<IntentForwarderWrapperActivity> mActivityRule = new ActivityTestRule<>(IntentForwarderWrapperActivity.class, true, false); private Context mContext; @Before public void setup() { MockitoAnnotations.initMocks(this); mContext = InstrumentationRegistry.getTargetContext(); sInjector = new TestInjector(); } @Test public void forwardToManagedProfile_canForward_sendIntent() throws Exception { sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME; // Intent can be forwarded. when(mIPm.canForwardTo( any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true); // Managed profile exists. List<UserInfo> profiles = new ArrayList<>(); profiles.add(CURRENT_USER_INFO); profiles.add(MANAGED_PROFILE_INFO); when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class); intent.setAction(Intent.ACTION_SEND); intent.setType(TYPE_PLAIN_TEXT); IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mIPm).canForwardTo(intentCaptor.capture(), eq(TYPE_PLAIN_TEXT), anyInt(), anyInt()); assertEquals(Intent.ACTION_SEND, intentCaptor.getValue().getAction()); assertEquals(Intent.ACTION_SEND, intentCaptor.getValue().getAction()); assertNotNull(activity.mStartActivityIntent); assertEquals(Intent.ACTION_SEND, activity.mStartActivityIntent.getAction()); assertNull(activity.mStartActivityIntent.getPackage()); assertNull(activity.mStartActivityIntent.getComponent()); assertEquals(CURRENT_USER_INFO.id, activity.mStartActivityIntent.getContentUserHint()); assertEquals(MANAGED_PROFILE_INFO.id, activity.mUserIdActivityLaunchedIn); } @Test public void forwardToManagedProfile_cannotForward_sendIntent() throws Exception { sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME; // Intent cannot be forwarded. when(mIPm.canForwardTo( any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(false); // Managed profile exists. List<UserInfo> profiles = new ArrayList<>(); profiles.add(CURRENT_USER_INFO); profiles.add(MANAGED_PROFILE_INFO); when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); // Create ACTION_SEND intent. Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class); intent.setAction(Intent.ACTION_SEND); IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); assertNull(activity.mStartActivityIntent); } @Test public void forwardToManagedProfile_noManagedProfile_sendIntent() throws Exception { sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME; // Intent can be forwarded. when(mIPm.canForwardTo( any(Intent.class), anyString(), anyInt(), anyInt())).thenReturn(true); // Managed profile does not exist. List<UserInfo> profiles = new ArrayList<>(); profiles.add(CURRENT_USER_INFO); when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); // Create ACTION_SEND intent. Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class); intent.setAction(Intent.ACTION_SEND); IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); assertNull(activity.mStartActivityIntent); } @Test public void forwardToManagedProfile_canForward_chooserIntent() throws Exception { sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME; // Intent can be forwarded. when(mIPm.canForwardTo( any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true); // Manage profile exists. List<UserInfo> profiles = new ArrayList<>(); profiles.add(CURRENT_USER_INFO); profiles.add(MANAGED_PROFILE_INFO); when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); // Create chooser Intent Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class); intent.setAction(Intent.ACTION_CHOOSER); Intent sendIntent = new Intent(Intent.ACTION_SEND); sendIntent.setComponent(new ComponentName("xx", "yyy")); sendIntent.setType(TYPE_PLAIN_TEXT); intent.putExtra(Intent.EXTRA_INTENT, sendIntent); IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mIPm).canForwardTo(intentCaptor.capture(), eq(TYPE_PLAIN_TEXT), anyInt(), anyInt()); assertEquals(Intent.ACTION_SEND, intentCaptor.getValue().getAction()); assertNotNull(activity.mStartActivityIntent); assertEquals(Intent.ACTION_CHOOSER, activity.mStartActivityIntent.getAction()); assertNull(activity.mStartActivityIntent.getPackage()); assertNull(activity.mStartActivityIntent.getComponent()); Intent innerIntent = activity.mStartActivityIntent.getParcelableExtra(Intent.EXTRA_INTENT); assertNotNull(innerIntent); assertEquals(Intent.ACTION_SEND, innerIntent.getAction()); assertNull(innerIntent.getComponent()); assertNull(innerIntent.getPackage()); assertEquals(CURRENT_USER_INFO.id, innerIntent.getContentUserHint()); assertEquals(MANAGED_PROFILE_INFO.id, activity.mUserIdActivityLaunchedIn); } @Test public void forwardToManagedProfile_canForward_selectorIntent() throws Exception { sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME; // Intent can be forwarded. when(mIPm.canForwardTo( any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true); // Manage profile exists. List<UserInfo> profiles = new ArrayList<>(); profiles.add(CURRENT_USER_INFO); profiles.add(MANAGED_PROFILE_INFO); when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); // Create selector intent. Intent intent = Intent.makeMainSelectorActivity( Intent.ACTION_VIEW, Intent.CATEGORY_BROWSABLE); IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mIPm).canForwardTo( intentCaptor.capture(), nullable(String.class), anyInt(), anyInt()); assertEquals(Intent.ACTION_VIEW, intentCaptor.getValue().getAction()); assertNotNull(activity.mStartActivityIntent); assertEquals(Intent.ACTION_MAIN, activity.mStartActivityIntent.getAction()); assertNull(activity.mStartActivityIntent.getPackage()); assertNull(activity.mStartActivityIntent.getComponent()); assertEquals(CURRENT_USER_INFO.id, activity.mStartActivityIntent.getContentUserHint()); Intent innerIntent = activity.mStartActivityIntent.getSelector(); assertNotNull(innerIntent); assertEquals(Intent.ACTION_VIEW, innerIntent.getAction()); assertNull(innerIntent.getComponent()); assertNull(innerIntent.getPackage()); assertEquals(MANAGED_PROFILE_INFO.id, activity.mUserIdActivityLaunchedIn); } public static class IntentForwarderWrapperActivity extends IntentForwarderActivity { private Intent mStartActivityIntent; private int mUserIdActivityLaunchedIn; @Override public void onCreate(@Nullable Bundle savedInstanceState) { getIntent().setComponent(sComponentName); super.onCreate(savedInstanceState); } @Override protected Injector createInjector() { return sInjector; } @Override public void startActivityAsCaller(Intent intent, @Nullable Bundle options, boolean ignoreTargetSecurity, int userId) { mStartActivityIntent = intent; mUserIdActivityLaunchedIn = userId; } } class TestInjector implements IntentForwarderActivity.Injector { @Override public IPackageManager getIPackageManager() { return mIPm; } @Override public UserManager getUserManager() { return mUserManager; } @Override public PackageManager getPackageManager() { return mPm; } } } No newline at end of file Loading
core/java/com/android/internal/app/IntentForwarderActivity.java +91 −39 Original line number Diff line number Diff line Loading @@ -16,16 +16,14 @@ 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.PackageManager; import android.content.pm.UserInfo; import android.os.Bundle; import android.os.RemoteException; Loading @@ -34,8 +32,12 @@ import android.os.UserManager; import android.util.Slog; import android.widget.Toast; import com.android.internal.annotations.VisibleForTesting; import java.util.List; import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; /** * This is used in conjunction with * {@link DevicePolicyManager#addCrossProfileIntentFilter} to enable intents to Loading @@ -51,15 +53,17 @@ public class IntentForwarderActivity extends Activity { public static String FORWARD_INTENT_TO_MANAGED_PROFILE = "com.android.internal.app.ForwardIntentToManagedProfile"; private Injector mInjector; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intentReceived = getIntent(); mInjector = createInjector(); 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(); Loading @@ -76,17 +80,12 @@ public class IntentForwarderActivity extends Activity { 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)) { final int callingUserId = getUserId(); final Intent newIntent = canForward(intentReceived, targetUserId); if (newIntent != null) { if (Intent.ACTION_CHOOSER.equals(newIntent.getAction())) { Intent innerIntent = (Intent) newIntent.getParcelableExtra(Intent.EXTRA_INTENT); Intent innerIntent = newIntent.getParcelableExtra(Intent.EXTRA_INTENT); // At this point, innerIntent is not null. Otherwise, canForward would have returned // false. innerIntent.prepareToLeaveUser(callingUserId); Loading @@ -94,8 +93,11 @@ public class IntentForwarderActivity extends Activity { newIntent.prepareToLeaveUser(callingUserId); } final android.content.pm.ResolveInfo ri = getPackageManager().resolveActivityAsUser( newIntent, MATCH_DEFAULT_ONLY, targetUserId); final android.content.pm.ResolveInfo ri = mInjector.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. Loading Loading @@ -126,44 +128,56 @@ public class IntentForwarderActivity extends Activity { Toast.makeText(this, getString(userMessageId), Toast.LENGTH_LONG).show(); } } else { Slog.wtf(TAG, "the intent: " + newIntent + " cannot be forwarded from user " Slog.wtf(TAG, "the intent: " + intentReceived + " 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())) { /** * Check whether the intent can be forwarded to target user. Return the intent used for * forwarding if it can be forwarded, {@code null} otherwise. */ Intent canForward(Intent incomingIntent, int targetUserId) { Intent forwardIntent = new Intent(incomingIntent); forwardIntent.addFlags( Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); sanitizeIntent(forwardIntent); Intent intentToCheck = forwardIntent; if (Intent.ACTION_CHOOSER.equals(forwardIntent.getAction())) { // The EXTRA_INITIAL_INTENTS may not be allowed to be forwarded. if (intent.hasExtra(Intent.EXTRA_INITIAL_INTENTS)) { if (forwardIntent.hasExtra(Intent.EXTRA_INITIAL_INTENTS)) { Slog.wtf(TAG, "An chooser intent with extra initial intents cannot be forwarded to" + " a different user"); return false; return null; } if (intent.hasExtra(Intent.EXTRA_REPLACEMENT_EXTRAS)) { if (forwardIntent.hasExtra(Intent.EXTRA_REPLACEMENT_EXTRAS)) { Slog.wtf(TAG, "A chooser intent with replacement extras cannot be forwarded to a" + " different user"); return false; return null; } intent = (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT); if (intent == null) { intentToCheck = forwardIntent.getParcelableExtra(Intent.EXTRA_INTENT); if (intentToCheck == null) { Slog.wtf(TAG, "Cannot forward a chooser intent with no extra " + Intent.EXTRA_INTENT); return false; return null; } } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); if (intent.getSelector() != null) { intent = intent.getSelector(); if (forwardIntent.getSelector() != null) { intentToCheck = forwardIntent.getSelector(); } String resolvedType = intentToCheck.resolveTypeIfNeeded(getContentResolver()); sanitizeIntent(intentToCheck); try { return ipm.canForwardTo(intent, resolvedType, getUserId(), targetUserId); if (mInjector.getIPackageManager(). canForwardTo(intentToCheck, resolvedType, getUserId(), targetUserId)) { return forwardIntent; } } catch (RemoteException e) { Slog.e(TAG, "PackageManagerService is dead?"); return false; } return null; } /** Loading @@ -174,8 +188,7 @@ public class IntentForwarderActivity extends Activity { * on the device. */ private int getManagedProfile() { UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); List<UserInfo> relatedUsers = userManager.getProfiles(UserHandle.myUserId()); List<UserInfo> relatedUsers = mInjector.getUserManager().getProfiles(UserHandle.myUserId()); for (UserInfo userInfo : relatedUsers) { if (userInfo.isManagedProfile()) return userInfo.id; } Loading @@ -189,8 +202,7 @@ public class IntentForwarderActivity extends Activity { * no parent. */ private int getProfileParent() { UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); UserInfo parent = userManager.getProfileParent(UserHandle.myUserId()); UserInfo parent = mInjector.getUserManager().getProfileParent(UserHandle.myUserId()); if (parent == null) { Slog.wtf(TAG, FORWARD_INTENT_TO_PARENT + " has been called, but there is no parent"); Loading @@ -198,4 +210,44 @@ public class IntentForwarderActivity extends Activity { } return parent.id; } /** * Sanitize the intent in place. */ private void sanitizeIntent(Intent intent) { // Apps should not be allowed to target a specific package/ component in the target user. intent.setPackage(null); intent.setComponent(null); } @VisibleForTesting protected Injector createInjector() { return new InjectorImpl(); } private class InjectorImpl implements Injector { @Override public IPackageManager getIPackageManager() { return AppGlobals.getPackageManager(); } @Override public UserManager getUserManager() { return getSystemService(UserManager.class); } @Override public PackageManager getPackageManager() { return IntentForwarderActivity.this.getPackageManager(); } } public interface Injector { IPackageManager getIPackageManager(); UserManager getUserManager(); PackageManager getPackageManager(); } }
core/tests/coretests/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -1172,6 +1172,7 @@ </activity> <activity android:name="com.android.internal.app.ChooserWrapperActivity"/> <activity android:name="com.android.internal.app.ResolverWrapperActivity"/> <activity android:name="com.android.internal.app.IntentForwarderActivityTest$IntentForwarderWrapperActivity"/> <receiver android:name="android.app.activity.AbortReceiver"> <intent-filter android:priority="1"> Loading
core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java 0 → 100644 +296 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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 android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.support.test.InstrumentationRegistry; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import android.util.Log; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(AndroidJUnit4.class) public class IntentForwarderActivityTest { private static final ComponentName FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME = new ComponentName( "android", IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE ); private static final String TYPE_PLAIN_TEXT = "text/plain"; private static UserInfo MANAGED_PROFILE_INFO = new UserInfo(); static { MANAGED_PROFILE_INFO.id = 10; MANAGED_PROFILE_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE; } private static UserInfo CURRENT_USER_INFO = new UserInfo(); static { CURRENT_USER_INFO.id = UserHandle.myUserId(); CURRENT_USER_INFO.flags = 0; } private static IntentForwarderActivity.Injector sInjector; private static ComponentName sComponentName; @Mock private IPackageManager mIPm; @Mock private PackageManager mPm; @Mock private UserManager mUserManager; @Rule public ActivityTestRule<IntentForwarderWrapperActivity> mActivityRule = new ActivityTestRule<>(IntentForwarderWrapperActivity.class, true, false); private Context mContext; @Before public void setup() { MockitoAnnotations.initMocks(this); mContext = InstrumentationRegistry.getTargetContext(); sInjector = new TestInjector(); } @Test public void forwardToManagedProfile_canForward_sendIntent() throws Exception { sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME; // Intent can be forwarded. when(mIPm.canForwardTo( any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true); // Managed profile exists. List<UserInfo> profiles = new ArrayList<>(); profiles.add(CURRENT_USER_INFO); profiles.add(MANAGED_PROFILE_INFO); when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class); intent.setAction(Intent.ACTION_SEND); intent.setType(TYPE_PLAIN_TEXT); IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mIPm).canForwardTo(intentCaptor.capture(), eq(TYPE_PLAIN_TEXT), anyInt(), anyInt()); assertEquals(Intent.ACTION_SEND, intentCaptor.getValue().getAction()); assertEquals(Intent.ACTION_SEND, intentCaptor.getValue().getAction()); assertNotNull(activity.mStartActivityIntent); assertEquals(Intent.ACTION_SEND, activity.mStartActivityIntent.getAction()); assertNull(activity.mStartActivityIntent.getPackage()); assertNull(activity.mStartActivityIntent.getComponent()); assertEquals(CURRENT_USER_INFO.id, activity.mStartActivityIntent.getContentUserHint()); assertEquals(MANAGED_PROFILE_INFO.id, activity.mUserIdActivityLaunchedIn); } @Test public void forwardToManagedProfile_cannotForward_sendIntent() throws Exception { sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME; // Intent cannot be forwarded. when(mIPm.canForwardTo( any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(false); // Managed profile exists. List<UserInfo> profiles = new ArrayList<>(); profiles.add(CURRENT_USER_INFO); profiles.add(MANAGED_PROFILE_INFO); when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); // Create ACTION_SEND intent. Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class); intent.setAction(Intent.ACTION_SEND); IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); assertNull(activity.mStartActivityIntent); } @Test public void forwardToManagedProfile_noManagedProfile_sendIntent() throws Exception { sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME; // Intent can be forwarded. when(mIPm.canForwardTo( any(Intent.class), anyString(), anyInt(), anyInt())).thenReturn(true); // Managed profile does not exist. List<UserInfo> profiles = new ArrayList<>(); profiles.add(CURRENT_USER_INFO); when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); // Create ACTION_SEND intent. Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class); intent.setAction(Intent.ACTION_SEND); IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); assertNull(activity.mStartActivityIntent); } @Test public void forwardToManagedProfile_canForward_chooserIntent() throws Exception { sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME; // Intent can be forwarded. when(mIPm.canForwardTo( any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true); // Manage profile exists. List<UserInfo> profiles = new ArrayList<>(); profiles.add(CURRENT_USER_INFO); profiles.add(MANAGED_PROFILE_INFO); when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); // Create chooser Intent Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class); intent.setAction(Intent.ACTION_CHOOSER); Intent sendIntent = new Intent(Intent.ACTION_SEND); sendIntent.setComponent(new ComponentName("xx", "yyy")); sendIntent.setType(TYPE_PLAIN_TEXT); intent.putExtra(Intent.EXTRA_INTENT, sendIntent); IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mIPm).canForwardTo(intentCaptor.capture(), eq(TYPE_PLAIN_TEXT), anyInt(), anyInt()); assertEquals(Intent.ACTION_SEND, intentCaptor.getValue().getAction()); assertNotNull(activity.mStartActivityIntent); assertEquals(Intent.ACTION_CHOOSER, activity.mStartActivityIntent.getAction()); assertNull(activity.mStartActivityIntent.getPackage()); assertNull(activity.mStartActivityIntent.getComponent()); Intent innerIntent = activity.mStartActivityIntent.getParcelableExtra(Intent.EXTRA_INTENT); assertNotNull(innerIntent); assertEquals(Intent.ACTION_SEND, innerIntent.getAction()); assertNull(innerIntent.getComponent()); assertNull(innerIntent.getPackage()); assertEquals(CURRENT_USER_INFO.id, innerIntent.getContentUserHint()); assertEquals(MANAGED_PROFILE_INFO.id, activity.mUserIdActivityLaunchedIn); } @Test public void forwardToManagedProfile_canForward_selectorIntent() throws Exception { sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME; // Intent can be forwarded. when(mIPm.canForwardTo( any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true); // Manage profile exists. List<UserInfo> profiles = new ArrayList<>(); profiles.add(CURRENT_USER_INFO); profiles.add(MANAGED_PROFILE_INFO); when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); // Create selector intent. Intent intent = Intent.makeMainSelectorActivity( Intent.ACTION_VIEW, Intent.CATEGORY_BROWSABLE); IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mIPm).canForwardTo( intentCaptor.capture(), nullable(String.class), anyInt(), anyInt()); assertEquals(Intent.ACTION_VIEW, intentCaptor.getValue().getAction()); assertNotNull(activity.mStartActivityIntent); assertEquals(Intent.ACTION_MAIN, activity.mStartActivityIntent.getAction()); assertNull(activity.mStartActivityIntent.getPackage()); assertNull(activity.mStartActivityIntent.getComponent()); assertEquals(CURRENT_USER_INFO.id, activity.mStartActivityIntent.getContentUserHint()); Intent innerIntent = activity.mStartActivityIntent.getSelector(); assertNotNull(innerIntent); assertEquals(Intent.ACTION_VIEW, innerIntent.getAction()); assertNull(innerIntent.getComponent()); assertNull(innerIntent.getPackage()); assertEquals(MANAGED_PROFILE_INFO.id, activity.mUserIdActivityLaunchedIn); } public static class IntentForwarderWrapperActivity extends IntentForwarderActivity { private Intent mStartActivityIntent; private int mUserIdActivityLaunchedIn; @Override public void onCreate(@Nullable Bundle savedInstanceState) { getIntent().setComponent(sComponentName); super.onCreate(savedInstanceState); } @Override protected Injector createInjector() { return sInjector; } @Override public void startActivityAsCaller(Intent intent, @Nullable Bundle options, boolean ignoreTargetSecurity, int userId) { mStartActivityIntent = intent; mUserIdActivityLaunchedIn = userId; } } class TestInjector implements IntentForwarderActivity.Injector { @Override public IPackageManager getIPackageManager() { return mIPm; } @Override public UserManager getUserManager() { return mUserManager; } @Override public PackageManager getPackageManager() { return mPm; } } } No newline at end of file