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