Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5c4f60a0 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Fix cannot share text from twitter to managed profile"

parents 0b1ca1f0 96d78f5f
Loading
Loading
Loading
Loading
+91 −39
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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();
@@ -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);
@@ -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.
@@ -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;
    }

    /**
@@ -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;
        }
@@ -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");
@@ -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();
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -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">
+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