Loading core/java/android/service/autofill/Dataset.java +8 −0 Original line number Diff line number Diff line Loading @@ -406,6 +406,8 @@ public final class Dataset implements Parcelable { * authentication. * * @throws IllegalStateException if {@link #build()} was already called. * @throws IllegalArgumentException if the provided content * {@link ClipData.Item#getIntent() contains an intent} * * @return this builder. * Loading @@ -416,6 +418,12 @@ public final class Dataset implements Parcelable { @SuppressLint("MissingGetterMatchingBuilder") public @NonNull Builder setContent(@NonNull AutofillId id, @Nullable ClipData content) { throwIfDestroyed(); if (content != null) { for (int i = 0; i < content.getItemCount(); i++) { Preconditions.checkArgument(content.getItemAt(i).getIntent() == null, "Content items cannot contain an Intent: content=" + content); } } setLifeTheUniverseAndEverything(id, null, null, null, null); mFieldContent = content; return this; Loading services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +8 −1 Original line number Diff line number Diff line Loading @@ -1246,8 +1246,10 @@ final class AutofillManagerServiceImpl mRemoteAugmentedAutofillService = null; } }; final int serviceUid = mRemoteAugmentedAutofillServiceInfo.applicationInfo.uid; mRemoteAugmentedAutofillService = new RemoteAugmentedAutofillService(getContext(), componentName, mUserId, callbacks, mMaster.isInstantServiceAllowed(), serviceUid, componentName, mUserId, callbacks, mMaster.isInstantServiceAllowed(), mMaster.verbose, mMaster.mAugmentedServiceIdleUnbindTimeoutMs, mMaster.mAugmentedServiceRequestTimeoutMs); } Loading @@ -1255,6 +1257,11 @@ final class AutofillManagerServiceImpl return mRemoteAugmentedAutofillService; } @GuardedBy("mLock") @Nullable RemoteAugmentedAutofillService getRemoteAugmentedAutofillServiceIfCreatedLocked() { return mRemoteAugmentedAutofillService; } /** * Called when the {@link AutofillManagerService#mAugmentedAutofillResolver} * changed (among other places). Loading services/autofill/java/com/android/server/autofill/AutofillUriGrantsManager.java 0 → 100644 +257 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.autofill; import static android.content.ContentResolver.SCHEME_CONTENT; import static com.android.server.autofill.Helper.sVerbose; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.IUriGrantsManager; import android.app.UriGrantsManager; import android.content.ClipData; import android.content.ComponentName; import android.content.ContentProvider; import android.content.Intent; import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; import com.android.server.uri.UriGrantsManagerInternal; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; /** * Grants and revokes URI permissions for content-based autofill suggestions. * * <p>Note that the system cannot just hand out grants directly; it must always do so on behalf of * an owner (see {@link com.android.server.uri.UriGrantsManagerService}). For autofill, the owner * is the autofill service provider that creates a given autofill suggestion containing a content * URI. Therefore, this manager class must be instantiated with the service uid of the provider for * which it will manage URI grants. * * <p>To dump the state of this class, use {@code adb shell dumpsys autofill}. * * <p>To dump all active URI permissions, use {@code adb shell dumpsys activity permissions}. */ final class AutofillUriGrantsManager { private static final String TAG = AutofillUriGrantsManager.class.getSimpleName(); private final int mSourceUid; @UserIdInt private final int mSourceUserId; @NonNull private final IBinder mPermissionOwner; @NonNull private final UriGrantsManagerInternal mUgmInternal; @NonNull private final IUriGrantsManager mUgm; // We use a local lock here for simplicity, since the synchronized code does not depend on // any other resources (the "hold and wait" condition required for deadlock is not present). // If this changes in the future, instead of using a local lock this should be updated to // use the shared lock from AutofillManagerServiceImpl. @NonNull private final Object mLock; // Tracks the URIs that have been granted to each package. For each URI, the map stores the // activities that triggered the grant. This allows revoking permissions only once all // activities that triggered the grant are finished. @NonNull @GuardedBy("mLock") private final ArrayMap<String, List<Pair<Uri, String>>> mActiveGrantsByPackage; /** * Creates a new instance of the manager. * * @param serviceUid The uid of the autofill service provider for which this manager is being * created. URI grants will be requested on behalf of this uid (ie, this uid will be passed as * the {@code fromUid} to {@link IUriGrantsManager#grantUriPermissionFromOwner}). */ AutofillUriGrantsManager(int serviceUid) { mSourceUid = serviceUid; mSourceUserId = UserHandle.getUserId(mSourceUid); mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class); mPermissionOwner = mUgmInternal.newUriPermissionOwner("autofill-" + serviceUid); mUgm = UriGrantsManager.getService(); mLock = new Object(); mActiveGrantsByPackage = new ArrayMap<>(0); } public void grantUriPermissions(@NonNull ComponentName targetActivity, @UserIdInt int targetUserId, @NonNull ClipData clip) { String targetPkg = targetActivity.getPackageName(); for (int i = 0; i < clip.getItemCount(); i++) { ClipData.Item item = clip.getItemAt(i); Uri uri = item.getUri(); if (uri == null || !SCHEME_CONTENT.equals(uri.getScheme())) { continue; } if (grantUriPermissions(targetPkg, targetUserId, uri)) { addToActiveGrants(uri, targetActivity); } } } public void revokeUriPermissions(@NonNull ComponentName targetActivity, @UserIdInt int targetUserId) { String targetPkg = targetActivity.getPackageName(); Set<Uri> urisWhoseGrantsShouldBeRevoked = removeFromActiveGrants(targetActivity); for (Uri uri : urisWhoseGrantsShouldBeRevoked) { revokeUriPermissions(targetPkg, targetUserId, uri); } } private boolean grantUriPermissions(@NonNull String targetPkg, @UserIdInt int targetUserId, @NonNull Uri uri) { final int sourceUserId = ContentProvider.getUserIdFromUri(uri, mSourceUserId); if (sVerbose) { Slog.v(TAG, "Granting URI permissions: uri=" + uri + ", sourceUid=" + mSourceUid + ", sourceUserId=" + sourceUserId + ", targetPkg=" + targetPkg + ", targetUserId=" + targetUserId); } final Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(uri); final long ident = Binder.clearCallingIdentity(); try { mUgm.grantUriPermissionFromOwner( mPermissionOwner, mSourceUid, targetPkg, uriWithoutUserId, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, targetUserId); return true; } catch (RemoteException e) { Slog.e(TAG, "Granting URI permissions failed: uri=" + uri + ", sourceUid=" + mSourceUid + ", sourceUserId=" + sourceUserId + ", targetPkg=" + targetPkg + ", targetUserId=" + targetUserId, e); return false; } finally { Binder.restoreCallingIdentity(ident); } } private void revokeUriPermissions(@NonNull String targetPkg, @UserIdInt int targetUserId, @NonNull Uri uri) { final int sourceUserId = ContentProvider.getUserIdFromUri(uri, mSourceUserId); if (sVerbose) { Slog.v(TAG, "Revoking URI permissions: uri=" + uri + ", sourceUid=" + mSourceUid + ", sourceUserId=" + sourceUserId + ", target=" + targetPkg + ", targetUserId=" + targetUserId); } final Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(uri); final long ident = Binder.clearCallingIdentity(); try { mUgmInternal.revokeUriPermissionFromOwner( mPermissionOwner, uriWithoutUserId, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, targetPkg, targetUserId); } finally { Binder.restoreCallingIdentity(ident); } } private void addToActiveGrants(@NonNull Uri uri, @NonNull ComponentName targetActivity) { synchronized (mLock) { String packageName = targetActivity.getPackageName(); List<Pair<Uri, String>> uris = mActiveGrantsByPackage.computeIfAbsent(packageName, k -> new ArrayList<>(1)); uris.add(Pair.create(uri, targetActivity.getClassName())); } } private Set<Uri> removeFromActiveGrants(@NonNull ComponentName targetActivity) { synchronized (mLock) { String targetPackageName = targetActivity.getPackageName(); List<Pair<Uri, String>> uris = mActiveGrantsByPackage.get(targetPackageName); if (uris == null || uris.isEmpty()) { return Collections.emptySet(); } // Collect all URIs whose grant was triggered by the target activity. String targetActivityClassName = targetActivity.getClassName(); Set<Uri> urisWhoseGrantsShouldBeRevoked = new ArraySet<>(1); for (Iterator<Pair<Uri, String>> iter = uris.iterator(); iter.hasNext(); ) { Pair<Uri, String> uriAndActivity = iter.next(); if (uriAndActivity.second.equals(targetActivityClassName)) { urisWhoseGrantsShouldBeRevoked.add(uriAndActivity.first); iter.remove(); } } // A URI grant may have been triggered by more than one activity for the same package. // We should not revoke a grant if it was triggered by multiple activities and one or // more of those activities is still alive. Therefore we do a second pass and prune // the set of URIs to be revoked if an additional activity that triggered its grant // is still present. for (Pair<Uri, String> uriAndActivity : uris) { urisWhoseGrantsShouldBeRevoked.remove(uriAndActivity.first); } // If there are no remaining URIs granted to the package, drop the entry from the map. if (uris.isEmpty()) { mActiveGrantsByPackage.remove(targetPackageName); } return urisWhoseGrantsShouldBeRevoked; } } /** * Dump the active URI grants. */ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) { synchronized (mLock) { if (mActiveGrantsByPackage.isEmpty()) { pw.print(prefix); pw.println("URI grants: none"); return; } pw.print(prefix); pw.println("URI grants:"); final String prefix2 = prefix + " "; final String prefix3 = prefix2 + " "; for (int i = mActiveGrantsByPackage.size() - 1; i >= 0; i--) { String packageName = mActiveGrantsByPackage.keyAt(i); pw.print(prefix2); pw.println(packageName); List<Pair<Uri, String>> uris = mActiveGrantsByPackage.valueAt(i); if (uris == null || uris.isEmpty()) { continue; } for (Pair<Uri, String> uriAndActivity : uris) { pw.print(prefix3); pw.println(uriAndActivity.first + ": " + uriAndActivity.second); } } } } } services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +20 −4 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.IResultReceiver; import com.android.server.autofill.ui.InlineFillUi; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CancellationException; Loading @@ -74,8 +75,9 @@ final class RemoteAugmentedAutofillService private final int mRequestTimeoutMs; private final ComponentName mComponentName; private final RemoteAugmentedAutofillServiceCallbacks mCallbacks; private final AutofillUriGrantsManager mUriGrantsManager; RemoteAugmentedAutofillService(Context context, ComponentName serviceName, RemoteAugmentedAutofillService(Context context, int serviceUid, ComponentName serviceName, int userId, RemoteAugmentedAutofillServiceCallbacks callbacks, boolean bindInstantServiceAllowed, boolean verbose, int idleUnbindTimeoutMs, int requestTimeoutMs) { Loading @@ -87,6 +89,7 @@ final class RemoteAugmentedAutofillService mRequestTimeoutMs = requestTimeoutMs; mComponentName = serviceName; mCallbacks = callbacks; mUriGrantsManager = new AutofillUriGrantsManager(serviceUid); // Bind right away. connect(); Loading Loading @@ -121,6 +124,10 @@ final class RemoteAugmentedAutofillService return mComponentName; } public AutofillUriGrantsManager getAutofillUriGrantsManager() { return mUriGrantsManager; } @Override // from ServiceConnector.Impl protected void onServiceConnectionStatusChanged( IAugmentedAutofillService service, boolean connected) { Loading Loading @@ -173,8 +180,8 @@ final class RemoteAugmentedAutofillService maybeRequestShowInlineSuggestions(sessionId, inlineSuggestionsRequest, inlineSuggestionsData, clientState, focusedId, focusedValue, inlineSuggestionsCallback, client, onErrorCallback, remoteRenderService, userId); inlineSuggestionsCallback, client, onErrorCallback, remoteRenderService, userId, activityComponent); if (!showingFillWindow) { requestAutofill.complete(null); } Loading Loading @@ -245,7 +252,8 @@ final class RemoteAugmentedAutofillService @Nullable Function<InlineFillUi, Boolean> inlineSuggestionsCallback, @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService, int userId) { int userId, @NonNull ComponentName targetActivity) { if (inlineSuggestionsData == null || inlineSuggestionsData.isEmpty() || inlineSuggestionsCallback == null || request == null || remoteRenderService == null) { Loading Loading @@ -299,6 +307,8 @@ final class RemoteAugmentedAutofillService final ArrayList<AutofillId> fieldIds = dataset.getFieldIds(); final ClipData content = dataset.getFieldContent(); if (content != null) { mUriGrantsManager.grantUriPermissions( targetActivity, userId, content); final AutofillId fieldId = fieldIds.get(0); if (sDebug) { Slog.d(TAG, "Calling client autofillContent(): " Loading Loading @@ -358,6 +368,12 @@ final class RemoteAugmentedAutofillService + ComponentName.flattenToShortString(mComponentName) + "]"; } @Override public void dump(@NonNull String prefix, @NonNull PrintWriter pw) { super.dump(prefix, pw); mUriGrantsManager.dump(prefix, pw); } /** * Called by {@link Session} when it's time to destroy all augmented autofill requests. */ Loading services/autofill/java/com/android/server/autofill/Session.java +27 −0 Original line number Diff line number Diff line Loading @@ -1555,6 +1555,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } // Get a handle to the RemoteAugmentedAutofillService. In // AutofillManagerServiceImpl.updateRemoteAugmentedAutofillService() we invalidate sessions // whenever the service changes, so there should never be a case when we get here and the // remote service instance is not present or different. final RemoteAugmentedAutofillService remoteAugmentedAutofillService = mService.getRemoteAugmentedAutofillServiceIfCreatedLocked(); if (remoteAugmentedAutofillService == null) { Slog.e(TAG, "Can't fill after auth: RemoteAugmentedAutofillService is null"); mService.resetLastAugmentedAutofillResponse(); removeFromServiceLocked(); return; } // Update state to ensure that after filling the field here we don't end up firing another // autofill request that will end up showing the same suggestions to the user again. When // the auth activity came up, the field for which the suggestions were shown lost focus and Loading @@ -1567,6 +1580,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final Bundle clientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE); mService.logAugmentedAutofillSelected(id, dataset.getId(), clientState); // For any content URIs, grant URI permissions to the target app before filling. if (content != null) { final AutofillUriGrantsManager autofillUgm = remoteAugmentedAutofillService.getAutofillUriGrantsManager(); autofillUgm.grantUriPermissions(mComponentName, userId, content); } // Fill the value into the field. if (sDebug) { Slog.d(TAG, "Filling after auth: fieldId=" + fieldId + ", value=" + value Loading Loading @@ -3987,6 +4007,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (remoteRenderService != null) { remoteRenderService.destroySuggestionViews(userId, id); } final RemoteAugmentedAutofillService remoteAugmentedAutofillService = mService.getRemoteAugmentedAutofillServiceIfCreatedLocked(); if (remoteAugmentedAutofillService != null) { final AutofillUriGrantsManager autofillUgm = remoteAugmentedAutofillService.getAutofillUriGrantsManager(); autofillUgm.revokeUriPermissions(mComponentName, userId); } mDestroyed = true; Loading Loading
core/java/android/service/autofill/Dataset.java +8 −0 Original line number Diff line number Diff line Loading @@ -406,6 +406,8 @@ public final class Dataset implements Parcelable { * authentication. * * @throws IllegalStateException if {@link #build()} was already called. * @throws IllegalArgumentException if the provided content * {@link ClipData.Item#getIntent() contains an intent} * * @return this builder. * Loading @@ -416,6 +418,12 @@ public final class Dataset implements Parcelable { @SuppressLint("MissingGetterMatchingBuilder") public @NonNull Builder setContent(@NonNull AutofillId id, @Nullable ClipData content) { throwIfDestroyed(); if (content != null) { for (int i = 0; i < content.getItemCount(); i++) { Preconditions.checkArgument(content.getItemAt(i).getIntent() == null, "Content items cannot contain an Intent: content=" + content); } } setLifeTheUniverseAndEverything(id, null, null, null, null); mFieldContent = content; return this; Loading
services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +8 −1 Original line number Diff line number Diff line Loading @@ -1246,8 +1246,10 @@ final class AutofillManagerServiceImpl mRemoteAugmentedAutofillService = null; } }; final int serviceUid = mRemoteAugmentedAutofillServiceInfo.applicationInfo.uid; mRemoteAugmentedAutofillService = new RemoteAugmentedAutofillService(getContext(), componentName, mUserId, callbacks, mMaster.isInstantServiceAllowed(), serviceUid, componentName, mUserId, callbacks, mMaster.isInstantServiceAllowed(), mMaster.verbose, mMaster.mAugmentedServiceIdleUnbindTimeoutMs, mMaster.mAugmentedServiceRequestTimeoutMs); } Loading @@ -1255,6 +1257,11 @@ final class AutofillManagerServiceImpl return mRemoteAugmentedAutofillService; } @GuardedBy("mLock") @Nullable RemoteAugmentedAutofillService getRemoteAugmentedAutofillServiceIfCreatedLocked() { return mRemoteAugmentedAutofillService; } /** * Called when the {@link AutofillManagerService#mAugmentedAutofillResolver} * changed (among other places). Loading
services/autofill/java/com/android/server/autofill/AutofillUriGrantsManager.java 0 → 100644 +257 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.autofill; import static android.content.ContentResolver.SCHEME_CONTENT; import static com.android.server.autofill.Helper.sVerbose; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.IUriGrantsManager; import android.app.UriGrantsManager; import android.content.ClipData; import android.content.ComponentName; import android.content.ContentProvider; import android.content.Intent; import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; import com.android.server.uri.UriGrantsManagerInternal; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; /** * Grants and revokes URI permissions for content-based autofill suggestions. * * <p>Note that the system cannot just hand out grants directly; it must always do so on behalf of * an owner (see {@link com.android.server.uri.UriGrantsManagerService}). For autofill, the owner * is the autofill service provider that creates a given autofill suggestion containing a content * URI. Therefore, this manager class must be instantiated with the service uid of the provider for * which it will manage URI grants. * * <p>To dump the state of this class, use {@code adb shell dumpsys autofill}. * * <p>To dump all active URI permissions, use {@code adb shell dumpsys activity permissions}. */ final class AutofillUriGrantsManager { private static final String TAG = AutofillUriGrantsManager.class.getSimpleName(); private final int mSourceUid; @UserIdInt private final int mSourceUserId; @NonNull private final IBinder mPermissionOwner; @NonNull private final UriGrantsManagerInternal mUgmInternal; @NonNull private final IUriGrantsManager mUgm; // We use a local lock here for simplicity, since the synchronized code does not depend on // any other resources (the "hold and wait" condition required for deadlock is not present). // If this changes in the future, instead of using a local lock this should be updated to // use the shared lock from AutofillManagerServiceImpl. @NonNull private final Object mLock; // Tracks the URIs that have been granted to each package. For each URI, the map stores the // activities that triggered the grant. This allows revoking permissions only once all // activities that triggered the grant are finished. @NonNull @GuardedBy("mLock") private final ArrayMap<String, List<Pair<Uri, String>>> mActiveGrantsByPackage; /** * Creates a new instance of the manager. * * @param serviceUid The uid of the autofill service provider for which this manager is being * created. URI grants will be requested on behalf of this uid (ie, this uid will be passed as * the {@code fromUid} to {@link IUriGrantsManager#grantUriPermissionFromOwner}). */ AutofillUriGrantsManager(int serviceUid) { mSourceUid = serviceUid; mSourceUserId = UserHandle.getUserId(mSourceUid); mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class); mPermissionOwner = mUgmInternal.newUriPermissionOwner("autofill-" + serviceUid); mUgm = UriGrantsManager.getService(); mLock = new Object(); mActiveGrantsByPackage = new ArrayMap<>(0); } public void grantUriPermissions(@NonNull ComponentName targetActivity, @UserIdInt int targetUserId, @NonNull ClipData clip) { String targetPkg = targetActivity.getPackageName(); for (int i = 0; i < clip.getItemCount(); i++) { ClipData.Item item = clip.getItemAt(i); Uri uri = item.getUri(); if (uri == null || !SCHEME_CONTENT.equals(uri.getScheme())) { continue; } if (grantUriPermissions(targetPkg, targetUserId, uri)) { addToActiveGrants(uri, targetActivity); } } } public void revokeUriPermissions(@NonNull ComponentName targetActivity, @UserIdInt int targetUserId) { String targetPkg = targetActivity.getPackageName(); Set<Uri> urisWhoseGrantsShouldBeRevoked = removeFromActiveGrants(targetActivity); for (Uri uri : urisWhoseGrantsShouldBeRevoked) { revokeUriPermissions(targetPkg, targetUserId, uri); } } private boolean grantUriPermissions(@NonNull String targetPkg, @UserIdInt int targetUserId, @NonNull Uri uri) { final int sourceUserId = ContentProvider.getUserIdFromUri(uri, mSourceUserId); if (sVerbose) { Slog.v(TAG, "Granting URI permissions: uri=" + uri + ", sourceUid=" + mSourceUid + ", sourceUserId=" + sourceUserId + ", targetPkg=" + targetPkg + ", targetUserId=" + targetUserId); } final Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(uri); final long ident = Binder.clearCallingIdentity(); try { mUgm.grantUriPermissionFromOwner( mPermissionOwner, mSourceUid, targetPkg, uriWithoutUserId, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, targetUserId); return true; } catch (RemoteException e) { Slog.e(TAG, "Granting URI permissions failed: uri=" + uri + ", sourceUid=" + mSourceUid + ", sourceUserId=" + sourceUserId + ", targetPkg=" + targetPkg + ", targetUserId=" + targetUserId, e); return false; } finally { Binder.restoreCallingIdentity(ident); } } private void revokeUriPermissions(@NonNull String targetPkg, @UserIdInt int targetUserId, @NonNull Uri uri) { final int sourceUserId = ContentProvider.getUserIdFromUri(uri, mSourceUserId); if (sVerbose) { Slog.v(TAG, "Revoking URI permissions: uri=" + uri + ", sourceUid=" + mSourceUid + ", sourceUserId=" + sourceUserId + ", target=" + targetPkg + ", targetUserId=" + targetUserId); } final Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(uri); final long ident = Binder.clearCallingIdentity(); try { mUgmInternal.revokeUriPermissionFromOwner( mPermissionOwner, uriWithoutUserId, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, targetPkg, targetUserId); } finally { Binder.restoreCallingIdentity(ident); } } private void addToActiveGrants(@NonNull Uri uri, @NonNull ComponentName targetActivity) { synchronized (mLock) { String packageName = targetActivity.getPackageName(); List<Pair<Uri, String>> uris = mActiveGrantsByPackage.computeIfAbsent(packageName, k -> new ArrayList<>(1)); uris.add(Pair.create(uri, targetActivity.getClassName())); } } private Set<Uri> removeFromActiveGrants(@NonNull ComponentName targetActivity) { synchronized (mLock) { String targetPackageName = targetActivity.getPackageName(); List<Pair<Uri, String>> uris = mActiveGrantsByPackage.get(targetPackageName); if (uris == null || uris.isEmpty()) { return Collections.emptySet(); } // Collect all URIs whose grant was triggered by the target activity. String targetActivityClassName = targetActivity.getClassName(); Set<Uri> urisWhoseGrantsShouldBeRevoked = new ArraySet<>(1); for (Iterator<Pair<Uri, String>> iter = uris.iterator(); iter.hasNext(); ) { Pair<Uri, String> uriAndActivity = iter.next(); if (uriAndActivity.second.equals(targetActivityClassName)) { urisWhoseGrantsShouldBeRevoked.add(uriAndActivity.first); iter.remove(); } } // A URI grant may have been triggered by more than one activity for the same package. // We should not revoke a grant if it was triggered by multiple activities and one or // more of those activities is still alive. Therefore we do a second pass and prune // the set of URIs to be revoked if an additional activity that triggered its grant // is still present. for (Pair<Uri, String> uriAndActivity : uris) { urisWhoseGrantsShouldBeRevoked.remove(uriAndActivity.first); } // If there are no remaining URIs granted to the package, drop the entry from the map. if (uris.isEmpty()) { mActiveGrantsByPackage.remove(targetPackageName); } return urisWhoseGrantsShouldBeRevoked; } } /** * Dump the active URI grants. */ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) { synchronized (mLock) { if (mActiveGrantsByPackage.isEmpty()) { pw.print(prefix); pw.println("URI grants: none"); return; } pw.print(prefix); pw.println("URI grants:"); final String prefix2 = prefix + " "; final String prefix3 = prefix2 + " "; for (int i = mActiveGrantsByPackage.size() - 1; i >= 0; i--) { String packageName = mActiveGrantsByPackage.keyAt(i); pw.print(prefix2); pw.println(packageName); List<Pair<Uri, String>> uris = mActiveGrantsByPackage.valueAt(i); if (uris == null || uris.isEmpty()) { continue; } for (Pair<Uri, String> uriAndActivity : uris) { pw.print(prefix3); pw.println(uriAndActivity.first + ": " + uriAndActivity.second); } } } } }
services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +20 −4 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.IResultReceiver; import com.android.server.autofill.ui.InlineFillUi; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CancellationException; Loading @@ -74,8 +75,9 @@ final class RemoteAugmentedAutofillService private final int mRequestTimeoutMs; private final ComponentName mComponentName; private final RemoteAugmentedAutofillServiceCallbacks mCallbacks; private final AutofillUriGrantsManager mUriGrantsManager; RemoteAugmentedAutofillService(Context context, ComponentName serviceName, RemoteAugmentedAutofillService(Context context, int serviceUid, ComponentName serviceName, int userId, RemoteAugmentedAutofillServiceCallbacks callbacks, boolean bindInstantServiceAllowed, boolean verbose, int idleUnbindTimeoutMs, int requestTimeoutMs) { Loading @@ -87,6 +89,7 @@ final class RemoteAugmentedAutofillService mRequestTimeoutMs = requestTimeoutMs; mComponentName = serviceName; mCallbacks = callbacks; mUriGrantsManager = new AutofillUriGrantsManager(serviceUid); // Bind right away. connect(); Loading Loading @@ -121,6 +124,10 @@ final class RemoteAugmentedAutofillService return mComponentName; } public AutofillUriGrantsManager getAutofillUriGrantsManager() { return mUriGrantsManager; } @Override // from ServiceConnector.Impl protected void onServiceConnectionStatusChanged( IAugmentedAutofillService service, boolean connected) { Loading Loading @@ -173,8 +180,8 @@ final class RemoteAugmentedAutofillService maybeRequestShowInlineSuggestions(sessionId, inlineSuggestionsRequest, inlineSuggestionsData, clientState, focusedId, focusedValue, inlineSuggestionsCallback, client, onErrorCallback, remoteRenderService, userId); inlineSuggestionsCallback, client, onErrorCallback, remoteRenderService, userId, activityComponent); if (!showingFillWindow) { requestAutofill.complete(null); } Loading Loading @@ -245,7 +252,8 @@ final class RemoteAugmentedAutofillService @Nullable Function<InlineFillUi, Boolean> inlineSuggestionsCallback, @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService, int userId) { int userId, @NonNull ComponentName targetActivity) { if (inlineSuggestionsData == null || inlineSuggestionsData.isEmpty() || inlineSuggestionsCallback == null || request == null || remoteRenderService == null) { Loading Loading @@ -299,6 +307,8 @@ final class RemoteAugmentedAutofillService final ArrayList<AutofillId> fieldIds = dataset.getFieldIds(); final ClipData content = dataset.getFieldContent(); if (content != null) { mUriGrantsManager.grantUriPermissions( targetActivity, userId, content); final AutofillId fieldId = fieldIds.get(0); if (sDebug) { Slog.d(TAG, "Calling client autofillContent(): " Loading Loading @@ -358,6 +368,12 @@ final class RemoteAugmentedAutofillService + ComponentName.flattenToShortString(mComponentName) + "]"; } @Override public void dump(@NonNull String prefix, @NonNull PrintWriter pw) { super.dump(prefix, pw); mUriGrantsManager.dump(prefix, pw); } /** * Called by {@link Session} when it's time to destroy all augmented autofill requests. */ Loading
services/autofill/java/com/android/server/autofill/Session.java +27 −0 Original line number Diff line number Diff line Loading @@ -1555,6 +1555,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } // Get a handle to the RemoteAugmentedAutofillService. In // AutofillManagerServiceImpl.updateRemoteAugmentedAutofillService() we invalidate sessions // whenever the service changes, so there should never be a case when we get here and the // remote service instance is not present or different. final RemoteAugmentedAutofillService remoteAugmentedAutofillService = mService.getRemoteAugmentedAutofillServiceIfCreatedLocked(); if (remoteAugmentedAutofillService == null) { Slog.e(TAG, "Can't fill after auth: RemoteAugmentedAutofillService is null"); mService.resetLastAugmentedAutofillResponse(); removeFromServiceLocked(); return; } // Update state to ensure that after filling the field here we don't end up firing another // autofill request that will end up showing the same suggestions to the user again. When // the auth activity came up, the field for which the suggestions were shown lost focus and Loading @@ -1567,6 +1580,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final Bundle clientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE); mService.logAugmentedAutofillSelected(id, dataset.getId(), clientState); // For any content URIs, grant URI permissions to the target app before filling. if (content != null) { final AutofillUriGrantsManager autofillUgm = remoteAugmentedAutofillService.getAutofillUriGrantsManager(); autofillUgm.grantUriPermissions(mComponentName, userId, content); } // Fill the value into the field. if (sDebug) { Slog.d(TAG, "Filling after auth: fieldId=" + fieldId + ", value=" + value Loading Loading @@ -3987,6 +4007,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (remoteRenderService != null) { remoteRenderService.destroySuggestionViews(userId, id); } final RemoteAugmentedAutofillService remoteAugmentedAutofillService = mService.getRemoteAugmentedAutofillServiceIfCreatedLocked(); if (remoteAugmentedAutofillService != null) { final AutofillUriGrantsManager autofillUgm = remoteAugmentedAutofillService.getAutofillUriGrantsManager(); autofillUgm.revokeUriPermissions(mComponentName, userId); } mDestroyed = true; Loading