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 * List of activities explicitly allowlisted for content capture (or {@code null} if allowlisted 74 * for all acitivites in the package). 75 */ 76 @Nullable 77 @SuppressLint("NullableCollection") 78 public final ArraySet<ComponentName> whitelistedComponents; 79 80 /** 81 * Used to enable just a small set of APIs so it can used by activities belonging to the 82 * content capture service APK. 83 */ 84 public final boolean lite; 85 86 /** 87 * Constructor for "lite" objects that are just used to enable a {@link ContentCaptureManager} 88 * for contexts belonging to the content capture service app. 89 */ ContentCaptureOptions(int loggingLevel)90 public ContentCaptureOptions(int loggingLevel) { 91 this(/* lite= */ true, loggingLevel, /* maxBufferSize= */ 0, 92 /* idleFlushingFrequencyMs= */ 0, /* textChangeFlushingFrequencyMs= */ 0, 93 /* logHistorySize= */ 0, /* whitelistedComponents= */ null); 94 } 95 96 /** 97 * Default constructor. 98 */ ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, @SuppressLint(R) @Nullable ArraySet<ComponentName> whitelistedComponents)99 public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, 100 int textChangeFlushingFrequencyMs, int logHistorySize, 101 @SuppressLint("NullableCollection") 102 @Nullable ArraySet<ComponentName> whitelistedComponents) { 103 this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs, 104 textChangeFlushingFrequencyMs, logHistorySize, whitelistedComponents); 105 } 106 107 /** @hide */ 108 @VisibleForTesting ContentCaptureOptions(@ullable ArraySet<ComponentName> whitelistedComponents)109 public ContentCaptureOptions(@Nullable ArraySet<ComponentName> whitelistedComponents) { 110 this(ContentCaptureManager.LOGGING_LEVEL_VERBOSE, 111 ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE, 112 ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS, 113 ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS, 114 ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, whitelistedComponents); 115 } 116 ContentCaptureOptions(boolean lite, int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, @Nullable ArraySet<ComponentName> whitelistedComponents)117 private ContentCaptureOptions(boolean lite, int loggingLevel, int maxBufferSize, 118 int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, 119 @Nullable ArraySet<ComponentName> whitelistedComponents) { 120 this.lite = lite; 121 this.loggingLevel = loggingLevel; 122 this.maxBufferSize = maxBufferSize; 123 this.idleFlushingFrequencyMs = idleFlushingFrequencyMs; 124 this.textChangeFlushingFrequencyMs = textChangeFlushingFrequencyMs; 125 this.logHistorySize = logHistorySize; 126 this.whitelistedComponents = whitelistedComponents; 127 } 128 forWhitelistingItself()129 public static ContentCaptureOptions forWhitelistingItself() { 130 final ActivityThread at = ActivityThread.currentActivityThread(); 131 if (at == null) { 132 throw new IllegalStateException("No ActivityThread"); 133 } 134 135 final String packageName = at.getApplication().getPackageName(); 136 137 if (!"android.contentcaptureservice.cts".equals(packageName) 138 && !"android.translation.cts".equals(packageName)) { 139 Log.e(TAG, "forWhitelistingItself(): called by " + packageName); 140 throw new SecurityException("Thou shall not pass!"); 141 } 142 143 final ContentCaptureOptions options = 144 new ContentCaptureOptions(/* whitelistedComponents= */ null); 145 // Always log, as it's used by test only 146 Log.i(TAG, "forWhitelistingItself(" + packageName + "): " + options); 147 148 return options; 149 } 150 151 /** @hide */ 152 @VisibleForTesting isWhitelisted(@onNull Context context)153 public boolean isWhitelisted(@NonNull Context context) { 154 if (whitelistedComponents == null) return true; // whole package is allowlisted 155 final ContentCaptureClient client = context.getContentCaptureClient(); 156 if (client == null) { 157 // Shouldn't happen, but it doesn't hurt to check... 158 Log.w(TAG, "isWhitelisted(): no ContentCaptureClient on " + context); 159 return false; 160 } 161 return whitelistedComponents.contains(client.contentCaptureClientGetComponentName()); 162 } 163 164 @Override toString()165 public String toString() { 166 if (lite) { 167 return "ContentCaptureOptions [loggingLevel=" + loggingLevel + " (lite)]"; 168 } 169 final StringBuilder string = new StringBuilder("ContentCaptureOptions ["); 170 string.append("loggingLevel=").append(loggingLevel) 171 .append(", maxBufferSize=").append(maxBufferSize) 172 .append(", idleFlushingFrequencyMs=").append(idleFlushingFrequencyMs) 173 .append(", textChangeFlushingFrequencyMs=").append(textChangeFlushingFrequencyMs) 174 .append(", logHistorySize=").append(logHistorySize); 175 if (whitelistedComponents != null) { 176 string.append(", whitelisted=").append(whitelistedComponents); 177 } 178 return string.append(']').toString(); 179 } 180 181 /** @hide */ dumpShort(@onNull PrintWriter pw)182 public void dumpShort(@NonNull PrintWriter pw) { 183 pw.print("logLvl="); pw.print(loggingLevel); 184 if (lite) { 185 pw.print(", lite"); 186 return; 187 } 188 pw.print(", bufferSize="); pw.print(maxBufferSize); 189 pw.print(", idle="); pw.print(idleFlushingFrequencyMs); 190 pw.print(", textIdle="); pw.print(textChangeFlushingFrequencyMs); 191 pw.print(", logSize="); pw.print(logHistorySize); 192 if (whitelistedComponents != null) { 193 pw.print(", whitelisted="); pw.print(whitelistedComponents); 194 } 195 } 196 197 @Override describeContents()198 public int describeContents() { 199 return 0; 200 } 201 202 @Override writeToParcel(Parcel parcel, int flags)203 public void writeToParcel(Parcel parcel, int flags) { 204 parcel.writeBoolean(lite); 205 parcel.writeInt(loggingLevel); 206 if (lite) return; 207 208 parcel.writeInt(maxBufferSize); 209 parcel.writeInt(idleFlushingFrequencyMs); 210 parcel.writeInt(textChangeFlushingFrequencyMs); 211 parcel.writeInt(logHistorySize); 212 parcel.writeArraySet(whitelistedComponents); 213 } 214 215 public static final @android.annotation.NonNull Parcelable.Creator<ContentCaptureOptions> CREATOR = 216 new Parcelable.Creator<ContentCaptureOptions>() { 217 218 @Override 219 public ContentCaptureOptions createFromParcel(Parcel parcel) { 220 final boolean lite = parcel.readBoolean(); 221 final int loggingLevel = parcel.readInt(); 222 if (lite) { 223 return new ContentCaptureOptions(loggingLevel); 224 } 225 final int maxBufferSize = parcel.readInt(); 226 final int idleFlushingFrequencyMs = parcel.readInt(); 227 final int textChangeFlushingFrequencyMs = parcel.readInt(); 228 final int logHistorySize = parcel.readInt(); 229 @SuppressWarnings("unchecked") 230 final ArraySet<ComponentName> whitelistedComponents = 231 (ArraySet<ComponentName>) parcel.readArraySet(null); 232 return new ContentCaptureOptions(loggingLevel, maxBufferSize, 233 idleFlushingFrequencyMs, textChangeFlushingFrequencyMs, logHistorySize, 234 whitelistedComponents); 235 } 236 237 @Override 238 public ContentCaptureOptions[] newArray(int size) { 239 return new ContentCaptureOptions[size]; 240 } 241 }; 242 } 243