1 /*
2  * Copyright (C) 2017 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 android.content.pm.cts;
18 
19 import static android.content.pm.PackageInfo.INSTALL_LOCATION_AUTO;
20 import static android.content.pm.PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
21 import static android.content.pm.PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL;
22 import static android.content.pm.PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
23 import static android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL;
24 import static android.content.pm.PackageInstaller.SessionParams.MODE_INHERIT_EXISTING;
25 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
26 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
27 import static android.content.pm.PackageManager.INSTALL_REASON_POLICY;
28 import static android.content.pm.PackageManager.INSTALL_REASON_UNKNOWN;
29 import static android.content.pm.PackageManager.INSTALL_REASON_USER;
30 import static android.content.pm.PackageManager.INSTALL_SCENARIO_BULK;
31 import static android.content.pm.PackageManager.INSTALL_SCENARIO_BULK_SECONDARY;
32 import static android.content.pm.PackageManager.INSTALL_SCENARIO_DEFAULT;
33 import static android.content.pm.PackageManager.INSTALL_SCENARIO_FAST;
34 
35 import static com.google.common.truth.Truth.assertThat;
36 
37 import static org.junit.Assert.fail;
38 
39 import android.content.pm.PackageInstaller;
40 import android.content.pm.PackageInstaller.SessionInfo;
41 import android.content.pm.PackageInstaller.SessionParams;
42 import android.content.pm.cts.util.AbandonAllPackageSessionsRule;
43 import android.graphics.Bitmap;
44 import android.net.Uri;
45 import android.platform.test.annotations.AppModeFull;
46 import android.util.Log;
47 
48 import androidx.test.InstrumentationRegistry;
49 
50 import org.junit.After;
51 import org.junit.Before;
52 import org.junit.Rule;
53 import org.junit.Test;
54 import org.junit.runner.RunWith;
55 import org.junit.runners.Parameterized;
56 
57 import java.util.ArrayList;
58 import java.util.Collection;
59 import java.util.List;
60 import java.util.function.Consumer;
61 
62 @RunWith(Parameterized.class)
63 @AppModeFull // TODO(Instant) Figure out which APIs should work.
64 public class InstallSessionParamsUnitTest {
65     private static final String LOG_TAG = InstallSessionParamsUnitTest.class.getSimpleName();
66     private static Optional UNSET = new Optional(false, null);
67 
68     @Rule
69     public AbandonAllPackageSessionsRule mAbandonSessionsRule = new AbandonAllPackageSessionsRule();
70 
71     @Parameterized.Parameter(0)
72     public Optional<Integer> mode;
73     @Parameterized.Parameter(1)
74     public Optional<Integer> installLocation;
75     @Parameterized.Parameter(2)
76     public Optional<Integer> size;
77     @Parameterized.Parameter(3)
78     public Optional<String> appPackageName;
79     @Parameterized.Parameter(4)
80     public Optional<Bitmap> appIcon;
81     @Parameterized.Parameter(5)
82     public Optional<String> appLabel;
83     @Parameterized.Parameter(6)
84     public Optional<Uri> originatingUri;
85     @Parameterized.Parameter(7)
86     public Optional<Integer> originatingUid;
87     @Parameterized.Parameter(8)
88     public Optional<Uri> referredUri;
89     @Parameterized.Parameter(9)
90     public Optional<Integer> installReason;
91     @Parameterized.Parameter(10)
92     public Optional<Integer> installScenario;
93     @Parameterized.Parameter(11)
94     public Optional<Integer> packageSource;
95     @Parameterized.Parameter(12)
96     public boolean expectFailure;
97 
98     private PackageInstaller mInstaller = InstrumentationRegistry.getInstrumentation()
99             .getTargetContext()
100             .getPackageManager()
101             .getPackageInstaller();
102 
103     /**
104      * Generate test-parameters where all params are the same, but one param cycles through all
105      * values.
106      */
getSingleParameterChangingTests( Object[][][] allParameterValues, int changingParameterIndex, Object[] changingParameterValues, boolean expectFailure)107     private static ArrayList<Object[]> getSingleParameterChangingTests(
108             Object[][][] allParameterValues, int changingParameterIndex,
109             Object[] changingParameterValues, boolean expectFailure) {
110         ArrayList<Object[]> params = new ArrayList<>();
111 
112         for (Object changingParameterValue : changingParameterValues) {
113             ArrayList<Object> singleTestParams = new ArrayList<>();
114 
115             // parameterIndex is the index of the parameter (0 = mode, ...)
116             for (int parameterIndex = 0; parameterIndex < allParameterValues.length;
117                     parameterIndex++) {
118                 Object[][] parameterValues = allParameterValues[parameterIndex];
119 
120                 if (parameterIndex == changingParameterIndex) {
121                     if (changingParameterValue == UNSET) {
122                         // No need to wrap UNSET again
123                         singleTestParams.add(UNSET);
124                     } else {
125                         singleTestParams.add(Optional.of(changingParameterValue));
126                     }
127                 } else {
128                     singleTestParams.add(Optional.of(parameterValues[0][0]));
129                 }
130             }
131             singleTestParams.add(expectFailure);
132             params.add(singleTestParams.toArray());
133         }
134 
135         return params;
136     }
137 
138     /**
139      * Generate test-parameters for all tests.
140      */
141     @Parameterized.Parameters
getParameters()142     public static Collection<Object[]> getParameters() {
143         // {{{valid parameters}, {invalid parameters}}}
144         Object[][][] allParameterValues = {
145          /*mode*/
146                 {{MODE_FULL_INSTALL, MODE_INHERIT_EXISTING}, {0xfff}},
147          /*installLocation*/
148                 {{INSTALL_LOCATION_UNSPECIFIED, INSTALL_LOCATION_AUTO,
149                         INSTALL_LOCATION_INTERNAL_ONLY, INSTALL_LOCATION_PREFER_EXTERNAL,
150                         /* parame is not verified */ 0xfff}, {}},
151          /*size*/
152                 {{1, 8092, Integer.MAX_VALUE, /* param is not verified */ -1, 0}, {}},
153          /*appPackageName*/
154                 {{"a.package.name", null, /* param is not verified */ "android"}, {}},
155          /*appIcon*/
156                 {{null, Bitmap.createBitmap(42, 42, Bitmap.Config.ARGB_8888)}, {}},
157          /*appLabel*/
158                 {{"A label", null}, {}},
159          /*originatingUri*/
160                 {{Uri.parse("android.com"), null}, {}},
161          /*originatingUid*/
162                 {{-1, 0, 1}, {}},
163          /*referredUri*/
164                 {{Uri.parse("android.com"), null}, {}},
165          /*installReason*/
166                 {{INSTALL_REASON_UNKNOWN, INSTALL_REASON_POLICY, INSTALL_REASON_DEVICE_RESTORE,
167                         INSTALL_REASON_DEVICE_SETUP, INSTALL_REASON_USER,
168                         /* parame is not verified */ 0xfff}, {}},
169          /*installScenario*/
170                 {{INSTALL_SCENARIO_DEFAULT, INSTALL_SCENARIO_FAST, INSTALL_SCENARIO_BULK,
171                         INSTALL_SCENARIO_BULK_SECONDARY}, {}},
172          /*packageSource*/
173                 {{PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED,
174                         PackageInstaller.PACKAGE_SOURCE_OTHER,
175                         PackageInstaller.PACKAGE_SOURCE_STORE,
176                         PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE,
177                         PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE}, {}}
178         };
179 
180         ArrayList<Object[]> allTestParams = new ArrayList<>();
181 
182         // changingParameterIndex is the index the parameter that changes (0 = mode ...)
183         for (int changingParameterIndex = 0; changingParameterIndex < allParameterValues.length;
184                 changingParameterIndex++) {
185             // Allowed values
186             allTestParams.addAll(getSingleParameterChangingTests(allParameterValues,
187                     changingParameterIndex, allParameterValues[changingParameterIndex][0], false));
188 
189             // Value unset (mode param cannot be unset)
190             if (changingParameterIndex != 0) {
191                 Object[] unset = {UNSET};
192                 allTestParams.addAll(getSingleParameterChangingTests(allParameterValues,
193                         changingParameterIndex, unset, false));
194             }
195 
196             // Illegal values
197             allTestParams.addAll(getSingleParameterChangingTests(allParameterValues,
198                     changingParameterIndex, allParameterValues[changingParameterIndex][1], true));
199         }
200 
201         return allTestParams;
202     }
203 
204     /**
205      * Get the sessionInfo if this package owns the session.
206      *
207      * @param sessionId The id of the session
208      *
209      * @return The {@link PackageInstaller.SessionInfo} object, or {@code null} if session is not
210      * owned by the this package.
211      */
getSessionInfo(int sessionId)212     private SessionInfo getSessionInfo(int sessionId) {
213         List<SessionInfo> mySessionInfos = mInstaller.getMySessions();
214 
215         for (SessionInfo sessionInfo : mySessionInfos) {
216             if (sessionInfo.getSessionId() == sessionId) {
217                 return sessionInfo;
218             }
219         }
220 
221         return null;
222     }
223 
224     @Before
onBefore()225     public void onBefore() throws Exception {
226         PackageManagerShellCommandInstallTest.setSystemProperty(
227                 "debug.pm.install_skip_size_check_for_maxint", "1");
228     }
229 
230     @After
onAfter()231     public void onAfter() throws Exception {
232         // Set the test override to invalid.
233         PackageManagerShellCommandInstallTest.setSystemProperty(
234                 "debug.pm.install_skip_size_check_for_maxint", "invalid");
235     }
236 
237     @Test
checkSessionParams()238     public void checkSessionParams() throws Exception {
239         Log.i(LOG_TAG, "mode=" + mode + " installLocation=" + installLocation + " size=" + size
240                 + " appPackageName=" + appPackageName + " appIcon=" + appIcon + " appLabel="
241                 + appLabel + " originatingUri=" + originatingUri + " originatingUid="
242                 + originatingUid + " referredUri=" + referredUri + " installReason=" + installReason
243                 + " installScenario=" + installScenario + " expectFailure=" + expectFailure);
244 
245         SessionParams params = new SessionParams(mode.get());
246         installLocation.ifPresent(params::setInstallLocation);
247         size.ifPresent(params::setSize);
248         appPackageName.ifPresent(params::setAppPackageName);
249         appIcon.ifPresent(params::setAppIcon);
250         appLabel.ifPresent(params::setAppLabel);
251         originatingUri.ifPresent(params::setOriginatingUri);
252         originatingUid.ifPresent(params::setOriginatingUid);
253         referredUri.ifPresent(params::setReferrerUri);
254         installReason.ifPresent(params::setInstallReason);
255         installScenario.ifPresent(params::setInstallScenario);
256         packageSource.ifPresent(params::setPackageSource);
257 
258         int sessionId;
259         try {
260             sessionId = mInstaller.createSession(params);
261 
262             if (expectFailure) {
263                 fail("Creating session did not fail");
264             }
265         } catch (Exception e) {
266             if (expectFailure) {
267                 return;
268             }
269 
270             throw e;
271         }
272 
273         SessionInfo info = getSessionInfo(sessionId);
274 
275         assertThat(info.getMode()).isEqualTo(mode.get());
276         packageSource.ifPresent(i -> assertThat(info.getPackageSource()).isEqualTo(i));
277         installLocation.ifPresent(i -> assertThat(info.getInstallLocation()).isEqualTo(i));
278         size.ifPresent(i -> assertThat(info.getSize()).isEqualTo(i));
279         appPackageName.ifPresent(s -> assertThat(info.getAppPackageName()).isEqualTo(s));
280 
281         if (appIcon.isPresent()) {
282             if (appIcon.get() == null) {
283                 assertThat(info.getAppIcon()).isNull();
284             } else {
285                 assertThat(appIcon.get().sameAs(info.getAppIcon())).isTrue();
286             }
287         }
288 
289         appLabel.ifPresent(s -> assertThat(info.getAppLabel()).isEqualTo(s));
290         originatingUri.ifPresent(uri -> assertThat(info.getOriginatingUri()).isEqualTo(uri));
291         originatingUid.ifPresent(i -> assertThat(info.getOriginatingUid()).isEqualTo(i));
292         referredUri.ifPresent(uri -> assertThat(info.getReferrerUri()).isEqualTo(uri));
293         installReason.ifPresent(i -> assertThat(info.getInstallReason()).isEqualTo(i));
294     }
295 
296     /** Similar to java.util.Optional but distinguishing between null and unset */
297     private static class Optional<T> {
298         private final boolean mIsSet;
299         private final T mValue;
300 
Optional(boolean isSet, T value)301         Optional(boolean isSet, T value) {
302             mIsSet = isSet;
303             mValue = value;
304         }
305 
of(T value)306         static <T> Optional of(T value) {
307             return new Optional(true, value);
308         }
309 
get()310         T get() {
311             if (!mIsSet) {
312                 throw new IllegalStateException(this + " is not set");
313             }
314             return mValue;
315         }
316 
toString()317         public String toString() {
318             if (!mIsSet) {
319                 return "unset";
320             } else if (mValue == null) {
321                 return "null";
322             } else {
323                 return mValue.toString();
324             }
325         }
326 
isPresent()327         boolean isPresent() {
328             return mIsSet;
329         }
330 
ifPresent(Consumer<T> consumer)331         void ifPresent(Consumer<T> consumer) {
332             if (mIsSet) {
333                 consumer.accept(mValue);
334             }
335         }
336     }
337 }
338