1 /* 2 * Copyright (C) 2018 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 package android.content; 17 18 import android.annotation.NonNull; 19 import android.annotation.Nullable; 20 import android.annotation.SuppressLint; 21 import android.annotation.TestApi; 22 import android.app.ActivityThread; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.util.ArraySet; 26 import android.util.Log; 27 import android.view.contentcapture.ContentCaptureManager; 28 import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 32 import java.io.PrintWriter; 33 34 /** 35 * Content capture options for a given package. 36 * 37 * <p>This object is created by the Content Capture System Service and passed back to the app when 38 * the application is created. 39 * 40 * @hide 41 */ 42 @TestApi 43 public final class ContentCaptureOptions implements Parcelable { 44 45 private static final String TAG = ContentCaptureOptions.class.getSimpleName(); 46 47 /** 48 * Logging level for {@code logcat} statements. 49 */ 50 public final int loggingLevel; 51 52 /** 53 * Maximum number of events that are buffered before sent to the app. 54 */ 55 public final int maxBufferSize; 56 57 /** 58 * Frequency the buffer is flushed if idle. 59 */ 60 public final int idleFlushingFrequencyMs; 61 62 /** 63 * Frequency the buffer is flushed if last event is a text change. 64 */ 65 public final int textChangeFlushingFrequencyMs; 66 67 /** 68 * Size of events that are logging on {@code dump}. 69 */ 70 public final int logHistorySize; 71 72 /** 73 * Disable flush when receiving a VIEW_TREE_APPEARING event. 74 * @hide 75 */ 76 public final boolean disableFlushForViewTreeAppearing; 77 78 /** 79 * List of activities explicitly allowlisted for content capture (or {@code null} if allowlisted 80 * for all acitivites in the package). 81 */ 82 @Nullable 83 @SuppressLint("NullableCollection") 84 public final ArraySet<ComponentName> whitelistedComponents; 85 86 /** 87 * Used to enable just a small set of APIs so it can used by activities belonging to the 88 * content capture service APK. 89 */ 90 public final boolean lite; 91 92 /** 93 * Constructor for "lite" objects that are just used to enable a {@link ContentCaptureManager} 94 * for contexts belonging to the content capture service app. 95 */ ContentCaptureOptions(int loggingLevel)96 public ContentCaptureOptions(int loggingLevel) { 97 this(/* lite= */ true, loggingLevel, /* maxBufferSize= */ 0, 98 /* idleFlushingFrequencyMs= */ 0, /* textChangeFlushingFrequencyMs= */ 0, 99 /* logHistorySize= */ 0, /* disableFlushForViewTreeAppearing= */ false, 100 /* whitelistedComponents= */ null); 101 } 102 103 /** 104 * Default constructor. 105 */ ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable ArraySet<ComponentName> whitelistedComponents)106 public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, 107 int textChangeFlushingFrequencyMs, int logHistorySize, 108 @SuppressLint({"ConcreteCollection", "NullableCollection"}) 109 @Nullable ArraySet<ComponentName> whitelistedComponents) { 110 this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs, 111 textChangeFlushingFrequencyMs, logHistorySize, 112 ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, 113 whitelistedComponents); 114 } 115 116 /** @hide */ ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, boolean disableFlushForViewTreeAppearing, @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable ArraySet<ComponentName> whitelistedComponents)117 public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, 118 int textChangeFlushingFrequencyMs, int logHistorySize, 119 boolean disableFlushForViewTreeAppearing, 120 @SuppressLint({"ConcreteCollection", "NullableCollection"}) 121 @Nullable ArraySet<ComponentName> whitelistedComponents) { 122 this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs, 123 textChangeFlushingFrequencyMs, logHistorySize, disableFlushForViewTreeAppearing, 124 whitelistedComponents); 125 } 126 127 /** @hide */ 128 @VisibleForTesting ContentCaptureOptions(@ullable ArraySet<ComponentName> whitelistedComponents)129 public ContentCaptureOptions(@Nullable ArraySet<ComponentName> whitelistedComponents) { 130 this(ContentCaptureManager.LOGGING_LEVEL_VERBOSE, 131 ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE, 132 ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS, 133 ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS, 134 ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, 135 ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, 136 whitelistedComponents); 137 } 138 ContentCaptureOptions(boolean lite, int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, boolean disableFlushForViewTreeAppearing, @Nullable ArraySet<ComponentName> whitelistedComponents)139 private ContentCaptureOptions(boolean lite, int loggingLevel, int maxBufferSize, 140 int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, 141 boolean disableFlushForViewTreeAppearing, 142 @Nullable ArraySet<ComponentName> whitelistedComponents) { 143 this.lite = lite; 144 this.loggingLevel = loggingLevel; 145 this.maxBufferSize = maxBufferSize; 146 this.idleFlushingFrequencyMs = idleFlushingFrequencyMs; 147 this.textChangeFlushingFrequencyMs = textChangeFlushingFrequencyMs; 148 this.logHistorySize = logHistorySize; 149 this.disableFlushForViewTreeAppearing = disableFlushForViewTreeAppearing; 150 this.whitelistedComponents = whitelistedComponents; 151 } 152 forWhitelistingItself()153 public static ContentCaptureOptions forWhitelistingItself() { 154 final ActivityThread at = ActivityThread.currentActivityThread(); 155 if (at == null) { 156 throw new IllegalStateException("No ActivityThread"); 157 } 158 159 final String packageName = at.getApplication().getPackageName(); 160 161 if (!"android.contentcaptureservice.cts".equals(packageName) 162 && !"android.translation.cts".equals(packageName)) { 163 Log.e(TAG, "forWhitelistingItself(): called by " + packageName); 164 throw new SecurityException("Thou shall not pass!"); 165 } 166 167 final ContentCaptureOptions options = 168 new ContentCaptureOptions(/* whitelistedComponents= */ null); 169 // Always log, as it's used by test only 170 Log.i(TAG, "forWhitelistingItself(" + packageName + "): " + options); 171 172 return options; 173 } 174 175 /** @hide */ 176 @VisibleForTesting isWhitelisted(@onNull Context context)177 public boolean isWhitelisted(@NonNull Context context) { 178 if (whitelistedComponents == null) return true; // whole package is allowlisted 179 final ContentCaptureClient client = context.getContentCaptureClient(); 180 if (client == null) { 181 // Shouldn't happen, but it doesn't hurt to check... 182 Log.w(TAG, "isWhitelisted(): no ContentCaptureClient on " + context); 183 return false; 184 } 185 return whitelistedComponents.contains(client.contentCaptureClientGetComponentName()); 186 } 187 188 @Override toString()189 public String toString() { 190 if (lite) { 191 return "ContentCaptureOptions [loggingLevel=" + loggingLevel + " (lite)]"; 192 } 193 final StringBuilder string = new StringBuilder("ContentCaptureOptions ["); 194 string.append("loggingLevel=").append(loggingLevel) 195 .append(", maxBufferSize=").append(maxBufferSize) 196 .append(", idleFlushingFrequencyMs=").append(idleFlushingFrequencyMs) 197 .append(", textChangeFlushingFrequencyMs=").append(textChangeFlushingFrequencyMs) 198 .append(", logHistorySize=").append(logHistorySize) 199 .append(", disableFlushForViewTreeAppearing=").append(disableFlushForViewTreeAppearing); 200 if (whitelistedComponents != null) { 201 string.append(", whitelisted=").append(whitelistedComponents); 202 } 203 return string.append(']').toString(); 204 } 205 206 /** @hide */ dumpShort(@onNull PrintWriter pw)207 public void dumpShort(@NonNull PrintWriter pw) { 208 pw.print("logLvl="); pw.print(loggingLevel); 209 if (lite) { 210 pw.print(", lite"); 211 return; 212 } 213 pw.print(", bufferSize="); pw.print(maxBufferSize); 214 pw.print(", idle="); pw.print(idleFlushingFrequencyMs); 215 pw.print(", textIdle="); pw.print(textChangeFlushingFrequencyMs); 216 pw.print(", logSize="); pw.print(logHistorySize); 217 pw.print(", disableFlushForViewTreeAppearing="); pw.print(disableFlushForViewTreeAppearing); 218 if (whitelistedComponents != null) { 219 pw.print(", whitelisted="); pw.print(whitelistedComponents); 220 } 221 } 222 223 @Override describeContents()224 public int describeContents() { 225 return 0; 226 } 227 228 @Override writeToParcel(Parcel parcel, int flags)229 public void writeToParcel(Parcel parcel, int flags) { 230 parcel.writeBoolean(lite); 231 parcel.writeInt(loggingLevel); 232 if (lite) return; 233 234 parcel.writeInt(maxBufferSize); 235 parcel.writeInt(idleFlushingFrequencyMs); 236 parcel.writeInt(textChangeFlushingFrequencyMs); 237 parcel.writeInt(logHistorySize); 238 parcel.writeBoolean(disableFlushForViewTreeAppearing); 239 parcel.writeArraySet(whitelistedComponents); 240 } 241 242 public static final @android.annotation.NonNull Parcelable.Creator<ContentCaptureOptions> CREATOR = 243 new Parcelable.Creator<ContentCaptureOptions>() { 244 245 @Override 246 public ContentCaptureOptions createFromParcel(Parcel parcel) { 247 final boolean lite = parcel.readBoolean(); 248 final int loggingLevel = parcel.readInt(); 249 if (lite) { 250 return new ContentCaptureOptions(loggingLevel); 251 } 252 final int maxBufferSize = parcel.readInt(); 253 final int idleFlushingFrequencyMs = parcel.readInt(); 254 final int textChangeFlushingFrequencyMs = parcel.readInt(); 255 final int logHistorySize = parcel.readInt(); 256 final boolean disableFlushForViewTreeAppearing = parcel.readBoolean(); 257 @SuppressWarnings("unchecked") 258 final ArraySet<ComponentName> whitelistedComponents = 259 (ArraySet<ComponentName>) parcel.readArraySet(null); 260 return new ContentCaptureOptions(loggingLevel, maxBufferSize, 261 idleFlushingFrequencyMs, textChangeFlushingFrequencyMs, logHistorySize, 262 disableFlushForViewTreeAppearing, whitelistedComponents); 263 } 264 265 @Override 266 public ContentCaptureOptions[] newArray(int size) { 267 return new ContentCaptureOptions[size]; 268 } 269 }; 270 } 271