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

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

Merge "No disclaimer when switching profile for system SMS and dialer apps." into pi-dev

parents e85115fd 64439c1e
Loading
Loading
Loading
Loading
+60 −20
Original line number Diff line number Diff line
@@ -16,14 +16,21 @@

package com.android.internal.app;

import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;

import android.annotation.Nullable;
import android.annotation.StringRes;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.admin.DevicePolicyManager;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.RemoteException;
@@ -31,12 +38,11 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
import android.widget.Toast;

import com.android.internal.annotations.VisibleForTesting;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
import java.util.Set;

/**
 * This is used in conjunction with
@@ -44,7 +50,6 @@ import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
 * 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
@@ -53,6 +58,9 @@ public class IntentForwarderActivity extends Activity {
    public static String FORWARD_INTENT_TO_MANAGED_PROFILE
            = "com.android.internal.app.ForwardIntentToManagedProfile";

    private static final Set<String> ALLOWED_TEXT_MESSAGE_SCHEME
            = new HashSet<>(Arrays.asList("sms", "smsto", "mms", "mmsto"));

    private Injector mInjector;

    @Override
@@ -93,19 +101,8 @@ public class IntentForwarderActivity extends Activity {
                newIntent.prepareToLeaveUser(callingUserId);
            }

            final android.content.pm.ResolveInfo ri =
                    mInjector.getPackageManager().resolveActivityAsUser(
                            newIntent,
                            MATCH_DEFAULT_ONLY,
            final ResolveInfo ri = mInjector.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) {
@@ -124,8 +121,8 @@ public class IntentForwarderActivity extends Activity {
                        + ActivityThread.currentProcessName(), e);
            }

            if (shouldShowDisclosure) {
                Toast.makeText(this, getString(userMessageId), Toast.LENGTH_LONG).show();
            if (shouldShowDisclosure(ri, intentReceived)) {
                mInjector.showToast(userMessageId, Toast.LENGTH_LONG);
            }
        } else {
            Slog.wtf(TAG, "the intent: " + intentReceived + " cannot be forwarded from user "
@@ -134,6 +131,35 @@ public class IntentForwarderActivity extends Activity {
        finish();
    }

    private boolean shouldShowDisclosure(@Nullable ResolveInfo ri, Intent intent) {
        if (ri == null || ri.activityInfo == null) {
            return true;
        }
        if (ri.activityInfo.applicationInfo.isSystemApp()
                && (isDialerIntent(intent) || isTextMessageIntent(intent))) {
            return false;
        }
        return !isTargetResolverOrChooserActivity(ri.activityInfo);
    }

    private boolean isTextMessageIntent(Intent intent) {
        return Intent.ACTION_SENDTO.equals(intent.getAction()) && intent.getData() != null
            && ALLOWED_TEXT_MESSAGE_SCHEME.contains(intent.getData().getScheme());
    }

    private boolean isDialerIntent(Intent intent) {
        return Intent.ACTION_DIAL.equals(intent.getAction())
            || Intent.ACTION_CALL.equals(intent.getAction());
    }

    private boolean isTargetResolverOrChooserActivity(ActivityInfo activityInfo) {
        if (!"android".equals(activityInfo.packageName)) {
            return false;
        }
        return ResolverActivity.class.getName().equals(activityInfo.name)
            || ChooserActivity.class.getName().equals(activityInfo.name);
    }

    /**
     * Check whether the intent can be forwarded to target user. Return the intent used for
     * forwarding if it can be forwarded, {@code null} otherwise.
@@ -241,6 +267,16 @@ public class IntentForwarderActivity extends Activity {
        public PackageManager getPackageManager() {
            return IntentForwarderActivity.this.getPackageManager();
        }

        @Override
        public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
            return getPackageManager().resolveActivityAsUser(intent, flags, userId);
        }

        @Override
        public void showToast(int messageId, int duration) {
            Toast.makeText(IntentForwarderActivity.this, getString(messageId), duration).show();
        }
    }

    public interface Injector {
@@ -249,5 +285,9 @@ public class IntentForwarderActivity extends Activity {
        UserManager getUserManager();

        PackageManager getPackageManager();

        ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId);

        void showToast(@StringRes int messageId, int duration);
    }
}
+186 −19
Original line number Diff line number Diff line
@@ -16,46 +16,50 @@

package com.android.internal.app;

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.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
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 java.util.ArrayList;
import java.util.List;
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",
@@ -77,22 +81,26 @@ public class IntentForwarderActivityTest {

    private static IntentForwarderActivity.Injector sInjector;
    private static ComponentName sComponentName;
    private static String sActivityName;
    private static String sPackageName;

    @Mock private IPackageManager mIPm;
    @Mock private PackageManager mPm;
    @Mock private UserManager mUserManager;
    @Mock private ApplicationInfo mApplicationInfo;

    @Rule
    public ActivityTestRule<IntentForwarderWrapperActivity> mActivityRule =
            new ActivityTestRule<>(IntentForwarderWrapperActivity.class, true, false);

    private Context mContext;
    public static final String PHONE_NUMBER = "123-456-789";

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        mContext = InstrumentationRegistry.getTargetContext();
        sInjector = new TestInjector();
        sInjector = spy(new TestInjector());
    }

    @Test
@@ -252,6 +260,149 @@ public class IntentForwarderActivityTest {
        assertEquals(MANAGED_PROFILE_INFO.id, activity.mUserIdActivityLaunchedIn);
    }

    @Test
    public void shouldSkipDisclosure_notWhitelisted() throws RemoteException {
        setupShouldSkipDisclosureTest();
        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
            .setAction(Intent.ACTION_SEND)
            .setType(TYPE_PLAIN_TEXT);

        mActivityRule.launchActivity(intent);

        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
        verify(sInjector).showToast(anyInt(), anyInt());
    }

    @Test
    public void shouldSkipDisclosure_withResolverActivity() throws RemoteException {
        setupShouldSkipDisclosureTest();
        sActivityName = ResolverActivity.class.getName();
        sPackageName = "android";
        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
            .setAction(Intent.ACTION_SEND)
            .setType(TYPE_PLAIN_TEXT);

        mActivityRule.launchActivity(intent);

        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
        verify(sInjector, never()).showToast(anyInt(), anyInt());
    }

    @Test
    public void shouldSkipDisclosure_callIntent_call() throws RemoteException {
        setupShouldSkipDisclosureTest();
        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
            .setAction(Intent.ACTION_CALL);

        mActivityRule.launchActivity(intent);

        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
        verify(sInjector, never()).showToast(anyInt(), anyInt());
    }

    @Test
    public void shouldSkipDisclosure_callIntent_dial() throws RemoteException {
        setupShouldSkipDisclosureTest();
        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
            .setAction(Intent.ACTION_DIAL);

        mActivityRule.launchActivity(intent);

        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
        verify(sInjector, never()).showToast(anyInt(), anyInt());
    }

    @Test
    public void shouldSkipDisclosure_callIntent_notCallOrDial() throws RemoteException {
        setupShouldSkipDisclosureTest();
        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
            .setAction(Intent.ACTION_ALARM_CHANGED);

        mActivityRule.launchActivity(intent);

        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
        verify(sInjector).showToast(anyInt(), anyInt());
    }

    @Test
    public void shouldSkipDisclosure_textMessageIntent_sms() throws RemoteException {
        setupShouldSkipDisclosureTest();
        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
            .setAction(Intent.ACTION_SENDTO)
            .setData(Uri.fromParts("sms", PHONE_NUMBER, null));

        mActivityRule.launchActivity(intent);

        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
        verify(sInjector, never()).showToast(anyInt(), anyInt());
    }

    @Test
    public void shouldSkipDisclosure_textMessageIntent_smsto() throws RemoteException {
        setupShouldSkipDisclosureTest();
        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
            .setAction(Intent.ACTION_SENDTO)
            .setData(Uri.fromParts("smsto", PHONE_NUMBER, null));

        mActivityRule.launchActivity(intent);

        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
        verify(sInjector, never()).showToast(anyInt(), anyInt());
    }

    @Test
    public void shouldSkipDisclosure_textMessageIntent_mms() throws RemoteException {
        setupShouldSkipDisclosureTest();
        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
            .setAction(Intent.ACTION_SENDTO)
            .setData(Uri.fromParts("mms", PHONE_NUMBER, null));

        mActivityRule.launchActivity(intent);

        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
        verify(sInjector, never()).showToast(anyInt(), anyInt());
    }

    @Test
    public void shouldSkipDisclosure_textMessageIntent_mmsto() throws RemoteException {
        setupShouldSkipDisclosureTest();
        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
            .setAction(Intent.ACTION_SENDTO)
            .setData(Uri.fromParts("mmsto", PHONE_NUMBER, null));

        mActivityRule.launchActivity(intent);

        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
        verify(sInjector, never()).showToast(anyInt(), anyInt());
    }

    @Test
    public void shouldSkipDisclosure_textMessageIntent_invalidUri() throws RemoteException {
        setupShouldSkipDisclosureTest();
        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
            .setAction(Intent.ACTION_SENDTO)
            .setData(Uri.fromParts("invalid", PHONE_NUMBER, null));

        mActivityRule.launchActivity(intent);

        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
        verify(sInjector).showToast(anyInt(), anyInt());
    }

    private void setupShouldSkipDisclosureTest() throws RemoteException {
        sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
        sActivityName = "MyTestActivity";
        sPackageName = "test.package.name";
        when(mApplicationInfo.isSystemApp()).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 can be forwarded.
        when(mIPm.canForwardTo(
            any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true);
    }

    public static class IntentForwarderWrapperActivity extends IntentForwarderActivity {
        private Intent mStartActivityIntent;
@@ -276,7 +427,7 @@ public class IntentForwarderActivityTest {
        }
    }

    class TestInjector implements IntentForwarderActivity.Injector {
    public class TestInjector implements IntentForwarderActivity.Injector {

        @Override
        public IPackageManager getIPackageManager() {
@@ -292,5 +443,21 @@ public class IntentForwarderActivityTest {
        public PackageManager getPackageManager() {
            return mPm;
        }

        @Override
        public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
            ActivityInfo activityInfo = new ActivityInfo();
            activityInfo.packageName = sPackageName;
            activityInfo.name = sActivityName;
            activityInfo.applicationInfo = mApplicationInfo;

            ResolveInfo resolveInfo = new ResolveInfo();
            resolveInfo.activityInfo = activityInfo;

            return resolveInfo;
        }

        @Override
        public void showToast(int messageId, int duration) {}
    }
}
 No newline at end of file