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