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