Intercept chooser intents to use unbundled chooser (when enabled)
When SystemUiDeviceConfigFlags.USE_DELEGATE_CHOOSER is true (default
false), rewrite intents destined for the sytem chooser to go to the
unbundled chooser and attach a start activity permission token.
Outside of this CL we're working on removing the need for the permission
token. When unbundling is complete, there won't be any rewriting of
components, as the intent will already be destined for the unbundled chooser.
Assuming the token goes away, we could then remove the interceptor
(b/222706900).
Bug: 202165481
Bug: 222706900
Test: atest ChooserActivityTest, manual tests with the flag enabled and
not to verify that the proper sharesheet appears and functions.
Change-Id: I56e26157fc3e1b3f2b4dfeae4bb1ab555191e047
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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.server.pm;
|
||||
|
||||
import static com.android.server.wm.ActivityInterceptorCallback.INTENT_RESOLVER_ORDERED_ID;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.wm.ActivityInterceptorCallback;
|
||||
import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo;
|
||||
import com.android.server.wm.ActivityTaskManagerInternal;
|
||||
|
||||
/**
|
||||
* Service to register an {@code ActivityInterceptorCallback} that modifies any {@code Intent}
|
||||
* that's being used to launch a user-space {@code ChooserActivity}, by adding
|
||||
* EXTRA_PERMISSION_TOKEN, a Binder representing a single-use-only permission to invoke the
|
||||
* #startActivityAsCaller() API (which normally isn't available in user-space); and setting the
|
||||
* destination component to the delegated component when appropriate.
|
||||
*/
|
||||
public final class IntentResolverInterceptor {
|
||||
private static final String TAG = "IntentResolverIntercept";
|
||||
|
||||
private final Context mContext;
|
||||
private boolean mUseDelegateChooser;
|
||||
|
||||
private final ActivityInterceptorCallback mActivityInterceptorCallback =
|
||||
new ActivityInterceptorCallback() {
|
||||
@Nullable
|
||||
@Override
|
||||
public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
|
||||
if (mUseDelegateChooser && isChooserActivity(info)) {
|
||||
return new ActivityInterceptResult(
|
||||
modifyChooserIntent(info.intent),
|
||||
info.checkedOptions);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
public IntentResolverInterceptor(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening for intents and USE_DELEGATE_CHOOSER property changes.
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
|
||||
public void registerListeners() {
|
||||
LocalServices.getService(ActivityTaskManagerInternal.class)
|
||||
.registerActivityStartInterceptor(INTENT_RESOLVER_ORDERED_ID,
|
||||
mActivityInterceptorCallback);
|
||||
|
||||
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||
mContext.getMainExecutor(), properties -> updateUseDelegateChooser());
|
||||
updateUseDelegateChooser();
|
||||
}
|
||||
|
||||
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
|
||||
private void updateUseDelegateChooser() {
|
||||
mUseDelegateChooser = DeviceConfig.getBoolean(
|
||||
DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||
SystemUiDeviceConfigFlags.USE_DELEGATE_CHOOSER,
|
||||
false);
|
||||
}
|
||||
|
||||
private Intent modifyChooserIntent(Intent intent) {
|
||||
intent.setComponent(getUnbundledChooserComponentName());
|
||||
addStartActivityPermissionTokenToIntent(intent, getUnbundledChooserComponentName());
|
||||
return intent;
|
||||
}
|
||||
|
||||
private static boolean isChooserActivity(ActivityInterceptorInfo info) {
|
||||
ComponentName targetComponent = new ComponentName(info.aInfo.packageName, info.aInfo.name);
|
||||
|
||||
return targetComponent.equals(getSystemChooserComponentName())
|
||||
|| targetComponent.equals(getUnbundledChooserComponentName());
|
||||
}
|
||||
|
||||
private static Intent addStartActivityPermissionTokenToIntent(
|
||||
Intent intent, ComponentName grantee) {
|
||||
try {
|
||||
intent.putExtra(
|
||||
ActivityTaskManager.EXTRA_PERMISSION_TOKEN,
|
||||
ActivityTaskManager.getService().requestStartActivityPermissionToken(grantee));
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Failed to add permission token to chooser intent");
|
||||
}
|
||||
return intent;
|
||||
}
|
||||
|
||||
private static ComponentName getSystemChooserComponentName() {
|
||||
return new ComponentName("android", "com.android.internal.app.ChooserActivity");
|
||||
}
|
||||
|
||||
private static ComponentName getUnbundledChooserComponentName() {
|
||||
return ComponentName.unflattenFromString(
|
||||
Resources.getSystem().getString(R.string.config_chooserActivity));
|
||||
}
|
||||
}
|
||||
@@ -957,6 +957,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
private final ResolveIntentHelper mResolveIntentHelper;
|
||||
private final DexOptHelper mDexOptHelper;
|
||||
private final SuspendPackageHelper mSuspendPackageHelper;
|
||||
private final IntentResolverInterceptor mIntentResolverInterceptor;
|
||||
|
||||
/**
|
||||
* Invalidate the package info cache, which includes updating the cached computer.
|
||||
@@ -1710,6 +1711,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|
||||
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
|
||||
|
||||
mIntentResolverInterceptor = null;
|
||||
|
||||
registerObservers(false);
|
||||
invalidatePackageInfoCache();
|
||||
}
|
||||
@@ -2238,6 +2241,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|
||||
mServiceStartWithDelay = SystemClock.uptimeMillis() + (60 * 1000L);
|
||||
|
||||
mIntentResolverInterceptor = new IntentResolverInterceptor(mContext);
|
||||
|
||||
Slog.i(TAG, "Fix for b/169414761 is applied");
|
||||
}
|
||||
|
||||
@@ -6395,6 +6400,11 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|
||||
// Prune unused static shared libraries which have been cached a period of time
|
||||
schedulePruneUnusedStaticSharedLibraries(false /* delay */);
|
||||
|
||||
// TODO(b/222706900): Remove this intent interceptor before T launch
|
||||
if (mIntentResolverInterceptor != null) {
|
||||
mIntentResolverInterceptor.registerListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -58,6 +58,7 @@ public abstract class ActivityInterceptorCallback {
|
||||
@IntDef(suffix = { "_ORDERED_ID" }, value = {
|
||||
FIRST_ORDERED_ID,
|
||||
PERMISSION_POLICY_ORDERED_ID,
|
||||
INTENT_RESOLVER_ORDERED_ID,
|
||||
VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
|
||||
LAST_ORDERED_ID // Update this when adding new ids
|
||||
})
|
||||
@@ -74,6 +75,11 @@ public abstract class ActivityInterceptorCallback {
|
||||
*/
|
||||
public static final int PERMISSION_POLICY_ORDERED_ID = 1;
|
||||
|
||||
/**
|
||||
* The identifier for {@link com.android.server.pm.IntentResolverInterceptor}.
|
||||
*/
|
||||
public static final int INTENT_RESOLVER_ORDERED_ID = 2;
|
||||
|
||||
/**
|
||||
* The identifier for {@link com.android.server.companion.virtual.VirtualDeviceManagerService}
|
||||
* interceptor.
|
||||
|
||||
Reference in New Issue
Block a user