• 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 android.content.Context;
20 import android.content.IntentFilter;
21 import android.util.Log;
22 
23 import com.android.bedstead.nene.TestApis;
24 import com.android.queryable.annotations.Query;
25 import com.android.queryable.info.ActivityInfo;
26 import com.android.queryable.info.ServiceInfo;
27 
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.Comparator;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Set;
36 
37 /** Entry point to Test App. Used for querying for {@link TestApp} instances. */
38 public final class TestAppProvider {
39 
40     private static final String TAG = TestAppProvider.class.getSimpleName();
41 
42     // Must be instrumentation context to access resources
43     private static final Context sContext = TestApis.context().instrumentationContext();
44     private boolean mTestAppsInitialised = false;
45     private final List<TestAppDetails> mTestApps = new ArrayList<>();
46     private Set<TestAppDetails> mTestAppsSnapshot = null;
47 
TestAppProvider()48     public TestAppProvider() {
49         initTestApps();
50     }
51 
52     /** Begin a query for a {@link TestApp}. */
query()53     public TestAppQueryBuilder query() {
54         return new TestAppQueryBuilder(this);
55     }
56 
57     /** Create a query for a {@link TestApp} starting with a {@link Query}. */
query(Query query)58     public TestAppQueryBuilder query(Query query) {
59         return query().applyAnnotation(query);
60     }
61 
62     /** Get any {@link TestApp}. */
any()63     public TestApp any() {
64         TestApp testApp = query().get();
65         Log.d(TAG, "any(): returning " + testApp);
66         return testApp;
67     }
68 
testApps()69     List<TestAppDetails> testApps() {
70         return mTestApps;
71     }
72 
73     /** Save the state of the provider, to be reset by {@link #restore()}. */
snapshot()74     public void snapshot() {
75         mTestAppsSnapshot = new HashSet<>(mTestApps);
76     }
77 
78     /**
79      * Restore the state of the provider to that recorded by {@link #snapshot()}.
80      */
restore()81     public void restore() {
82         if (mTestAppsSnapshot == null) {
83             throw new IllegalStateException("You must call snapshot() before restore()");
84         }
85         mTestApps.clear();
86         mTestApps.addAll(mTestAppsSnapshot);
87     }
88 
initTestApps()89     private void initTestApps() {
90         if (mTestAppsInitialised) {
91             return;
92         }
93         mTestAppsInitialised = true;
94 
95         int indexId = sContext.getResources().getIdentifier(
96                 "raw/index", /* defType= */ null, sContext.getPackageName());
97 
98         try (InputStream inputStream = sContext.getResources().openRawResource(indexId)) {
99             TestappProtos.TestAppIndex index = TestappProtos.TestAppIndex.parseFrom(inputStream);
100             for (int i = 0; i < index.getAppsCount(); i++) {
101                 loadApk(index.getApps(i));
102             }
103             Collections.sort(mTestApps,
104                     Comparator.comparing((testAppDetails) -> testAppDetails.mApp.getPackageName()));
105         } catch (IOException e) {
106             throw new RuntimeException("Error loading testapp index", e);
107         }
108     }
109 
loadApk(TestappProtos.AndroidApp app)110     private void loadApk(TestappProtos.AndroidApp app) {
111         TestAppDetails details = new TestAppDetails();
112         details.mApp = app;
113         details.mResourceIdentifier = sContext.getResources().getIdentifier(
114                 "raw/" + getApkNameWithoutSuffix(app.getApkName()),
115                 /* defType= */ null, sContext.getPackageName());
116 
117         for (int i = 0; i < app.getMetadataCount(); i++) {
118             TestappProtos.Metadata metadataEntry = app.getMetadata(i);
119             details.mMetadata.putString(metadataEntry.getName(), metadataEntry.getValue());
120         }
121 
122         for (int i = 0; i < app.getPermissionsCount(); i++) {
123             details.mPermissions.add(app.getPermissions(i).getName());
124         }
125 
126         for (int i = 0; i < app.getActivitiesCount(); i++) {
127             TestappProtos.Activity activityEntry = app.getActivities(i);
128             details.mActivities.add(ActivityInfo.builder()
129                     .activityClass(activityEntry.getName())
130                     .exported(activityEntry.getExported())
131                     .intentFilters(intentFilterSetFromProtoList(
132                             activityEntry.getIntentFiltersList()))
133                     .permission(activityEntry.getPermission().equals("") ? null
134                             : activityEntry.getPermission())
135                     .build());
136         }
137 
138         for (int i = 0; i < app.getServicesCount(); i++) {
139             TestappProtos.Service serviceEntry = app.getServices(i);
140             details.mServices.add(ServiceInfo.builder()
141                     .serviceClass(serviceEntry.getName())
142                     .intentFilters(intentFilterSetFromProtoList(
143                             serviceEntry.getIntentFiltersList()))
144                     .build());
145         }
146 
147         mTestApps.add(details);
148     }
149 
intentFilterSetFromProtoList( List<TestappProtos.IntentFilter> list)150     private Set<IntentFilter> intentFilterSetFromProtoList(
151             List<TestappProtos.IntentFilter> list) {
152         Set<IntentFilter> filterInfoSet = new HashSet<>();
153 
154         for (TestappProtos.IntentFilter filter : list) {
155             IntentFilter filterInfo = intentFilterFromProto(filter);
156             filterInfoSet.add(filterInfo);
157         }
158 
159         return filterInfoSet;
160     }
161 
intentFilterFromProto(TestappProtos.IntentFilter filterProto)162     private IntentFilter intentFilterFromProto(TestappProtos.IntentFilter filterProto) {
163         IntentFilter filter = new IntentFilter();
164 
165         for (String action : filterProto.getActionsList()) {
166             filter.addAction(action);
167         }
168         for (String category : filterProto.getCategoriesList()) {
169             filter.addCategory(category);
170         }
171 
172         return filter;
173     }
174 
getApkNameWithoutSuffix(String apkName)175     private String getApkNameWithoutSuffix(String apkName) {
176         return apkName.split("\\.", 2)[0];
177     }
178 
markTestAppUsed(TestAppDetails testApp)179     void markTestAppUsed(TestAppDetails testApp) {
180         mTestApps.remove(testApp);
181     }
182 }
183