Merge "Propagate ContentCaptureManager allowlist update to activities" into sc-dev am: d77366e4f2

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13634645

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Icb217df0361b3751df45d11c0fc13ed347a8e3dc
This commit is contained in:
Alex Mang
2021-03-04 02:40:44 +00:00
committed by Automerger Merge Worker
7 changed files with 220 additions and 2 deletions

View File

@@ -176,6 +176,8 @@ import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.autofill.AutofillId;
import android.view.contentcapture.IContentCaptureManager;
import android.view.contentcapture.IContentCaptureOptionsCallback;
import android.view.translation.TranslationSpec;
import android.webkit.WebView;
import android.window.SplashScreen;
@@ -512,6 +514,8 @@ public final class ActivityThread extends ClientTransactionHandler {
boolean mHasImeComponent = false;
private IContentCaptureOptionsCallback.Stub mContentCaptureOptionsCallback = null;
/** Activity client record, used for bookkeeping for the real {@link Activity} instance. */
public static final class ActivityClientRecord {
@UnsupportedAppUsage
@@ -1939,6 +1943,7 @@ public final class ActivityThread extends ClientTransactionHandler {
public static final int PURGE_RESOURCES = 161;
public static final int ATTACH_STARTUP_AGENTS = 162;
public static final int UPDATE_UI_TRANSLATION_STATE = 163;
public static final int SET_CONTENT_CAPTURE_OPTIONS_CALLBACK = 164;
public static final int INSTRUMENT_WITHOUT_RESTART = 170;
public static final int FINISH_INSTRUMENTATION_WITHOUT_RESTART = 171;
@@ -1988,6 +1993,8 @@ public final class ActivityThread extends ClientTransactionHandler {
case PURGE_RESOURCES: return "PURGE_RESOURCES";
case ATTACH_STARTUP_AGENTS: return "ATTACH_STARTUP_AGENTS";
case UPDATE_UI_TRANSLATION_STATE: return "UPDATE_UI_TRANSLATION_STATE";
case SET_CONTENT_CAPTURE_OPTIONS_CALLBACK:
return "SET_CONTENT_CAPTURE_OPTIONS_CALLBACK";
case INSTRUMENT_WITHOUT_RESTART: return "INSTRUMENT_WITHOUT_RESTART";
case FINISH_INSTRUMENTATION_WITHOUT_RESTART:
return "FINISH_INSTRUMENTATION_WITHOUT_RESTART";
@@ -2180,6 +2187,9 @@ public final class ActivityThread extends ClientTransactionHandler {
(TranslationSpec) args.arg3, (TranslationSpec) args.arg4,
(List<AutofillId>) args.arg5);
break;
case SET_CONTENT_CAPTURE_OPTIONS_CALLBACK:
handleSetContentCaptureOptionsCallback((String) msg.obj);
break;
case INSTRUMENT_WITHOUT_RESTART:
handleInstrumentWithoutRestart((AppBindData) msg.obj);
break;
@@ -6795,6 +6805,7 @@ public final class ActivityThread extends ClientTransactionHandler {
// Propagate Content Capture options
app.setContentCaptureOptions(data.contentCaptureOptions);
sendMessage(H.SET_CONTENT_CAPTURE_OPTIONS_CALLBACK, data.appInfo.packageName);
mInitialApplication = app;
@@ -6856,6 +6867,36 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
private void handleSetContentCaptureOptionsCallback(String packageName) {
if (mContentCaptureOptionsCallback != null) {
return;
}
IBinder b = ServiceManager.getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
if (b == null) {
return;
}
IContentCaptureManager service = IContentCaptureManager.Stub.asInterface(b);
mContentCaptureOptionsCallback = new IContentCaptureOptionsCallback.Stub() {
@Override
public void setContentCaptureOptions(ContentCaptureOptions options)
throws RemoteException {
if (mInitialApplication != null) {
mInitialApplication.setContentCaptureOptions(options);
}
}
};
try {
service.registerContentCaptureOptionsCallback(packageName,
mContentCaptureOptionsCallback);
} catch (RemoteException e) {
Slog.w(TAG, "registerContentCaptureOptionsCallback() failed: "
+ packageName, e);
mContentCaptureOptionsCallback = null;
}
}
private void handleInstrumentWithoutRestart(AppBindData data) {
try {
data.compatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;

View File

@@ -22,6 +22,7 @@ import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.DataRemovalRequest;
import android.view.contentcapture.DataShareRequest;
import android.view.contentcapture.IDataShareWriteAdapter;
import android.view.contentcapture.IContentCaptureOptionsCallback;
import android.os.IBinder;
import android.os.ICancellationSignal;
@@ -101,4 +102,10 @@ oneway interface IContentCaptureManager {
* Sets whether the default service should be used.
*/
void setDefaultServiceEnabled(int userId, boolean enabled);
/**
* Registers a listener to handle updates ContentCaptureOptions from server.
*/
void registerContentCaptureOptionsCallback(String packageName,
in IContentCaptureOptionsCallback callback);
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2021, 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 android.view.contentcapture;
import android.content.ContentCaptureOptions;
/**
* Callback for changes to content capture options made by ContentCaptureService.
* Callback interface used by IContentCaptureManager to send asynchronous
* notifications back to its clients. Note that this is a
* one-way interface so the server does not block waiting for the client.
*
* @hide
*/
oneway interface IContentCaptureOptionsCallback {
void setContentCaptureOptions(in ContentCaptureOptions options);
}

View File

@@ -98,6 +98,18 @@ public class GlobalWhitelistState {
}
}
/**
* Gets packages that are either entirely allowlisted or have components that are allowlisted
* for the given user.
*/
public ArraySet<String> getWhitelistedPackages(@UserIdInt int userId) {
synchronized (mGlobalWhitelistStateLock) {
if (mWhitelisterHelpers == null) return null;
final WhitelistHelper helper = mWhitelisterHelpers.get(userId);
return helper == null ? null : helper.getWhitelistedPackages();
}
}
/**
* Resets the allowlist for the given user.
*/

View File

@@ -140,6 +140,15 @@ public final class WhitelistHelper {
return mWhitelistedPackages == null ? null : mWhitelistedPackages.get(packageName);
}
/**
* Returns a set of all packages that are either entirely allowlisted or have components that
* are allowlisted.
*/
@Nullable
public ArraySet<String> getWhitelistedPackages() {
return mWhitelistedPackages == null ? null : new ArraySet<>(mWhitelistedPackages.keySet());
}
@Override
public String toString() {
return "WhitelistHelper[" + mWhitelistedPackages + ']';

View File

@@ -69,6 +69,7 @@ import android.provider.Settings;
import android.service.contentcapture.ActivityEvent.ActivityEventType;
import android.service.contentcapture.IDataShareCallback;
import android.service.contentcapture.IDataShareReadAdapter;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Pair;
@@ -81,6 +82,7 @@ import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.DataRemovalRequest;
import android.view.contentcapture.DataShareRequest;
import android.view.contentcapture.IContentCaptureManager;
import android.view.contentcapture.IContentCaptureOptionsCallback;
import android.view.contentcapture.IDataShareWriteAdapter;
import com.android.internal.annotations.GuardedBy;
@@ -134,6 +136,9 @@ public final class ContentCaptureManagerService extends
private final LocalService mLocalService = new LocalService();
private final ContentCaptureManagerServiceStub mContentCaptureManagerServiceStub =
new ContentCaptureManagerServiceStub();
@Nullable
final LocalLog mRequestsHistory;
@@ -224,8 +229,7 @@ public final class ContentCaptureManagerService extends
@Override // from SystemService
public void onStart() {
publishBinderService(CONTENT_CAPTURE_MANAGER_SERVICE,
new ContentCaptureManagerServiceStub());
publishBinderService(CONTENT_CAPTURE_MANAGER_SERVICE, mContentCaptureManagerServiceStub);
publishLocalService(ContentCaptureManagerInternal.class, mLocalService);
}
@@ -492,6 +496,19 @@ public final class ContentCaptureManagerService extends
}
}
void updateOptions(String packageName, ContentCaptureOptions options) {
ArraySet<CallbackRecord> records;
synchronized (mLock) {
records = mContentCaptureManagerServiceStub.mCallbacks.get(packageName);
if (records != null) {
int N = records.size();
for (int i = 0; i < N; i++) {
records.valueAt(i).setContentCaptureOptions(options);
}
}
}
}
private ActivityManagerInternal getAmInternal() {
synchronized (mLock) {
if (mAm == null) {
@@ -599,6 +616,8 @@ public final class ContentCaptureManagerService extends
}
final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub {
@GuardedBy("mLock")
private final ArrayMap<String, ArraySet<CallbackRecord>> mCallbacks = new ArrayMap<>();
@Override
public void startSession(@NonNull IBinder activityToken,
@@ -754,6 +773,46 @@ public final class ContentCaptureManagerService extends
}
}
@Override
public void registerContentCaptureOptionsCallback(@NonNull String packageName,
IContentCaptureOptionsCallback callback) {
assertCalledByPackageOwner(packageName);
CallbackRecord record = new CallbackRecord(callback, packageName);
record.registerObserver();
synchronized (mLock) {
ArraySet<CallbackRecord> records = mCallbacks.get(packageName);
if (records == null) {
records = new ArraySet<>();
}
records.add(record);
mCallbacks.put(packageName, records);
}
// Set options here in case it was updated before this was registered.
final int userId = UserHandle.getCallingUserId();
final ContentCaptureOptions options = mGlobalContentCaptureOptions.getOptions(userId,
packageName);
if (options != null) {
record.setContentCaptureOptions(options);
}
}
private void unregisterContentCaptureOptionsCallback(CallbackRecord record) {
synchronized (mLock) {
ArraySet<CallbackRecord> records = mCallbacks.get(record.mPackageName);
if (records != null) {
records.remove(record);
}
if (records == null || records.isEmpty()) {
mCallbacks.remove(record.mPackageName);
}
}
record.unregisterObserver();
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
@@ -1218,4 +1277,39 @@ public final class ContentCaptureManagerService extends
mDataShareRequest.getPackageName());
}
}
private final class CallbackRecord implements IBinder.DeathRecipient {
private final String mPackageName;
private final IContentCaptureOptionsCallback mCallback;
private CallbackRecord(IContentCaptureOptionsCallback callback, String packageName) {
mCallback = callback;
mPackageName = packageName;
}
private void setContentCaptureOptions(ContentCaptureOptions options) {
try {
mCallback.setContentCaptureOptions(options);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to send setContentCaptureOptions(): " + e);
}
}
private void registerObserver() {
try {
mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to register callback cleanup " + e);
}
}
private void unregisterObserver() {
mCallback.asBinder().unlinkToDeath(this, 0);
}
@Override
public void binderDied() {
mContentCaptureManagerServiceStub.unregisterContentCaptureOptionsCallback(this);
}
}
}

View File

@@ -597,9 +597,15 @@ final class ContentCapturePerUserService
? "null_activities" : activities.size() + " activities") + ")"
+ " for user " + mUserId);
}
ArraySet<String> oldList =
mMaster.mGlobalContentCaptureOptions.getWhitelistedPackages(mUserId);
mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities);
writeSetWhitelistEvent(getServiceComponentName(), packages, activities);
updateContentCaptureOptions(oldList);
// Must disable session that are not the allowlist anymore...
final int numSessions = mSessions.size();
if (numSessions <= 0) return;
@@ -671,5 +677,23 @@ final class ContentCapturePerUserService
ContentCaptureMetricsLogger.writeSessionFlush(sessionId, getServiceComponentName(), app,
flushMetrics, options, flushReason);
}
/** Updates {@link ContentCaptureOptions} for all newly added packages on allowlist. */
private void updateContentCaptureOptions(@Nullable ArraySet<String> oldList) {
ArraySet<String> adding = mMaster.mGlobalContentCaptureOptions
.getWhitelistedPackages(mUserId);
if (oldList != null && adding != null) {
adding.removeAll(oldList);
}
int N = adding != null ? adding.size() : 0;
for (int i = 0; i < N; i++) {
String packageName = adding.valueAt(i);
ContentCaptureOptions options = mMaster.mGlobalContentCaptureOptions
.getOptions(mUserId, packageName);
mMaster.updateOptions(packageName, options);
}
}
}
}