From de16f7176ffdf0b8bc74653af16815291e2d0787 Mon Sep 17 00:00:00 2001 From: Maurice Lam Date: Thu, 3 Feb 2022 13:09:23 -0800 Subject: [PATCH] Roll forward "Show error dialog if activity is blocked from streaming" Added BlockedAppStreamingActivity to an "always allow" list so that it can always be displayed even if no other activities are allowed. Also, instead of reusing BlockedAppActivity, separated it out to a different activity that doesn't have the lockTaskMode="always". Bug: 203834118 Test: atest CtsVirtualDevicesTestsCases Change-Id: I5f0b68cd8ac701847489130e6018ecafb1ebc4a8 --- .../app/BlockedAppStreamingActivity.java | 80 +++++++++++++++++++ core/res/AndroidManifest.xml | 6 ++ core/res/res/values/strings.xml | 9 +++ core/res/res/values/symbols.xml | 3 + .../GenericWindowPolicyController.java | 35 +++++--- .../companion/virtual/VirtualDeviceImpl.java | 15 +++- 6 files changed, 137 insertions(+), 11 deletions(-) create mode 100644 core/java/com/android/internal/app/BlockedAppStreamingActivity.java diff --git a/core/java/com/android/internal/app/BlockedAppStreamingActivity.java b/core/java/com/android/internal/app/BlockedAppStreamingActivity.java new file mode 100644 index 0000000000000..31c38220ccc21 --- /dev/null +++ b/core/java/com/android/internal/app/BlockedAppStreamingActivity.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2022 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.content.Intent; +import android.content.pm.ActivityInfo; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Slog; + +import com.android.internal.R; + +/** + * A dialog shown to the user when they try to launch an app that is not allowed on a virtual + * device. The intent to start this activity must be created with the static factory method provided + * below. + */ +public class BlockedAppStreamingActivity extends AlertActivity { + + private static final String TAG = "BlockedAppStreamingActivity"; + private static final String PACKAGE_NAME = "com.android.internal.app"; + private static final String EXTRA_BLOCKED_ACTIVITY_INFO = + PACKAGE_NAME + ".extra.BLOCKED_ACTIVITY_INFO"; + private static final String EXTRA_STREAMED_DEVICE = PACKAGE_NAME + ".extra.STREAMED_DEVICE"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + CharSequence appLabel = null; + ActivityInfo activityInfo = intent.getParcelableExtra(EXTRA_BLOCKED_ACTIVITY_INFO); + if (activityInfo != null) { + appLabel = activityInfo.loadLabel(getPackageManager()); + } + + if (TextUtils.isEmpty(appLabel)) { + Slog.wtf(TAG, "Invalid activity info: " + activityInfo); + finish(); + return; + } + + CharSequence streamedDeviceName = intent.getCharSequenceExtra(EXTRA_STREAMED_DEVICE); + if (!TextUtils.isEmpty(streamedDeviceName)) { + mAlertParams.mTitle = getString(R.string.app_streaming_blocked_title, appLabel); + mAlertParams.mMessage = + getString(R.string.app_streaming_blocked_message, streamedDeviceName); + } else { + mAlertParams.mTitle = getString(R.string.app_blocked_title); + mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel); + } + mAlertParams.mPositiveButtonText = getString(android.R.string.ok); + setupAlert(); + } + + /** + * Creates an intent that launches {@link BlockedAppStreamingActivity} when app streaming is + * blocked. + */ + public static Intent createIntent(ActivityInfo activityInfo, CharSequence streamedDeviceName) { + return new Intent() + .setClassName("android", BlockedAppStreamingActivity.class.getName()) + .putExtra(EXTRA_BLOCKED_ACTIVITY_INFO, activityInfo) + .putExtra(EXTRA_STREAMED_DEVICE, streamedDeviceName); + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 74bf152344c3b..388f4d37d4bb4 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -6582,6 +6582,12 @@ android:process=":ui"> + + + %1$s is not available right now. + + %1$s unavailable + + This can’t be accessed on your %1$s at this time. Try on your Android TV device instead. + + This can’t be accessed on your %1$s at this time. Try on your tablet instead. + + This can’t be accessed on your %1$s at this time. Try on your phone instead. + This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 2b25c3eb099b1..3b2bac1a9bd1b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3285,6 +3285,9 @@ + + + diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java index 75acf81a4a3cc..dafcc60fb651d 100644 --- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java @@ -37,8 +37,11 @@ import android.util.Slog; import android.view.Display; import android.window.DisplayWindowPolicyController; +import com.android.internal.app.BlockedAppStreamingActivity; + import java.util.List; import java.util.Set; +import java.util.function.Consumer; /** @@ -48,6 +51,9 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController { private static final String TAG = "VirtualDeviceManager"; + private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT = + new ComponentName("android", BlockedAppStreamingActivity.class.getName()); + /** * If required, allow the secure activity to display on remote device since * {@link android.os.Build.VERSION_CODES#TIRAMISU}. @@ -61,6 +67,7 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController { private final ArraySet mAllowedActivities; @Nullable private final ArraySet mBlockedActivities; + private Consumer mActivityBlockedCallback; @NonNull final ArraySet mRunningUids = new ArraySet<>(); @@ -81,10 +88,12 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController { @NonNull ArraySet allowedUsers, @Nullable Set allowedActivities, @Nullable Set blockedActivities, - @NonNull ActivityListener activityListener) { + @NonNull ActivityListener activityListener, + @NonNull Consumer activityBlockedCallback) { mAllowedUsers = allowedUsers; mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities); mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities); + mActivityBlockedCallback = activityBlockedCallback; setInterestedWindowFlags(windowFlags, systemWindowFlags); mActivityListener = activityListener; } @@ -96,6 +105,7 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController { for (int i = 0; i < activityCount; i++) { final ActivityInfo aInfo = activities.get(i); if (!canContainActivity(aInfo, /* windowFlags= */ 0, /* systemWindowFlags= */ 0)) { + mActivityBlockedCallback.accept(aInfo); return false; } } @@ -105,7 +115,11 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController { @Override public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags, int systemWindowFlags) { - return canContainActivity(activityInfo, windowFlags, systemWindowFlags); + if (!canContainActivity(activityInfo, windowFlags, systemWindowFlags)) { + mActivityBlockedCallback.accept(activityInfo); + return false; + } + return true; } @Override @@ -140,22 +154,23 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController { if ((activityInfo.flags & FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) { return false; } + ComponentName activityComponent = activityInfo.getComponentName(); + if (BLOCKED_APP_STREAMING_COMPONENT.equals(activityComponent)) { + // The error dialog alerting users that streaming is blocked is always allowed. + return true; + } final UserHandle activityUser = UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid); if (!mAllowedUsers.contains(activityUser)) { Slog.d(TAG, "Virtual device activity not allowed from user " + activityUser); return false; } - if (mBlockedActivities != null - && mBlockedActivities.contains(activityInfo.getComponentName())) { - Slog.d(TAG, - "Virtual device blocking launch of " + activityInfo.getComponentName()); + if (mBlockedActivities != null && mBlockedActivities.contains(activityComponent)) { + Slog.d(TAG, "Virtual device blocking launch of " + activityComponent); return false; } - if (mAllowedActivities != null - && !mAllowedActivities.contains(activityInfo.getComponentName())) { - Slog.d(TAG, - activityInfo.getComponentName() + " is not in the allowed list."); + if (mAllowedActivities != null && !mAllowedActivities.contains(activityComponent)) { + Slog.d(TAG, activityComponent + " is not in the allowed list."); return false; } if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE, diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 47e218b48c093..5f3a030360f90 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -34,6 +34,8 @@ import android.companion.virtual.VirtualDeviceManager.ActivityListener; import android.companion.virtual.VirtualDeviceParams; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; import android.graphics.Point; import android.graphics.PointF; import android.hardware.display.DisplayManager; @@ -57,6 +59,7 @@ import android.window.DisplayWindowPolicyController; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.BlockedAppStreamingActivity; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -441,7 +444,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub getAllowedUserHandles(), mParams.getAllowedActivities(), mParams.getBlockedActivities(), - createListenerAdapter(displayId)); + createListenerAdapter(displayId), + activityInfo -> onActivityBlocked(displayId, activityInfo)); mWindowPolicyControllers.put(displayId, dwpc); return dwpc; } @@ -464,6 +468,15 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } + private void onActivityBlocked(int displayId, ActivityInfo activityInfo) { + Intent intent = BlockedAppStreamingActivity.createIntent( + activityInfo, mAssociationInfo.getDisplayName()); + mContext.startActivityAsUser( + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), + ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(), + mContext.getUser()); + } + private ArraySet getAllowedUserHandles() { ArraySet result = new ArraySet<>(); DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);