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

Commit 13b8f773 authored by Liahav Eitan's avatar Liahav Eitan
Browse files

Ask for user consent before resolving personal intent in work profile

Bug: 262700480
Test: manual, see screencasts on bug

Change-Id: I32a8960d0a59f26752ad838ba6c3aee3c4d64be0
parent 96919901
Loading
Loading
Loading
Loading
+52 −4
Original line number Diff line number Diff line
@@ -45,8 +45,13 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.util.Slog;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -151,17 +156,60 @@ public class IntentForwarderActivity extends Activity {
                    if (isResolverActivityResolveInfo(targetResolveInfo)) {
                        launchResolverActivityWithCorrectTab(intentReceived, className, newIntent,
                                callingUserId, targetUserId);
                        return targetResolveInfo;
                    }
                    // When switching to the personal profile, automatically start the activity
                    } else if (className.equals(FORWARD_INTENT_TO_PARENT)) {
                        startActivityAsCaller(newIntent, targetUserId);
                    }
                    return targetResolveInfo;
                }, mExecutorService)
                .thenAcceptAsync(result -> {
                    // When switching to the personal profile, inform user after starting activity
                    if (className.equals(FORWARD_INTENT_TO_PARENT)) {
                        maybeShowDisclosure(intentReceived, result, userMessage);
                        finish();
                    // When switching to the work profile, ask the user for consent before launching
                    } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
                        maybeShowUserConsentMiniResolver(result, newIntent, targetUserId);
                    }
                }, getApplicationContext().getMainExecutor());
    }

    private void maybeShowUserConsentMiniResolver(
            ResolveInfo target, Intent launchIntent, int targetUserId) {
        if (target == null || isIntentForwarderResolveInfo(target) || !isDeviceProvisioned()) {
            finish();
            return;
        }

        int layoutId = R.layout.miniresolver;
        setContentView(layoutId);

        findViewById(R.id.title_container).setElevation(0);

        ImageView icon = findViewById(R.id.icon);
        PackageManager packageManagerForTargetUser =
                createContextAsUser(UserHandle.of(targetUserId), /* flags= */ 0)
                        .getPackageManager();
        icon.setImageDrawable(target.loadIcon(packageManagerForTargetUser));

        View buttonContainer = findViewById(R.id.button_bar_container);
        buttonContainer.setPadding(0, 0, 0, buttonContainer.getPaddingBottom());

        ((TextView) findViewById(R.id.open_cross_profile)).setText(
                getResources().getString(
                        R.string.miniresolver_open_in_work,
                        target.loadLabel(packageManagerForTargetUser)));

        // The mini-resolver's negative button is reused in this flow to cancel the intent
        ((Button) findViewById(R.id.use_same_profile_browser)).setText(R.string.cancel);
        findViewById(R.id.use_same_profile_browser).setOnClickListener(v -> finish());

        findViewById(R.id.button_open).setOnClickListener(v -> {
            startActivityAsCaller(launchIntent, targetUserId);
            finish();
        });
    }

    private String getForwardToPersonalMessage() {
        return getSystemService(DevicePolicyManager.class).getResources().getString(
                FORWARD_INTENT_TO_PERSONAL,
+4 −1
Original line number Diff line number Diff line
@@ -7147,8 +7147,11 @@
        </activity>
        <activity android:name="com.android.internal.app.IntentForwarderActivity"
                android:finishOnCloseSystemDialogs="true"
                android:theme="@style/Theme.Translucent.NoTitleBar"
                android:theme="@style/Theme.DeviceDefault.Resolver"
                android:excludeFromRecents="true"
                android:documentLaunchMode="never"
                android:relinquishTaskIdentity="true"
                android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
                android:label="@string/user_owner_label"
                android:exported="true"
                android:visibleToInstantApps="true"
+6 −0
Original line number Diff line number Diff line
@@ -14,6 +14,11 @@
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->

<!-- Layout used to decide whether to launch a single target in another profile.
     When this layout is used in ResolverActivity, the user can choose between a verified app in the
     other profile and the default browser in the current profile.
     In IntentForwarderActivity, they choose whether to launch in the other profile or cancel. -->
<com.android.internal.widget.ResolverDrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
@@ -24,6 +29,7 @@
    android:id="@id/contentPanel">

    <RelativeLayout
        android:id="@+id/title_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alwaysShow="true"
+33 −2
Original line number Diff line number Diff line
@@ -16,6 +16,12 @@

package com.android.internal.app;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
@@ -53,6 +59,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;

import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;

@@ -140,6 +147,8 @@ public class IntentForwarderActivityTest {
    @Test
    public void forwardToManagedProfile_canForward_sendIntent() throws Exception {
        sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
        sActivityName = "MyTestActivity";
        sPackageName = "test.package.name";

        // Intent can be forwarded.
        when(mIPm.canForwardTo(
@@ -160,7 +169,13 @@ public class IntentForwarderActivityTest {
        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());
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
        onView(withId(R.id.icon)).check(matches(isDisplayed()));
        onView(withId(R.id.open_cross_profile)).check(matches(isDisplayed()));
        onView(withId(R.id.use_same_profile_browser)).check(matches(isDisplayed()));
        onView(withId(R.id.button_open)).perform(click());
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();

        assertNotNull(activity.mStartActivityIntent);
        assertEquals(Intent.ACTION_SEND, activity.mStartActivityIntent.getAction());
        assertNull(activity.mStartActivityIntent.getPackage());
@@ -250,6 +265,8 @@ public class IntentForwarderActivityTest {
    @Test
    public void forwardToManagedProfile_canForward_selectorIntent() throws Exception {
        sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
        sActivityName = "MyTestActivity";
        sPackageName = "test.package.name";

        // Intent can be forwarded.
        when(mIPm.canForwardTo(
@@ -264,6 +281,7 @@ public class IntentForwarderActivityTest {
        // 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);
@@ -271,6 +289,13 @@ public class IntentForwarderActivityTest {
                intentCaptor.capture(), nullable(String.class), anyInt(), anyInt());
        assertEquals(Intent.ACTION_VIEW, intentCaptor.getValue().getAction());

        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
        onView(withId(R.id.icon)).check(matches(isDisplayed()));
        onView(withId(R.id.open_cross_profile)).check(matches(isDisplayed()));
        onView(withId(R.id.use_same_profile_browser)).check(matches(isDisplayed()));
        onView(withId(R.id.button_open)).perform(click());
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();

        assertNotNull(activity.mStartActivityIntent);
        assertEquals(Intent.ACTION_MAIN, activity.mStartActivityIntent.getAction());
        assertNull(activity.mStartActivityIntent.getPackage());
@@ -608,7 +633,7 @@ public class IntentForwarderActivityTest {
    }

    private void setupShouldSkipDisclosureTest() throws RemoteException {
        sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
        sComponentName = FORWARD_TO_PARENT_COMPONENT_NAME;
        sActivityName = "MyTestActivity";
        sPackageName = "test.package.name";
        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED,
@@ -619,6 +644,7 @@ public class IntentForwarderActivityTest {
        profiles.add(CURRENT_USER_INFO);
        profiles.add(MANAGED_PROFILE_INFO);
        when(mUserManager.getProfiles(anyInt())).thenReturn(profiles);
        when(mUserManager.getProfileParent(anyInt())).thenReturn(CURRENT_USER_INFO);
        // Intent can be forwarded.
        when(mIPm.canForwardTo(
                any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true);
@@ -653,6 +679,11 @@ public class IntentForwarderActivityTest {
            mUserIdActivityLaunchedIn = userId;
        }

        @Override
        public Context createContextAsUser(UserHandle user, int flags) {
            return this;
        }

        @Override
        protected MetricsLogger getMetricsLogger() {
            return mMetricsLogger;