• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 
17 package com.android.bedstead.testapp;
18 
19 import com.android.queryable.Queryable;
20 import com.android.queryable.annotations.Query;
21 import com.android.queryable.info.ActivityInfo;
22 import com.android.queryable.info.ServiceInfo;
23 import com.android.queryable.queries.BooleanQuery;
24 import com.android.queryable.queries.BooleanQueryHelper;
25 import com.android.queryable.queries.BundleQuery;
26 import com.android.queryable.queries.BundleQueryHelper;
27 import com.android.queryable.queries.IntegerQuery;
28 import com.android.queryable.queries.IntegerQueryHelper;
29 import com.android.queryable.queries.SetQuery;
30 import com.android.queryable.queries.SetQueryHelper;
31 import com.android.queryable.queries.StringQuery;
32 import com.android.queryable.queries.StringQueryHelper;
33 
34 import com.google.auto.value.AutoAnnotation;
35 
36 /** Builder for progressively building {@link TestApp} queries. */
37 public final class TestAppQueryBuilder implements Queryable {
38     private final TestAppProvider mProvider;
39 
40     StringQueryHelper<TestAppQueryBuilder> mLabel = new StringQueryHelper<>(this);
41     StringQueryHelper<TestAppQueryBuilder> mPackageName = new StringQueryHelper<>(this);
42     BundleQueryHelper<TestAppQueryBuilder> mMetadata = new BundleQueryHelper<>(this);
43     IntegerQueryHelper<TestAppQueryBuilder> mMinSdkVersion = new IntegerQueryHelper<>(this);
44     IntegerQueryHelper<TestAppQueryBuilder> mMaxSdkVersion = new IntegerQueryHelper<>(this);
45     IntegerQueryHelper<TestAppQueryBuilder> mTargetSdkVersion = new IntegerQueryHelper<>(this);
46     SetQueryHelper<TestAppQueryBuilder, String> mPermissions =
47             new SetQueryHelper<>(this);
48     BooleanQueryHelper<TestAppQueryBuilder> mTestOnly = new BooleanQueryHelper<>(this);
49     BooleanQueryHelper<TestAppQueryBuilder> mCrossProfile = new BooleanQueryHelper<>(this);
50     SetQueryHelper<TestAppQueryBuilder, ActivityInfo> mActivities =
51             new SetQueryHelper<>(this);
52     SetQueryHelper<TestAppQueryBuilder, ServiceInfo> mServices =
53             new SetQueryHelper<>(this);
54     BooleanQueryHelper<TestAppQueryBuilder> mIsDeviceAdmin = new BooleanQueryHelper<>(this);
55     StringQueryHelper<TestAppQueryBuilder> mSharedUserId = new StringQueryHelper<>(this);
56     private boolean mAllowInternalBedsteadTestApps = false;
57 
58     /**
59      * Returns a {@link TestAppQueryBuilder} not linked to a specific {@link TestAppProvider}.
60      *
61      * <p>Note that attempts to resolve this query will fail.
62      */
queryBuilder()63     public static TestAppQueryBuilder queryBuilder() {
64         return new TestAppQueryBuilder();
65     }
66 
TestAppQueryBuilder()67     private TestAppQueryBuilder() {
68         mProvider = null;
69     }
70 
TestAppQueryBuilder(TestAppProvider provider)71     TestAppQueryBuilder(TestAppProvider provider) {
72         if (provider == null) {
73             throw new NullPointerException();
74         }
75         mProvider = provider;
76     }
77 
78     /**
79      * Apply the query parameters inside the {@link Query} to this {@link TestAppQueryBuilder}.
80      */
applyAnnotation(Query query)81     public TestAppQueryBuilder applyAnnotation(Query query) {
82         if (query == null) {
83             return this;
84         }
85 
86         TestAppQueryBuilder queryBuilder = this;
87         queryBuilder = queryBuilder.whereTargetSdkVersion().matchesAnnotation(query.targetSdkVersion());
88         queryBuilder = queryBuilder.whereMinSdkVersion().matchesAnnotation(query.minSdkVersion());
89         queryBuilder = queryBuilder.whereMaxSdkVersion().matchesAnnotation(query.maxSdkVersion());
90         queryBuilder = queryBuilder.wherePackageName().matchesAnnotation(query.packageName());
91         return queryBuilder;
92     }
93 
94     /**
95      * Query for a {@link TestApp} which declares the given label.
96      */
whereLabel()97     public StringQuery<TestAppQueryBuilder> whereLabel() {
98         return mLabel;
99     }
100 
101     /**
102      * Query for a {@link TestApp} with a given package name.
103      *
104      * <p>Only use this filter when you are relying specifically on the package name itself. If you
105      * are relying on features you know the {@link TestApp} with that package name has, query for
106      * those features directly.
107      */
wherePackageName()108     public StringQuery<TestAppQueryBuilder> wherePackageName() {
109         return mPackageName;
110     }
111 
112     /**
113      * Query for a {@link TestApp} by metadata.
114      */
whereMetadata()115     public BundleQuery<TestAppQueryBuilder> whereMetadata() {
116         return mMetadata;
117     }
118 
119     /**
120      * Query for a {@link TestApp} by minSdkVersion.
121      */
whereMinSdkVersion()122     public IntegerQuery<TestAppQueryBuilder> whereMinSdkVersion() {
123         return mMinSdkVersion;
124     }
125 
126     /**
127      * Query for a {@link TestApp} by maxSdkVersion.
128      */
whereMaxSdkVersion()129     public IntegerQuery<TestAppQueryBuilder> whereMaxSdkVersion() {
130         return mMaxSdkVersion;
131     }
132 
133     /**
134      * Query for a {@link TestApp} by targetSdkVersion.
135      */
whereTargetSdkVersion()136     public IntegerQuery<TestAppQueryBuilder> whereTargetSdkVersion() {
137         return mTargetSdkVersion;
138     }
139 
140     /**
141      * Query for a {@link TestApp} by declared permissions.
142      */
wherePermissions()143     public SetQuery<TestAppQueryBuilder, String> wherePermissions() {
144         return mPermissions;
145     }
146 
147     /**
148      * Query for a {@link TestApp} by the testOnly attribute.
149      */
whereTestOnly()150     public BooleanQuery<TestAppQueryBuilder> whereTestOnly() {
151         return mTestOnly;
152     }
153 
154     /**
155      * Query for a {@link TestApp} by the crossProfile attribute.
156      */
whereCrossProfile()157     public BooleanQuery<TestAppQueryBuilder> whereCrossProfile() {
158         return mCrossProfile;
159     }
160 
161     /**
162      * Query for an app which is a device admin.
163      */
whereIsDeviceAdmin()164     public BooleanQuery<TestAppQueryBuilder> whereIsDeviceAdmin() {
165         return mIsDeviceAdmin;
166     }
167 
168     /**
169      * Query for a {@link TestApp} by its sharedUserId;
170      */
whereSharedUserId()171     public StringQuery<TestAppQueryBuilder> whereSharedUserId() {
172         return mSharedUserId;
173     }
174 
175     /**
176      * Query for a {@link TestApp} by its activities.
177      */
whereActivities()178     public SetQuery<TestAppQueryBuilder, ActivityInfo> whereActivities() {
179         return mActivities;
180     }
181 
182     /**
183      * Query for a {@link TestApp} by its services.
184      */
whereServices()185     public SetQuery<TestAppQueryBuilder, ServiceInfo> whereServices() {
186         return mServices;
187     }
188 
189     /**
190      * Allow the query to return internal bedstead testapps.
191      */
allowInternalBedsteadTestApps()192     public TestAppQueryBuilder allowInternalBedsteadTestApps() {
193         mAllowInternalBedsteadTestApps = true;
194         return this;
195     }
196 
197     /**
198      * Get the {@link TestApp} matching the query.
199      *
200      * @throws NotFoundException if there is no matching @{link TestApp}.
201      */
get()202     public TestApp get() {
203         // TODO(scottjonathan): Provide instructions on adding the TestApp if the query fails
204         return new TestApp(resolveQuery());
205     }
206 
207     /**
208      * Checks if the query matches the specified test app
209      */
matches(TestApp testApp)210     public boolean matches(TestApp testApp) {
211         TestAppDetails details = testApp.mDetails;
212         return matches(details);
213     }
214 
resolveQuery()215     private TestAppDetails resolveQuery() {
216         if (mProvider == null) {
217             throw new IllegalStateException("Cannot resolve testApps in an empty query. You must"
218                     + " create the query using a testAppProvider.query() rather than "
219                     + "TestAppQueryBuilder.query() in order to get results");
220         }
221 
222         for (TestAppDetails details : mProvider.testApps()) {
223             if (!matches(details)) {
224                 continue;
225             }
226 
227             mProvider.markTestAppUsed(details);
228             return details;
229         }
230 
231         throw new NotFoundException(this);
232     }
233 
234     @Override
isEmptyQuery()235     public boolean isEmptyQuery() {
236         return Queryable.isEmptyQuery(mPackageName)
237                 && Queryable.isEmptyQuery(mLabel)
238                 && Queryable.isEmptyQuery(mMetadata)
239                 && Queryable.isEmptyQuery(mMinSdkVersion)
240                 && Queryable.isEmptyQuery(mMaxSdkVersion)
241                 && Queryable.isEmptyQuery(mTargetSdkVersion)
242                 && Queryable.isEmptyQuery(mActivities)
243                 && Queryable.isEmptyQuery(mServices)
244                 && Queryable.isEmptyQuery(mPermissions)
245                 && Queryable.isEmptyQuery(mTestOnly)
246                 && Queryable.isEmptyQuery(mCrossProfile)
247                 && Queryable.isEmptyQuery(mIsDeviceAdmin)
248                 && Queryable.isEmptyQuery(mSharedUserId);
249     }
250 
matches(TestAppDetails details)251     private boolean matches(TestAppDetails details) {
252         if (!StringQueryHelper.matches(mPackageName, details.mApp.getPackageName())) {
253             return false;
254         }
255 
256         if (!StringQueryHelper.matches(mLabel, details.label())) {
257             return false;
258         }
259 
260         if (!BundleQueryHelper.matches(mMetadata, details.mMetadata)) {
261             return false;
262         }
263 
264         if (!IntegerQueryHelper.matches(
265                 mMinSdkVersion, details.mApp.getUsesSdk().getMinSdkVersion())) {
266             return false;
267         }
268 
269         if (!IntegerQueryHelper.matches(
270                 mMaxSdkVersion, details.mApp.getUsesSdk().getMaxSdkVersion())) {
271             return false;
272         }
273 
274         if (!IntegerQueryHelper.matches(
275                 mTargetSdkVersion, details.mApp.getUsesSdk().getTargetSdkVersion())) {
276             return false;
277         }
278 
279         if (!SetQueryHelper.matches(mActivities, details.mActivities)) {
280             return false;
281         }
282 
283         if (!SetQueryHelper.matches(mServices, details.mServices)) {
284             return false;
285         }
286 
287         if (!SetQueryHelper.matches(mPermissions, details.mPermissions)) {
288             return false;
289         }
290 
291         if (!BooleanQueryHelper.matches(mTestOnly, details.mApp.getTestOnly())) {
292             return false;
293         }
294 
295         if (!BooleanQueryHelper.matches(mCrossProfile, details.mApp.getCrossProfile())) {
296             return false;
297         }
298 
299         // TODO(b/198419895): Actually query for the correct receiver + metadata
300         boolean isDeviceAdmin = details.mApp.getPackageName().contains(
301                 "DeviceAdminTestApp");
302         if (!BooleanQueryHelper.matches(mIsDeviceAdmin, isDeviceAdmin)) {
303             return false;
304         }
305 
306         if (mSharedUserId.isEmpty()) {
307             if (details.sharedUserId() != null) {
308                 return false;
309             }
310         } else {
311             if (!StringQueryHelper.matches(mSharedUserId, details.sharedUserId())) {
312                 return false;
313             }
314         }
315 
316         if (!mAllowInternalBedsteadTestApps
317                 && details.mMetadata.getString("testapp-package-query-only", "false")
318                 .equals("true")) {
319             if (!mPackageName.isQueryingForExactMatch()) {
320                 return false;
321             }
322         }
323 
324         return true;
325     }
326 
327     @Override
describeQuery(String fieldName)328     public String describeQuery(String fieldName) {
329         return "{" + Queryable.joinQueryStrings(
330                 mPackageName.describeQuery("packageName"),
331                 mLabel.describeQuery("label"),
332                 mMetadata.describeQuery("metadata"),
333                 mMinSdkVersion.describeQuery("minSdkVersion"),
334                 mMaxSdkVersion.describeQuery("maxSdkVersion"),
335                 mTargetSdkVersion.describeQuery("targetSdkVersion"),
336                 mActivities.describeQuery("activities"),
337                 mServices.describeQuery("services"),
338                 mPermissions.describeQuery("permissions"),
339                 mSharedUserId.describeQuery("sharedUserId"),
340                 mTestOnly.describeQuery("testOnly"),
341                 mCrossProfile.describeQuery("crossProfile"),
342                 mIsDeviceAdmin.describeQuery("isDeviceAdmin")
343         ) + "}";
344     }
345 
346     @Override
toString()347     public String toString() {
348         return "TestAppQueryBuilder" + describeQuery(null);
349     }
350 
toAnnotation()351     public Query toAnnotation() {
352         return query(mPackageName.toAnnotation(),
353                 mTargetSdkVersion.toAnnotation(),
354                 mMinSdkVersion.toAnnotation(),
355                 mMaxSdkVersion.toAnnotation());
356     }
357 
358     @AutoAnnotation
query( com.android.queryable.annotations.StringQuery packageName, com.android.queryable.annotations.IntegerQuery targetSdkVersion, com.android.queryable.annotations.IntegerQuery minSdkVersion, com.android.queryable.annotations.IntegerQuery maxSdkVersion)359     private static Query query(
360             com.android.queryable.annotations.StringQuery packageName,
361             com.android.queryable.annotations.IntegerQuery targetSdkVersion,
362             com.android.queryable.annotations.IntegerQuery minSdkVersion,
363             com.android.queryable.annotations.IntegerQuery maxSdkVersion) {
364         return new AutoAnnotation_TestAppQueryBuilder_query(
365                 packageName, targetSdkVersion, minSdkVersion, maxSdkVersion);
366     }
367 }
368