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

This commit is contained in:
TreeHugger Robot
2018-08-06 17:25:48 +00:00
committed by Android (Google) Code Review
2 changed files with 256 additions and 49 deletions

View File

@@ -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,
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));
final ResolveInfo ri = mInjector.resolveActivityAsUser(newIntent, MATCH_DEFAULT_ONLY,
targetUserId);
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);
}
}

View File

@@ -16,33 +16,6 @@
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;
@@ -51,11 +24,42 @@ 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 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.MockitoAnnotations;
@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) {}
}
}