1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server; 18 19 import android.content.ClipData; 20 import android.content.ClipDescription; 21 import android.content.Context; 22 import android.content.IClipboard; 23 import android.content.IOnPrimaryClipChangedListener; 24 import android.os.PermissionEnforcer; 25 import android.os.RemoteCallbackList; 26 import android.os.RemoteException; 27 import android.os.UserHandle; 28 29 import com.android.internal.util.Preconditions; 30 31 /** 32 * Fake implementation of {@code ClipboardManager} since the real implementation is tightly 33 * coupled with many other internal services. 34 */ 35 public class FakeClipboardService extends IClipboard.Stub { 36 private final RemoteCallbackList<IOnPrimaryClipChangedListener> mListeners = 37 new RemoteCallbackList<>(); 38 39 private ClipData mPrimaryClip; 40 private String mPrimaryClipSource; 41 FakeClipboardService(Context context)42 public FakeClipboardService(Context context) { 43 super(PermissionEnforcer.fromContext(context)); 44 } 45 46 public static class Lifecycle extends SystemService { 47 private FakeClipboardService mService; 48 Lifecycle(Context context)49 public Lifecycle(Context context) { 50 super(context); 51 } 52 53 @Override onStart()54 public void onStart() { 55 mService = new FakeClipboardService(getContext()); 56 publishBinderService(Context.CLIPBOARD_SERVICE, mService); 57 } 58 } 59 checkArguments(int userId, int deviceId)60 private static void checkArguments(int userId, int deviceId) { 61 Preconditions.checkArgument(userId == UserHandle.USER_SYSTEM, 62 "Fake only supports USER_SYSTEM user"); 63 Preconditions.checkArgument(deviceId == Context.DEVICE_ID_DEFAULT, 64 "Fake only supports DEVICE_ID_DEFAULT device"); 65 } 66 dispatchPrimaryClipChanged()67 private void dispatchPrimaryClipChanged() { 68 mListeners.broadcast((listener) -> { 69 try { 70 listener.dispatchPrimaryClipChanged(); 71 } catch (RemoteException ignored) { 72 } 73 }); 74 } 75 76 @Override setPrimaryClip(ClipData clip, String callingPackage, String attributionTag, int userId, int deviceId)77 public void setPrimaryClip(ClipData clip, String callingPackage, String attributionTag, 78 int userId, int deviceId) { 79 checkArguments(userId, deviceId); 80 mPrimaryClip = clip; 81 mPrimaryClipSource = callingPackage; 82 dispatchPrimaryClipChanged(); 83 } 84 85 @Override 86 @android.annotation.EnforcePermission(android.Manifest.permission.SET_CLIP_SOURCE) setPrimaryClipAsPackage(ClipData clip, String callingPackage, String attributionTag, int userId, int deviceId, String sourcePackage)87 public void setPrimaryClipAsPackage(ClipData clip, String callingPackage, String attributionTag, 88 int userId, int deviceId, String sourcePackage) { 89 setPrimaryClipAsPackage_enforcePermission(); 90 checkArguments(userId, deviceId); 91 mPrimaryClip = clip; 92 mPrimaryClipSource = sourcePackage; 93 dispatchPrimaryClipChanged(); 94 } 95 96 @Override clearPrimaryClip(String callingPackage, String attributionTag, int userId, int deviceId)97 public void clearPrimaryClip(String callingPackage, String attributionTag, int userId, 98 int deviceId) { 99 checkArguments(userId, deviceId); 100 mPrimaryClip = null; 101 mPrimaryClipSource = null; 102 dispatchPrimaryClipChanged(); 103 } 104 105 @Override getPrimaryClip(String pkg, String attributionTag, int userId, int deviceId)106 public ClipData getPrimaryClip(String pkg, String attributionTag, int userId, int deviceId) { 107 checkArguments(userId, deviceId); 108 return mPrimaryClip; 109 } 110 111 @Override getPrimaryClipDescription(String callingPackage, String attributionTag, int userId, int deviceId)112 public ClipDescription getPrimaryClipDescription(String callingPackage, String attributionTag, 113 int userId, int deviceId) { 114 checkArguments(userId, deviceId); 115 return (mPrimaryClip != null) ? mPrimaryClip.getDescription() : null; 116 } 117 118 @Override hasPrimaryClip(String callingPackage, String attributionTag, int userId, int deviceId)119 public boolean hasPrimaryClip(String callingPackage, String attributionTag, int userId, 120 int deviceId) { 121 checkArguments(userId, deviceId); 122 return mPrimaryClip != null; 123 } 124 125 @Override addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener, String callingPackage, String attributionTag, int userId, int deviceId)126 public void addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener, 127 String callingPackage, String attributionTag, int userId, int deviceId) { 128 checkArguments(userId, deviceId); 129 mListeners.register(listener); 130 } 131 132 @Override removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener, String callingPackage, String attributionTag, int userId, int deviceId)133 public void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener, 134 String callingPackage, String attributionTag, int userId, int deviceId) { 135 checkArguments(userId, deviceId); 136 mListeners.unregister(listener); 137 } 138 139 @Override hasClipboardText(String callingPackage, String attributionTag, int userId, int deviceId)140 public boolean hasClipboardText(String callingPackage, String attributionTag, int userId, 141 int deviceId) { 142 checkArguments(userId, deviceId); 143 return (mPrimaryClip != null) && (mPrimaryClip.getItemCount() > 0) 144 && (mPrimaryClip.getItemAt(0).getText() != null); 145 } 146 147 @Override 148 @android.annotation.EnforcePermission(android.Manifest.permission.SET_CLIP_SOURCE) getPrimaryClipSource(String callingPackage, String attributionTag, int userId, int deviceId)149 public String getPrimaryClipSource(String callingPackage, String attributionTag, int userId, 150 int deviceId) { 151 getPrimaryClipSource_enforcePermission(); 152 checkArguments(userId, deviceId); 153 return mPrimaryClipSource; 154 } 155 156 @Override areClipboardAccessNotificationsEnabledForUser(int userId)157 public boolean areClipboardAccessNotificationsEnabledForUser(int userId) { 158 throw new UnsupportedOperationException(); 159 } 160 161 @Override setClipboardAccessNotificationsEnabledForUser(boolean enable, int userId)162 public void setClipboardAccessNotificationsEnabledForUser(boolean enable, int userId) { 163 throw new UnsupportedOperationException(); 164 } 165 } 166