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