Merge "Ask for user consent before resolving personal intent in work profile" into udc-dev

This commit is contained in:
Liahav Eitan
2023-02-21 17:05:15 +00:00
committed by Android (Google) Code Review
4 changed files with 95 additions and 7 deletions

View File

@@ -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);
}
startActivityAsCaller(newIntent, targetUserId);
return targetResolveInfo;
}, mExecutorService)
.thenAcceptAsync(result -> {
maybeShowDisclosure(intentReceived, result, userMessage);
finish();
// 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,

View File

@@ -7695,8 +7695,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"

View File

@@ -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"

View File

@@ -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;