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 * Is the content capture receiver enabled. 80 * 81 * @hide 82 */ 83 public final boolean enableReceiver; 84 85 /** 86 * Options for the content protection flow. 87 * 88 * @hide 89 */ 90 @NonNull public final ContentProtectionOptions contentProtectionOptions; 91 92 /** 93 * List of activities explicitly allowlisted for content capture (or {@code null} if allowlisted 94 * for all acitivites in the package). 95 */ 96 @Nullable 97 @SuppressLint("NullableCollection") 98 public final ArraySet<ComponentName> whitelistedComponents; 99 100 /** 101 * Used to enable just a small set of APIs so it can used by activities belonging to the 102 * content capture service APK. 103 */ 104 public final boolean lite; 105 106 /** 107 * Constructor for "lite" objects that are just used to enable a {@link ContentCaptureManager} 108 * for contexts belonging to the content capture service app. 109 */ ContentCaptureOptions(int loggingLevel)110 public ContentCaptureOptions(int loggingLevel) { 111 this( 112 /* lite= */ true, 113 loggingLevel, 114 /* maxBufferSize= */ 0, 115 /* idleFlushingFrequencyMs= */ 0, 116 /* textChangeFlushingFrequencyMs= */ 0, 117 /* logHistorySize= */ 0, 118 /* disableFlushForViewTreeAppearing= */ false, 119 /* enableReceiver= */ false, 120 new ContentProtectionOptions( 121 /* enableReceiver= */ false, 122 /* bufferSize= */ 0), 123 /* whitelistedComponents= */ null); 124 } 125 126 /** Default constructor. */ ContentCaptureOptions( int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, @SuppressLint({R, R}) @Nullable ArraySet<ComponentName> whitelistedComponents)127 public ContentCaptureOptions( 128 int loggingLevel, 129 int maxBufferSize, 130 int idleFlushingFrequencyMs, 131 int textChangeFlushingFrequencyMs, 132 int logHistorySize, 133 @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable 134 ArraySet<ComponentName> whitelistedComponents) { 135 this( 136 /* lite= */ false, 137 loggingLevel, 138 maxBufferSize, 139 idleFlushingFrequencyMs, 140 textChangeFlushingFrequencyMs, 141 logHistorySize, 142 ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, 143 ContentCaptureManager.DEFAULT_ENABLE_CONTENT_CAPTURE_RECEIVER, 144 new ContentProtectionOptions( 145 ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER, 146 ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE), 147 whitelistedComponents); 148 } 149 150 /** @hide */ ContentCaptureOptions( int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, boolean disableFlushForViewTreeAppearing, boolean enableReceiver, @NonNull ContentProtectionOptions contentProtectionOptions, @SuppressLint({R, R}) @Nullable ArraySet<ComponentName> whitelistedComponents)151 public ContentCaptureOptions( 152 int loggingLevel, 153 int maxBufferSize, 154 int idleFlushingFrequencyMs, 155 int textChangeFlushingFrequencyMs, 156 int logHistorySize, 157 boolean disableFlushForViewTreeAppearing, 158 boolean enableReceiver, 159 @NonNull ContentProtectionOptions contentProtectionOptions, 160 @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable 161 ArraySet<ComponentName> whitelistedComponents) { 162 this( 163 /* lite= */ false, 164 loggingLevel, 165 maxBufferSize, 166 idleFlushingFrequencyMs, 167 textChangeFlushingFrequencyMs, 168 logHistorySize, 169 disableFlushForViewTreeAppearing, 170 enableReceiver, 171 contentProtectionOptions, 172 whitelistedComponents); 173 } 174 175 /** @hide */ 176 @VisibleForTesting ContentCaptureOptions(@ullable ArraySet<ComponentName> whitelistedComponents)177 public ContentCaptureOptions(@Nullable ArraySet<ComponentName> whitelistedComponents) { 178 this( 179 ContentCaptureManager.LOGGING_LEVEL_VERBOSE, 180 ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE, 181 ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS, 182 ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS, 183 ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, 184 ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, 185 ContentCaptureManager.DEFAULT_ENABLE_CONTENT_CAPTURE_RECEIVER, 186 new ContentProtectionOptions( 187 ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER, 188 ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE), 189 whitelistedComponents); 190 } 191 ContentCaptureOptions( boolean lite, int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, boolean disableFlushForViewTreeAppearing, boolean enableReceiver, @NonNull ContentProtectionOptions contentProtectionOptions, @SuppressLint({R, R}) @Nullable ArraySet<ComponentName> whitelistedComponents)192 private ContentCaptureOptions( 193 boolean lite, 194 int loggingLevel, 195 int maxBufferSize, 196 int idleFlushingFrequencyMs, 197 int textChangeFlushingFrequencyMs, 198 int logHistorySize, 199 boolean disableFlushForViewTreeAppearing, 200 boolean enableReceiver, 201 @NonNull ContentProtectionOptions contentProtectionOptions, 202 @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable 203 ArraySet<ComponentName> whitelistedComponents) { 204 this.lite = lite; 205 this.loggingLevel = loggingLevel; 206 this.maxBufferSize = maxBufferSize; 207 this.idleFlushingFrequencyMs = idleFlushingFrequencyMs; 208 this.textChangeFlushingFrequencyMs = textChangeFlushingFrequencyMs; 209 this.logHistorySize = logHistorySize; 210 this.disableFlushForViewTreeAppearing = disableFlushForViewTreeAppearing; 211 this.enableReceiver = enableReceiver; 212 this.contentProtectionOptions = contentProtectionOptions; 213 this.whitelistedComponents = whitelistedComponents; 214 } 215 forWhitelistingItself()216 public static ContentCaptureOptions forWhitelistingItself() { 217 final ActivityThread at = ActivityThread.currentActivityThread(); 218 if (at == null) { 219 throw new IllegalStateException("No ActivityThread"); 220 } 221 222 final String packageName = at.getApplication().getPackageName(); 223 224 if (!"android.contentcaptureservice.cts".equals(packageName) 225 && !"android.translation.cts".equals(packageName)) { 226 Log.e(TAG, "forWhitelistingItself(): called by " + packageName); 227 throw new SecurityException("Thou shall not pass!"); 228 } 229 230 final ContentCaptureOptions options = 231 new ContentCaptureOptions(/* whitelistedComponents= */ null); 232 // Always log, as it's used by test only 233 Log.i(TAG, "forWhitelistingItself(" + packageName + "): " + options); 234 235 return options; 236 } 237 238 /** @hide */ 239 @VisibleForTesting isWhitelisted(@onNull Context context)240 public boolean isWhitelisted(@NonNull Context context) { 241 if (whitelistedComponents == null) return true; // whole package is allowlisted 242 final ContentCaptureClient client = context.getContentCaptureClient(); 243 if (client == null) { 244 // Shouldn't happen, but it doesn't hurt to check... 245 Log.w(TAG, "isWhitelisted(): no ContentCaptureClient on " + context); 246 return false; 247 } 248 return whitelistedComponents.contains(client.contentCaptureClientGetComponentName()); 249 } 250 251 @Override toString()252 public String toString() { 253 if (lite) { 254 return "ContentCaptureOptions [loggingLevel=" + loggingLevel + " (lite)]"; 255 } 256 final StringBuilder string = new StringBuilder("ContentCaptureOptions ["); 257 string.append("loggingLevel=") 258 .append(loggingLevel) 259 .append(", maxBufferSize=") 260 .append(maxBufferSize) 261 .append(", idleFlushingFrequencyMs=") 262 .append(idleFlushingFrequencyMs) 263 .append(", textChangeFlushingFrequencyMs=") 264 .append(textChangeFlushingFrequencyMs) 265 .append(", logHistorySize=") 266 .append(logHistorySize) 267 .append(", disableFlushForViewTreeAppearing=") 268 .append(disableFlushForViewTreeAppearing) 269 .append(", enableReceiver=") 270 .append(enableReceiver) 271 .append(", contentProtectionOptions=") 272 .append(contentProtectionOptions); 273 if (whitelistedComponents != null) { 274 string.append(", whitelisted=").append(whitelistedComponents); 275 } 276 return string.append(']').toString(); 277 } 278 279 /** @hide */ dumpShort(@onNull PrintWriter pw)280 public void dumpShort(@NonNull PrintWriter pw) { 281 pw.print("logLvl="); pw.print(loggingLevel); 282 if (lite) { 283 pw.print(", lite"); 284 return; 285 } 286 pw.print(", bufferSize="); 287 pw.print(maxBufferSize); 288 pw.print(", idle="); 289 pw.print(idleFlushingFrequencyMs); 290 pw.print(", textIdle="); 291 pw.print(textChangeFlushingFrequencyMs); 292 pw.print(", logSize="); 293 pw.print(logHistorySize); 294 pw.print(", disableFlushForViewTreeAppearing="); 295 pw.print(disableFlushForViewTreeAppearing); 296 pw.print(", enableReceiver="); 297 pw.print(enableReceiver); 298 pw.print(", contentProtectionOptions=["); 299 contentProtectionOptions.dumpShort(pw); 300 pw.print("]"); 301 if (whitelistedComponents != null) { 302 pw.print(", whitelisted="); pw.print(whitelistedComponents); 303 } 304 } 305 306 @Override describeContents()307 public int describeContents() { 308 return 0; 309 } 310 311 @Override writeToParcel(Parcel parcel, int flags)312 public void writeToParcel(Parcel parcel, int flags) { 313 parcel.writeBoolean(lite); 314 parcel.writeInt(loggingLevel); 315 if (lite) return; 316 317 parcel.writeInt(maxBufferSize); 318 parcel.writeInt(idleFlushingFrequencyMs); 319 parcel.writeInt(textChangeFlushingFrequencyMs); 320 parcel.writeInt(logHistorySize); 321 parcel.writeBoolean(disableFlushForViewTreeAppearing); 322 parcel.writeBoolean(enableReceiver); 323 contentProtectionOptions.writeToParcel(parcel); 324 parcel.writeArraySet(whitelistedComponents); 325 } 326 327 public static final @android.annotation.NonNull Parcelable.Creator<ContentCaptureOptions> CREATOR = 328 new Parcelable.Creator<ContentCaptureOptions>() { 329 330 @Override 331 public ContentCaptureOptions createFromParcel(Parcel parcel) { 332 final boolean lite = parcel.readBoolean(); 333 final int loggingLevel = parcel.readInt(); 334 if (lite) { 335 return new ContentCaptureOptions(loggingLevel); 336 } 337 final int maxBufferSize = parcel.readInt(); 338 final int idleFlushingFrequencyMs = parcel.readInt(); 339 final int textChangeFlushingFrequencyMs = parcel.readInt(); 340 final int logHistorySize = parcel.readInt(); 341 final boolean disableFlushForViewTreeAppearing = parcel.readBoolean(); 342 final boolean enableReceiver = parcel.readBoolean(); 343 final ContentProtectionOptions contentProtectionOptions = 344 ContentProtectionOptions.createFromParcel(parcel); 345 @SuppressWarnings("unchecked") 346 final ArraySet<ComponentName> whitelistedComponents = 347 (ArraySet<ComponentName>) parcel.readArraySet(null); 348 return new ContentCaptureOptions( 349 loggingLevel, 350 maxBufferSize, 351 idleFlushingFrequencyMs, 352 textChangeFlushingFrequencyMs, 353 logHistorySize, 354 disableFlushForViewTreeAppearing, 355 enableReceiver, 356 contentProtectionOptions, 357 whitelistedComponents); 358 } 359 360 @Override 361 public ContentCaptureOptions[] newArray(int size) { 362 return new ContentCaptureOptions[size]; 363 } 364 }; 365 366 /** 367 * Content protection options for a given package. 368 * 369 * <p>Does not implement {@code Parcelable} since it is an inner class without a matching AIDL. 370 * 371 * @hide 372 */ 373 public static class ContentProtectionOptions { 374 375 /** 376 * Is the content protection receiver enabled. 377 * 378 * @hide 379 */ 380 public final boolean enableReceiver; 381 382 /** 383 * Size of the in-memory ring buffer for the content protection flow. 384 * 385 * @hide 386 */ 387 public final int bufferSize; 388 ContentProtectionOptions(boolean enableReceiver, int bufferSize)389 public ContentProtectionOptions(boolean enableReceiver, int bufferSize) { 390 this.enableReceiver = enableReceiver; 391 this.bufferSize = bufferSize; 392 } 393 394 @Override toString()395 public String toString() { 396 StringBuilder stringBuilder = new StringBuilder("ContentProtectionOptions ["); 397 stringBuilder 398 .append("enableReceiver=") 399 .append(enableReceiver) 400 .append(", bufferSize=") 401 .append(bufferSize); 402 return stringBuilder.append(']').toString(); 403 } 404 dumpShort(@onNull PrintWriter pw)405 private void dumpShort(@NonNull PrintWriter pw) { 406 pw.print("enableReceiver="); 407 pw.print(enableReceiver); 408 pw.print(", bufferSize="); 409 pw.print(bufferSize); 410 } 411 writeToParcel(Parcel parcel)412 private void writeToParcel(Parcel parcel) { 413 parcel.writeBoolean(enableReceiver); 414 parcel.writeInt(bufferSize); 415 } 416 createFromParcel(Parcel parcel)417 private static ContentProtectionOptions createFromParcel(Parcel parcel) { 418 boolean enableReceiver = parcel.readBoolean(); 419 int bufferSize = parcel.readInt(); 420 return new ContentProtectionOptions(enableReceiver, bufferSize); 421 } 422 } 423 } 424