diff options
6 files changed, 137 insertions, 11 deletions
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 000000000000..31c38220ccc2 --- /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 74bf152344c3..388f4d37d4bb 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -6582,6 +6582,12 @@ android:process=":ui"> </activity> + <activity android:name="com.android.internal.app.BlockedAppStreamingActivity" + android:theme="@style/Theme.Dialog.Confirmation" + android:excludeFromRecents="true" + android:process=":ui"> + </activity> + <activity android:name="com.android.internal.app.LaunchAfterAuthenticationActivity" android:theme="@style/Theme.Translucent.NoTitleBar" android:excludeFromRecents="true" diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 47b4d38cf0ce..63cc113f1c38 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5464,6 +5464,15 @@ <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> is not available right now. </string> + <!-- Title of the dialog shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] --> + <string name="app_streaming_blocked_title"><xliff:g id="activity" example="Permission dialog">%1$s</xliff:g> unavailable</string> + <!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] --> + <string name="app_streaming_blocked_message" product="tv">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your Android TV device instead.</string> + <!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] --> + <string name="app_streaming_blocked_message" product="tablet">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your tablet instead.</string> + <!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] --> + <string name="app_streaming_blocked_message" product="default">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your phone instead.</string> + <!-- Message displayed in dialog when app is too old to run on this verison of android. [CHAR LIMIT=NONE] --> <string name="deprecated_target_sdk_message">This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer.</string> <!-- Title for button to see application detail in app store which it came from - it may allow user to update to newer version. [CHAR LIMIT=50] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 2b25c3eb099b..3b2bac1a9bd1 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3285,6 +3285,9 @@ <java-symbol type="string" name="app_blocked_title" /> <java-symbol type="string" name="app_blocked_message" /> + <java-symbol type="string" name="app_streaming_blocked_title" /> + <java-symbol type="string" name="app_streaming_blocked_message" /> + <!-- Used internally for assistant to launch activity transitions --> <java-symbol type="id" name="cross_task_transition" /> 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 75acf81a4a3c..dafcc60fb651 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<ComponentName> mAllowedActivities; @Nullable private final ArraySet<ComponentName> mBlockedActivities; + private Consumer<ActivityInfo> mActivityBlockedCallback; @NonNull final ArraySet<Integer> mRunningUids = new ArraySet<>(); @@ -81,10 +88,12 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController { @NonNull ArraySet<UserHandle> allowedUsers, @Nullable Set<ComponentName> allowedActivities, @Nullable Set<ComponentName> blockedActivities, - @NonNull ActivityListener activityListener) { + @NonNull ActivityListener activityListener, + @NonNull Consumer<ActivityInfo> 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 47e218b48c09..5f3a030360f9 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<UserHandle> getAllowedUserHandles() { ArraySet<UserHandle> result = new ArraySet<>(); DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); |