diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 093ecd57124f6..e120343335544 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -50,6 +50,8 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.IUserManager; +import android.os.Looper; +import android.os.Message; import android.os.Parcel; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -74,6 +76,8 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; @@ -99,6 +103,22 @@ public class ClipboardService extends SystemService { private static final boolean IS_EMULATOR = SystemProperties.getBoolean("ro.boot.qemu", false); + @VisibleForTesting + public static final long DEFAULT_CLIPBOARD_TIMEOUT_MILLIS = 3600000; + + /** + * Device config property for whether clipboard auto clear is enabled on the device + **/ + public static final String PROPERTY_AUTO_CLEAR_ENABLED = + "auto_clear_enabled"; + + /** + * Device config property for time period in milliseconds after which clipboard is auto + * cleared + **/ + public static final String PROPERTY_AUTO_CLEAR_TIMEOUT = + "auto_clear_timeout"; + // DeviceConfig properties private static final String PROPERTY_MAX_CLASSIFICATION_LENGTH = "max_classification_length"; private static final int DEFAULT_MAX_CLASSIFICATION_LENGTH = 400; @@ -312,6 +332,10 @@ public class ClipboardService extends SystemService { * 'intendingUserId' and the uid is called 'intendingUid'. */ private class ClipboardImpl extends IClipboard.Stub { + + private final Handler mClipboardClearHandler = new ClipboardClearHandler( + mWorkerHandler.getLooper()); + @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { @@ -352,10 +376,34 @@ public class ClipboardService extends SystemService { } checkDataOwner(clip, intendingUid); synchronized (mLock) { + scheduleAutoClear(userId); setPrimaryClipInternalLocked(clip, intendingUid, sourcePackage); } } + private void scheduleAutoClear(@UserIdInt int userId) { + final long oldIdentity = Binder.clearCallingIdentity(); + try { + if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD, + PROPERTY_AUTO_CLEAR_ENABLED, false)) { + mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR, + userId); + Message clearMessage = Message.obtain(mClipboardClearHandler, + ClipboardClearHandler.MSG_CLEAR, userId, 0, userId); + mClipboardClearHandler.sendMessageDelayed(clearMessage, + getTimeoutForAutoClear()); + } + } finally { + Binder.restoreCallingIdentity(oldIdentity); + } + } + + private long getTimeoutForAutoClear() { + return DeviceConfig.getLong(DeviceConfig.NAMESPACE_CLIPBOARD, + PROPERTY_AUTO_CLEAR_TIMEOUT, + DEFAULT_CLIPBOARD_TIMEOUT_MILLIS); + } + @Override public void clearPrimaryClip(String callingPackage, @UserIdInt int userId) { final int intendingUid = getIntendingUid(callingPackage, userId); @@ -365,6 +413,8 @@ public class ClipboardService extends SystemService { return; } synchronized (mLock) { + mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR, + userId); setPrimaryClipInternalLocked(null, intendingUid, callingPackage); } } @@ -391,6 +441,9 @@ public class ClipboardService extends SystemService { PerUserClipboard clipboard = getClipboardLocked(intendingUserId); showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard); notifyTextClassifierLocked(clipboard, pkg, intendingUid); + if (clipboard.primaryClip != null) { + scheduleAutoClear(userId); + } return clipboard.primaryClip; } } @@ -484,6 +537,32 @@ public class ClipboardService extends SystemService { return null; } } + + private class ClipboardClearHandler extends Handler { + + public static final int MSG_CLEAR = 101; + + ClipboardClearHandler(Looper looper) { + super(looper); + } + + public void handleMessage(@NonNull Message msg) { + switch (msg.what) { + case MSG_CLEAR: + final int userId = msg.arg1; + synchronized (mLock) { + if (getClipboardLocked(userId).primaryClip != null) { + FrameworkStatsLog.write(FrameworkStatsLog.CLIPBOARD_CLEARED, + FrameworkStatsLog.CLIPBOARD_CLEARED__SOURCE__AUTO_CLEAR); + setPrimaryClipInternalLocked(null, Binder.getCallingUid(), null); + } + } + break; + default: + Slog.wtf(TAG, "ClipboardClearHandler received unknown message " + msg.what); + } + } + } }; @GuardedBy("mLock")