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.info.ActivityInfo;
21 import com.android.queryable.info.ServiceInfo;
22 import com.android.queryable.queries.ActivityQuery;
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.ServiceQuery;
30 import com.android.queryable.queries.SetQuery;
31 import com.android.queryable.queries.SetQueryHelper;
32 import com.android.queryable.queries.StringQuery;
33 import com.android.queryable.queries.StringQueryHelper;
34 
35 /** Builder for progressively building {@link TestApp} queries. */
36 public final class TestAppQueryBuilder implements Queryable {
37     private final TestAppProvider mProvider;
38 
39     StringQueryHelper<TestAppQueryBuilder> mPackageName = new StringQueryHelper<>(this);
40     BundleQueryHelper<TestAppQueryBuilder> mMetadata = new BundleQueryHelper<>(this);
41     IntegerQueryHelper<TestAppQueryBuilder> mMinSdkVersion = new IntegerQueryHelper<>(this);
42     IntegerQueryHelper<TestAppQueryBuilder> mMaxSdkVersion = new IntegerQueryHelper<>(this);
43     IntegerQueryHelper<TestAppQueryBuilder> mTargetSdkVersion = new IntegerQueryHelper<>(this);
44     SetQueryHelper<TestAppQueryBuilder, String, StringQuery<?>> mPermissions =
45             new SetQueryHelper<>(this);
46     BooleanQueryHelper<TestAppQueryBuilder> mTestOnly = new BooleanQueryHelper<>(this);
47     SetQueryHelper<TestAppQueryBuilder, ActivityInfo, ActivityQuery<?>> mActivities =
48             new SetQueryHelper<>(this);
49     SetQueryHelper<TestAppQueryBuilder, ServiceInfo, ServiceQuery<?>> mServices =
50             new SetQueryHelper<>(this);
51     StringQueryHelper<TestAppQueryBuilder> mSharedUserId = new StringQueryHelper<>(this);
52 
TestAppQueryBuilder(TestAppProvider provider)53     TestAppQueryBuilder(TestAppProvider provider) {
54         if (provider == null) {
55             throw new NullPointerException();
56         }
57         mProvider = provider;
58     }
59 
60     /**
61      * Query for a {@link TestApp} with a given package name.
62      *
63      * <p>Only use this filter when you are relying specifically on the package name itself. If you
64      * are relying on features you know the {@link TestApp} with that package name has, query for
65      * those features directly.
66      */
wherePackageName()67     public StringQuery<TestAppQueryBuilder> wherePackageName() {
68         return mPackageName;
69     }
70 
71     /**
72      * Query for a {@link TestApp} by metadata.
73      */
whereMetadata()74     public BundleQuery<TestAppQueryBuilder> whereMetadata() {
75         return mMetadata;
76     }
77 
78     /**
79      * Query for a {@link TestApp} by minSdkVersion.
80      */
whereMinSdkVersion()81     public IntegerQuery<TestAppQueryBuilder> whereMinSdkVersion() {
82         return mMinSdkVersion;
83     }
84 
85     /**
86      * Query for a {@link TestApp} by maxSdkVersion.
87      */
whereMaxSdkVersion()88     public IntegerQuery<TestAppQueryBuilder> whereMaxSdkVersion() {
89         return mMaxSdkVersion;
90     }
91 
92     /**
93      * Query for a {@link TestApp} by targetSdkVersion.
94      */
whereTargetSdkVersion()95     public IntegerQuery<TestAppQueryBuilder> whereTargetSdkVersion() {
96         return mTargetSdkVersion;
97     }
98 
99     /**
100      * Query for a {@link TestApp} by declared permissions.
101      */
wherePermissions()102     public SetQuery<TestAppQueryBuilder, String, StringQuery<?>> wherePermissions() {
103         return mPermissions;
104     }
105 
106     /**
107      * Query for a {@link TestApp} by the testOnly attribute.
108      */
whereTestOnly()109     public BooleanQuery<TestAppQueryBuilder> whereTestOnly() {
110         return mTestOnly;
111     }
112 
113     /**
114      * Query for a {@link TestApp} by its sharedUserId;
115      */
whereSharedUserId()116     public StringQuery<TestAppQueryBuilder> whereSharedUserId() {
117         return mSharedUserId;
118     }
119 
120     /**
121      * Query for a {@link TestApp} by its activities.
122      */
whereActivities()123     public SetQuery<TestAppQueryBuilder, ActivityInfo, ActivityQuery<?>> whereActivities() {
124         return mActivities;
125     }
126 
127     /**
128      * Query for a {@link TestApp} by its services.
129      */
whereServices()130     public SetQuery<TestAppQueryBuilder, ServiceInfo, ServiceQuery<?>> whereServices() {
131         return mServices;
132     }
133 
134     /**
135      * Get the {@link TestApp} matching the query.
136      *
137      * @throws NotFoundException if there is no matching @{link TestApp}.
138      */
get()139     public TestApp get() {
140         // TODO(scottjonathan): Provide instructions on adding the TestApp if the query fails
141         return new TestApp(resolveQuery());
142     }
143 
resolveQuery()144     private TestAppDetails resolveQuery() {
145         for (TestAppDetails details : mProvider.testApps()) {
146             if (!matches(details)) {
147                 continue;
148             }
149 
150             mProvider.markTestAppUsed(details);
151             return details;
152         }
153 
154         throw new NotFoundException(this);
155     }
156 
matches(TestAppDetails details)157     private boolean matches(TestAppDetails details) {
158         if (!StringQueryHelper.matches(mPackageName, details.mApp.getPackageName())) {
159             return false;
160         }
161 
162         if (!BundleQueryHelper.matches(mMetadata, details.mMetadata)) {
163             return false;
164         }
165 
166         if (!IntegerQueryHelper.matches(
167                 mMinSdkVersion, details.mApp.getUsesSdk().getMinSdkVersion())) {
168             return false;
169         }
170 
171         if (!IntegerQueryHelper.matches(
172                 mMaxSdkVersion, details.mApp.getUsesSdk().getMaxSdkVersion())) {
173             return false;
174         }
175 
176         if (!IntegerQueryHelper.matches(
177                 mTargetSdkVersion, details.mApp.getUsesSdk().getTargetSdkVersion())) {
178             return false;
179         }
180 
181         if (!SetQueryHelper.matches(mActivities, details.mActivities)) {
182             return false;
183         }
184 
185         if (!SetQueryHelper.matches(mServices, details.mServices)) {
186             return false;
187         }
188 
189         if (!SetQueryHelper.matches(mPermissions, details.mPermissions)) {
190             return false;
191         }
192 
193         if (!BooleanQueryHelper.matches(mTestOnly, details.mApp.getTestOnly())) {
194             return false;
195         }
196 
197         if (mSharedUserId.isEmpty()) {
198             if (details.sharedUserId() != null) {
199                 return false;
200             }
201         } else {
202             if (!StringQueryHelper.matches(mSharedUserId, details.sharedUserId())) {
203                 return false;
204             }
205         }
206 
207         if (details.mMetadata.getString("testapp-package-query-only", "false")
208                 .equals("true")) {
209             if (!mPackageName.isQueryingForExactMatch()) {
210                 return false;
211             }
212         }
213 
214         return true;
215     }
216 
217     @Override
describeQuery(String fieldName)218     public String describeQuery(String fieldName) {
219         return "{" + Queryable.joinQueryStrings(
220                 mPackageName.describeQuery("packageName"),
221                 mMetadata.describeQuery("metadata"),
222                 mMinSdkVersion.describeQuery("minSdkVersion"),
223                 mMaxSdkVersion.describeQuery("maxSdkVersion"),
224                 mTargetSdkVersion.describeQuery("targetSdkVersion"),
225                 mActivities.describeQuery("activities"),
226                 mServices.describeQuery("services"),
227                 mPermissions.describeQuery("permissions"),
228                 mSharedUserId.describeQuery("sharedUserId"),
229                 mTestOnly.describeQuery("testOnly")
230         ) + "}";
231     }
232 }
233