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.os.SystemClock;
25 import android.util.ArrayMap;
26 import android.util.ArraySet;
27 import android.util.Log;
28 import android.view.autofill.AutofillManager;
29 import android.view.autofill.AutofillManager.AutofillClient;
30 
31 import java.io.PrintWriter;
32 
33 /**
34  * Autofill options for a given package.
35  *
36  * <p>This object is created by the Autofill System Service and passed back to the app when the
37  * application is created.
38  *
39  * @hide
40  */
41 @TestApi
42 public final class AutofillOptions implements Parcelable {
43 
44     private static final String TAG = AutofillOptions.class.getSimpleName();
45 
46     /**
47      * Logging level for {@code logcat} statements.
48      */
49     public final int loggingLevel;
50 
51     /**
52      * Whether compatibility mode is enabled for the package.
53      */
54     public final boolean compatModeEnabled;
55 
56     /**
57      * Whether package is whitelisted for augmented autofill.
58      */
59     public boolean augmentedAutofillEnabled;
60 
61     /**
62      * List of whitelisted activities.
63      */
64     @Nullable
65     public ArraySet<ComponentName> whitelistedActivitiesForAugmentedAutofill;
66 
67     /**
68      * The package disable expiration by autofill service.
69      */
70     public long appDisabledExpiration;
71 
72     /**
73      * The disabled Activities of the package. key is component name string, value is when they
74      * will be enabled.
75      */
76     @Nullable
77     public ArrayMap<String, Long> disabledActivities;
78 
AutofillOptions(int loggingLevel, boolean compatModeEnabled)79     public AutofillOptions(int loggingLevel, boolean compatModeEnabled) {
80         this.loggingLevel = loggingLevel;
81         this.compatModeEnabled = compatModeEnabled;
82     }
83 
84     /**
85      * Returns whether activity is whitelisted for augmented autofill.
86      */
isAugmentedAutofillEnabled(@onNull Context context)87     public boolean isAugmentedAutofillEnabled(@NonNull Context context) {
88         if (!augmentedAutofillEnabled) return false;
89 
90         final AutofillClient autofillClient = context.getAutofillClient();
91         if (autofillClient == null) return false;
92 
93         final ComponentName component = autofillClient.autofillClientGetComponentName();
94         return whitelistedActivitiesForAugmentedAutofill == null
95                 || whitelistedActivitiesForAugmentedAutofill.contains(component);
96     }
97 
98     /**
99      * Returns if autofill is disabled by service to the given activity.
100      *
101      * @hide
102      */
isAutofillDisabledLocked(@onNull ComponentName componentName)103     public boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
104         final long elapsedTime = SystemClock.elapsedRealtime();
105         final String component = componentName.flattenToString();
106         // Check app first.
107         if (appDisabledExpiration >= elapsedTime) return true;
108 
109         // Then check activities.
110         if (disabledActivities != null) {
111             final Long expiration = disabledActivities.get(component);
112             if (expiration != null) {
113                 if (expiration >= elapsedTime) return true;
114                 disabledActivities.remove(component);
115             }
116         }
117         appDisabledExpiration = 0;
118         return false;
119     }
120 
121     /**
122      * @hide
123      */
124     @TestApi
forWhitelistingItself()125     public static AutofillOptions forWhitelistingItself() {
126         final ActivityThread at = ActivityThread.currentActivityThread();
127         if (at == null) {
128             throw new IllegalStateException("No ActivityThread");
129         }
130 
131         final String packageName = at.getApplication().getPackageName();
132 
133         if (!"android.autofillservice.cts".equals(packageName)) {
134             Log.e(TAG, "forWhitelistingItself(): called by " + packageName);
135             throw new SecurityException("Thou shall not pass!");
136         }
137 
138         final AutofillOptions options = new AutofillOptions(
139                 AutofillManager.FLAG_ADD_CLIENT_VERBOSE, /* compatModeAllowed= */ true);
140         options.augmentedAutofillEnabled = true;
141         // Always log, as it's used by test only
142         Log.i(TAG, "forWhitelistingItself(" + packageName + "): " + options);
143 
144         return options;
145     }
146 
147     @Override
toString()148     public String toString() {
149         return "AutofillOptions [loggingLevel=" + loggingLevel + ", compatMode=" + compatModeEnabled
150                 + ", augmentedAutofillEnabled=" + augmentedAutofillEnabled
151                 + ", appDisabledExpiration=" + appDisabledExpiration + "]";
152     }
153 
154     /** @hide */
dumpShort(@onNull PrintWriter pw)155     public void dumpShort(@NonNull PrintWriter pw) {
156         pw.print("logLvl="); pw.print(loggingLevel);
157         pw.print(", compatMode="); pw.print(compatModeEnabled);
158         pw.print(", augmented="); pw.print(augmentedAutofillEnabled);
159         if (whitelistedActivitiesForAugmentedAutofill != null) {
160             pw.print(", whitelistedActivitiesForAugmentedAutofill=");
161             pw.print(whitelistedActivitiesForAugmentedAutofill);
162         }
163         pw.print(", appDisabledExpiration="); pw.print(appDisabledExpiration);
164         if (disabledActivities != null) {
165             pw.print(", disabledActivities=");
166             pw.print(disabledActivities);
167         }
168     }
169 
170     @Override
describeContents()171     public int describeContents() {
172         return 0;
173     }
174 
175     @Override
writeToParcel(Parcel parcel, int flags)176     public void writeToParcel(Parcel parcel, int flags) {
177         parcel.writeInt(loggingLevel);
178         parcel.writeBoolean(compatModeEnabled);
179         parcel.writeBoolean(augmentedAutofillEnabled);
180         parcel.writeArraySet(whitelistedActivitiesForAugmentedAutofill);
181         parcel.writeLong(appDisabledExpiration);
182         final int size = disabledActivities != null ? disabledActivities.size() : 0;
183         parcel.writeInt(size);
184         if (size > 0) {
185             for (int i = 0; i < size; i++) {
186                 final String key = disabledActivities.keyAt(i);
187                 parcel.writeString(key);
188                 parcel.writeLong(disabledActivities.get(key));
189             }
190         }
191     }
192 
193     public static final @android.annotation.NonNull Parcelable.Creator<AutofillOptions> CREATOR =
194             new Parcelable.Creator<AutofillOptions>() {
195 
196                 @Override
197                 public AutofillOptions createFromParcel(Parcel parcel) {
198                     final int loggingLevel = parcel.readInt();
199                     final boolean compatMode = parcel.readBoolean();
200                     final AutofillOptions options = new AutofillOptions(loggingLevel, compatMode);
201                     options.augmentedAutofillEnabled = parcel.readBoolean();
202                     options.whitelistedActivitiesForAugmentedAutofill =
203                             (ArraySet<ComponentName>) parcel.readArraySet(null);
204                     options.appDisabledExpiration = parcel.readLong();
205                     final int size = parcel.readInt();
206                     if (size > 0) {
207                         options.disabledActivities = new ArrayMap<>();
208                         for (int i = 0; i < size; i++) {
209                             options.disabledActivities.put(parcel.readString(), parcel.readLong());
210                         }
211                     }
212                     return options;
213                 }
214 
215                 @Override
216                 public AutofillOptions[] newArray(int size) {
217                     return new AutofillOptions[size];
218                 }
219     };
220 }
221