Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 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; |
| 18 | |
| 19 | import android.annotation.NonNull; |
| 20 | import android.annotation.Nullable; |
| 21 | import android.annotation.SystemApi; |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 22 | import android.content.Intent; |
Patrick Baumann | 709ee15 | 2017-12-04 16:12:52 -0800 | [diff] [blame] | 23 | import android.os.Bundle; |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 24 | import android.os.Parcel; |
| 25 | import android.os.Parcelable; |
| 26 | |
| 27 | import java.security.MessageDigest; |
| 28 | import java.security.NoSuchAlgorithmException; |
Patrick Baumann | 4db6bc1 | 2018-02-06 09:55:36 -0800 | [diff] [blame] | 29 | import java.security.SecureRandom; |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 30 | import java.util.ArrayList; |
Patrick Baumann | 4db6bc1 | 2018-02-06 09:55:36 -0800 | [diff] [blame] | 31 | import java.util.Arrays; |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 32 | import java.util.Collections; |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 33 | import java.util.List; |
| 34 | import java.util.Locale; |
Patrick Baumann | 4db6bc1 | 2018-02-06 09:55:36 -0800 | [diff] [blame] | 35 | import java.util.Random; |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 36 | |
| 37 | /** |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 38 | * Describes an externally resolvable instant application. There are three states that this class |
| 39 | * can represent: <p/> |
| 40 | * <ul> |
| 41 | * <li> |
| 42 | * The first, usable only for non http/s intents, implies that the resolver cannot |
| 43 | * immediately resolve this intent and would prefer that resolution be deferred to the |
| 44 | * instant app installer. Represent this state with {@link #InstantAppResolveInfo(Bundle)}. |
| 45 | * If the {@link android.content.Intent} has the scheme set to http/s and a set of digest |
| 46 | * prefixes were passed into one of the resolve methods in |
| 47 | * {@link android.app.InstantAppResolverService}, this state cannot be used. |
| 48 | * </li> |
| 49 | * <li> |
| 50 | * The second represents a partial match and is constructed with any of the other |
| 51 | * constructors. By setting one or more of the {@link Nullable}arguments to null, you |
| 52 | * communicate to the resolver in response to |
| 53 | * {@link android.app.InstantAppResolverService#onGetInstantAppResolveInfo(Intent, int[], |
| 54 | * String, InstantAppResolverService.InstantAppResolutionCallback)} |
| 55 | * that you need a 2nd round of resolution to complete the request. |
| 56 | * </li> |
| 57 | * <li> |
| 58 | * The third represents a complete match and is constructed with all @Nullable parameters |
| 59 | * populated. |
| 60 | * </li> |
| 61 | * </ul> |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 62 | * @hide |
| 63 | */ |
| 64 | @SystemApi |
| 65 | public final class InstantAppResolveInfo implements Parcelable { |
| 66 | /** Algorithm that will be used to generate the domain digest */ |
Todd Kennedy | 877e979 | 2017-06-02 07:53:44 -0700 | [diff] [blame] | 67 | private static final String SHA_ALGORITHM = "SHA-256"; |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 68 | |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 69 | private static final byte[] EMPTY_DIGEST = new byte[0]; |
| 70 | |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 71 | private final InstantAppDigest mDigest; |
| 72 | private final String mPackageName; |
| 73 | /** The filters used to match domain */ |
| 74 | private final List<InstantAppIntentFilter> mFilters; |
| 75 | /** The version code of the app that this class resolves to */ |
Dianne Hackborn | 3accca0 | 2013-09-20 09:32:11 -0700 | [diff] [blame] | 76 | private final long mVersionCode; |
Patrick Baumann | 709ee15 | 2017-12-04 16:12:52 -0800 | [diff] [blame] | 77 | /** Data about the app that should be passed along to the Instant App installer on resolve */ |
| 78 | private final Bundle mExtras; |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 79 | /** |
| 80 | * A flag that indicates that the resolver is aware that an app may match, but would prefer |
Patrick Baumann | 3abf547 | 2018-03-01 09:58:23 -0800 | [diff] [blame] | 81 | * that the installer get the sanitized intent to decide. |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 82 | */ |
| 83 | private final boolean mShouldLetInstallerDecide; |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 84 | |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 85 | /** Constructor for intent-based InstantApp resolution results. */ |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 86 | public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName, |
Todd Kennedy | c0dd03a | 2017-05-05 17:15:38 +0000 | [diff] [blame] | 87 | @Nullable List<InstantAppIntentFilter> filters, int versionCode) { |
Patrick Baumann | 709ee15 | 2017-12-04 16:12:52 -0800 | [diff] [blame] | 88 | this(digest, packageName, filters, (long) versionCode, null /* extras */); |
Dianne Hackborn | 3accca0 | 2013-09-20 09:32:11 -0700 | [diff] [blame] | 89 | } |
| 90 | |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 91 | /** Constructor for intent-based InstantApp resolution results with extras. */ |
Dianne Hackborn | 3accca0 | 2013-09-20 09:32:11 -0700 | [diff] [blame] | 92 | public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName, |
Patrick Baumann | 709ee15 | 2017-12-04 16:12:52 -0800 | [diff] [blame] | 93 | @Nullable List<InstantAppIntentFilter> filters, long versionCode, |
| 94 | @Nullable Bundle extras) { |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 95 | this(digest, packageName, filters, versionCode, extras, false); |
| 96 | } |
| 97 | |
Patrick Baumann | 3abf547 | 2018-03-01 09:58:23 -0800 | [diff] [blame] | 98 | /** Constructor for intent-based InstantApp resolution results by hostname. */ |
| 99 | public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName, |
| 100 | @Nullable List<InstantAppIntentFilter> filters) { |
| 101 | this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/, |
| 102 | null /* extras */); |
| 103 | } |
| 104 | |
| 105 | /** |
| 106 | * Constructor that indicates that resolution could be delegated to the installer when the |
| 107 | * sanitized intent contains enough information to resolve completely. |
| 108 | */ |
| 109 | public InstantAppResolveInfo(@Nullable Bundle extras) { |
| 110 | this(InstantAppDigest.UNDEFINED, null, null, -1, extras, true); |
| 111 | } |
| 112 | |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 113 | private InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName, |
| 114 | @Nullable List<InstantAppIntentFilter> filters, long versionCode, |
| 115 | @Nullable Bundle extras, boolean shouldLetInstallerDecide) { |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 116 | // validate arguments |
| 117 | if ((packageName == null && (filters != null && filters.size() != 0)) |
| 118 | || (packageName != null && (filters == null || filters.size() == 0))) { |
| 119 | throw new IllegalArgumentException(); |
| 120 | } |
| 121 | mDigest = digest; |
| 122 | if (filters != null) { |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 123 | mFilters = new ArrayList<>(filters.size()); |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 124 | mFilters.addAll(filters); |
| 125 | } else { |
| 126 | mFilters = null; |
| 127 | } |
| 128 | mPackageName = packageName; |
Todd Kennedy | c0dd03a | 2017-05-05 17:15:38 +0000 | [diff] [blame] | 129 | mVersionCode = versionCode; |
Patrick Baumann | 709ee15 | 2017-12-04 16:12:52 -0800 | [diff] [blame] | 130 | mExtras = extras; |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 131 | mShouldLetInstallerDecide = shouldLetInstallerDecide; |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 132 | } |
| 133 | |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 134 | InstantAppResolveInfo(Parcel in) { |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 135 | mShouldLetInstallerDecide = in.readBoolean(); |
Patrick Baumann | 709ee15 | 2017-12-04 16:12:52 -0800 | [diff] [blame] | 136 | mExtras = in.readBundle(); |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 137 | if (mShouldLetInstallerDecide) { |
| 138 | mDigest = InstantAppDigest.UNDEFINED; |
| 139 | mPackageName = null; |
| 140 | mFilters = Collections.emptyList(); |
| 141 | mVersionCode = -1; |
| 142 | } else { |
| 143 | mDigest = in.readParcelable(null /*loader*/); |
| 144 | mPackageName = in.readString(); |
| 145 | mFilters = new ArrayList<>(); |
| 146 | in.readList(mFilters, null /*loader*/); |
| 147 | mVersionCode = in.readLong(); |
| 148 | } |
| 149 | } |
| 150 | |
Patrick Baumann | 3abf547 | 2018-03-01 09:58:23 -0800 | [diff] [blame] | 151 | /** |
| 152 | * Returns true if the resolver is aware that an app may match, but would prefer |
| 153 | * that the installer get the sanitized intent to decide. This should not be true for |
| 154 | * resolutions that include a host and will be ignored in such cases. |
| 155 | */ |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 156 | public boolean shouldLetInstallerDecide() { |
| 157 | return mShouldLetInstallerDecide; |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 158 | } |
| 159 | |
| 160 | public byte[] getDigestBytes() { |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 161 | return mDigest.mDigestBytes.length > 0 ? mDigest.getDigestBytes()[0] : EMPTY_DIGEST; |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 162 | } |
| 163 | |
| 164 | public int getDigestPrefix() { |
| 165 | return mDigest.getDigestPrefix()[0]; |
| 166 | } |
| 167 | |
| 168 | public String getPackageName() { |
| 169 | return mPackageName; |
| 170 | } |
| 171 | |
| 172 | public List<InstantAppIntentFilter> getIntentFilters() { |
| 173 | return mFilters; |
| 174 | } |
| 175 | |
Dianne Hackborn | 3accca0 | 2013-09-20 09:32:11 -0700 | [diff] [blame] | 176 | /** |
| 177 | * @deprecated Use {@link #getLongVersionCode} instead. |
| 178 | */ |
| 179 | @Deprecated |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 180 | public int getVersionCode() { |
Dianne Hackborn | 3accca0 | 2013-09-20 09:32:11 -0700 | [diff] [blame] | 181 | return (int) (mVersionCode & 0xffffffff); |
| 182 | } |
| 183 | |
| 184 | public long getLongVersionCode() { |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 185 | return mVersionCode; |
| 186 | } |
| 187 | |
Patrick Baumann | 709ee15 | 2017-12-04 16:12:52 -0800 | [diff] [blame] | 188 | @Nullable |
| 189 | public Bundle getExtras() { |
| 190 | return mExtras; |
| 191 | } |
| 192 | |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 193 | @Override |
| 194 | public int describeContents() { |
| 195 | return 0; |
| 196 | } |
| 197 | |
| 198 | @Override |
| 199 | public void writeToParcel(Parcel out, int flags) { |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 200 | out.writeBoolean(mShouldLetInstallerDecide); |
| 201 | out.writeBundle(mExtras); |
| 202 | if (mShouldLetInstallerDecide) { |
| 203 | return; |
| 204 | } |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 205 | out.writeParcelable(mDigest, flags); |
| 206 | out.writeString(mPackageName); |
| 207 | out.writeList(mFilters); |
Dianne Hackborn | 3accca0 | 2013-09-20 09:32:11 -0700 | [diff] [blame] | 208 | out.writeLong(mVersionCode); |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 209 | } |
| 210 | |
Jeff Sharkey | 9e8f83d | 2019-02-28 12:06:45 -0700 | [diff] [blame] | 211 | public static final @android.annotation.NonNull Parcelable.Creator<InstantAppResolveInfo> CREATOR |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 212 | = new Parcelable.Creator<InstantAppResolveInfo>() { |
| 213 | public InstantAppResolveInfo createFromParcel(Parcel in) { |
| 214 | return new InstantAppResolveInfo(in); |
| 215 | } |
| 216 | |
| 217 | public InstantAppResolveInfo[] newArray(int size) { |
| 218 | return new InstantAppResolveInfo[size]; |
| 219 | } |
| 220 | }; |
| 221 | |
| 222 | /** |
| 223 | * Helper class to generate and store each of the digests and prefixes |
| 224 | * sent to the Instant App Resolver. |
| 225 | * <p> |
| 226 | * Since intent filters may want to handle multiple hosts within a |
| 227 | * domain [eg “*.google.com”], the resolver is presented with multiple |
| 228 | * hash prefixes. For example, "a.b.c.d.e" generates digests for |
| 229 | * "d.e", "c.d.e", "b.c.d.e" and "a.b.c.d.e". |
| 230 | * |
| 231 | * @hide |
| 232 | */ |
| 233 | @SystemApi |
| 234 | public static final class InstantAppDigest implements Parcelable { |
Patrick Baumann | 4db6bc1 | 2018-02-06 09:55:36 -0800 | [diff] [blame] | 235 | static final int DIGEST_MASK = 0xfffff000; |
Patrick Baumann | 3abf547 | 2018-03-01 09:58:23 -0800 | [diff] [blame] | 236 | |
| 237 | /** |
| 238 | * A special instance that represents and undefined digest used for cases that a host was |
| 239 | * not provided or is irrelevant to the response. |
| 240 | */ |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 241 | public static final InstantAppDigest UNDEFINED = |
| 242 | new InstantAppDigest(new byte[][]{}, new int[]{}); |
Patrick Baumann | 4db6bc1 | 2018-02-06 09:55:36 -0800 | [diff] [blame] | 243 | |
| 244 | private static Random sRandom = null; |
| 245 | static { |
| 246 | try { |
| 247 | sRandom = SecureRandom.getInstance("SHA1PRNG"); |
| 248 | } catch (NoSuchAlgorithmException e) { |
| 249 | // oh well |
| 250 | sRandom = new Random(); |
| 251 | } |
| 252 | } |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 253 | /** Full digest of the domain hashes */ |
| 254 | private final byte[][] mDigestBytes; |
Patrick Baumann | 4db6bc1 | 2018-02-06 09:55:36 -0800 | [diff] [blame] | 255 | /** The first 5 bytes of the domain hashes */ |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 256 | private final int[] mDigestPrefix; |
Patrick Baumann | 4db6bc1 | 2018-02-06 09:55:36 -0800 | [diff] [blame] | 257 | /** The first 5 bytes of the domain hashes interspersed with random data */ |
| 258 | private int[] mDigestPrefixSecure; |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 259 | |
| 260 | public InstantAppDigest(@NonNull String hostName) { |
| 261 | this(hostName, -1 /*maxDigests*/); |
| 262 | } |
| 263 | |
| 264 | /** @hide */ |
| 265 | public InstantAppDigest(@NonNull String hostName, int maxDigests) { |
| 266 | if (hostName == null) { |
| 267 | throw new IllegalArgumentException(); |
| 268 | } |
| 269 | mDigestBytes = generateDigest(hostName.toLowerCase(Locale.ENGLISH), maxDigests); |
| 270 | mDigestPrefix = new int[mDigestBytes.length]; |
| 271 | for (int i = 0; i < mDigestBytes.length; i++) { |
| 272 | mDigestPrefix[i] = |
| 273 | ((mDigestBytes[i][0] & 0xFF) << 24 |
| 274 | | (mDigestBytes[i][1] & 0xFF) << 16 |
| 275 | | (mDigestBytes[i][2] & 0xFF) << 8 |
| 276 | | (mDigestBytes[i][3] & 0xFF) << 0) |
| 277 | & DIGEST_MASK; |
| 278 | } |
| 279 | } |
| 280 | |
Patrick Baumann | 577d402 | 2018-01-31 16:55:10 +0000 | [diff] [blame] | 281 | private InstantAppDigest(byte[][] digestBytes, int[] prefix) { |
| 282 | this.mDigestPrefix = prefix; |
| 283 | this.mDigestBytes = digestBytes; |
| 284 | } |
| 285 | |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 286 | private static byte[][] generateDigest(String hostName, int maxDigests) { |
| 287 | ArrayList<byte[]> digests = new ArrayList<>(); |
| 288 | try { |
| 289 | final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM); |
| 290 | if (maxDigests <= 0) { |
| 291 | final byte[] hostBytes = hostName.getBytes(); |
| 292 | digests.add(digest.digest(hostBytes)); |
| 293 | } else { |
| 294 | int prevDot = hostName.lastIndexOf('.'); |
| 295 | prevDot = hostName.lastIndexOf('.', prevDot - 1); |
| 296 | // shortcut for short URLs |
| 297 | if (prevDot < 0) { |
| 298 | digests.add(digest.digest(hostName.getBytes())); |
| 299 | } else { |
| 300 | byte[] hostBytes = |
| 301 | hostName.substring(prevDot + 1, hostName.length()).getBytes(); |
| 302 | digests.add(digest.digest(hostBytes)); |
| 303 | int digestCount = 1; |
| 304 | while (prevDot >= 0 && digestCount < maxDigests) { |
| 305 | prevDot = hostName.lastIndexOf('.', prevDot - 1); |
| 306 | hostBytes = |
| 307 | hostName.substring(prevDot + 1, hostName.length()).getBytes(); |
| 308 | digests.add(digest.digest(hostBytes)); |
| 309 | digestCount++; |
| 310 | } |
| 311 | } |
| 312 | } |
| 313 | } catch (NoSuchAlgorithmException e) { |
| 314 | throw new IllegalStateException("could not find digest algorithm"); |
| 315 | } |
| 316 | return digests.toArray(new byte[digests.size()][]); |
| 317 | } |
| 318 | |
| 319 | InstantAppDigest(Parcel in) { |
| 320 | final int digestCount = in.readInt(); |
| 321 | if (digestCount == -1) { |
| 322 | mDigestBytes = null; |
| 323 | } else { |
| 324 | mDigestBytes = new byte[digestCount][]; |
| 325 | for (int i = 0; i < digestCount; i++) { |
| 326 | mDigestBytes[i] = in.createByteArray(); |
| 327 | } |
| 328 | } |
| 329 | mDigestPrefix = in.createIntArray(); |
Patrick Baumann | 4db6bc1 | 2018-02-06 09:55:36 -0800 | [diff] [blame] | 330 | mDigestPrefixSecure = in.createIntArray(); |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 331 | } |
| 332 | |
| 333 | public byte[][] getDigestBytes() { |
| 334 | return mDigestBytes; |
| 335 | } |
| 336 | |
| 337 | public int[] getDigestPrefix() { |
| 338 | return mDigestPrefix; |
| 339 | } |
| 340 | |
Patrick Baumann | 4db6bc1 | 2018-02-06 09:55:36 -0800 | [diff] [blame] | 341 | /** |
| 342 | * Returns a digest prefix with additional random prefixes interspersed. |
| 343 | * @hide |
| 344 | */ |
| 345 | public int[] getDigestPrefixSecure() { |
| 346 | if (this == InstantAppResolveInfo.InstantAppDigest.UNDEFINED) { |
| 347 | return getDigestPrefix(); |
| 348 | } else if (mDigestPrefixSecure == null) { |
| 349 | // let's generate some random data to intersperse throughout the set of prefixes |
| 350 | final int realSize = getDigestPrefix().length; |
| 351 | final int manufacturedSize = realSize + 10 + sRandom.nextInt(10); |
| 352 | mDigestPrefixSecure = Arrays.copyOf(getDigestPrefix(), manufacturedSize); |
| 353 | for (int i = realSize; i < manufacturedSize; i++) { |
| 354 | mDigestPrefixSecure[i] = sRandom.nextInt() & DIGEST_MASK; |
| 355 | } |
| 356 | Arrays.sort(mDigestPrefixSecure); |
| 357 | } |
| 358 | return mDigestPrefixSecure; |
| 359 | } |
| 360 | |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 361 | @Override |
| 362 | public int describeContents() { |
| 363 | return 0; |
| 364 | } |
| 365 | |
| 366 | @Override |
| 367 | public void writeToParcel(Parcel out, int flags) { |
Patrick Baumann | 4db6bc1 | 2018-02-06 09:55:36 -0800 | [diff] [blame] | 368 | final boolean isUndefined = this == UNDEFINED; |
| 369 | out.writeBoolean(isUndefined); |
| 370 | if (isUndefined) { |
| 371 | return; |
| 372 | } |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 373 | if (mDigestBytes == null) { |
| 374 | out.writeInt(-1); |
| 375 | } else { |
| 376 | out.writeInt(mDigestBytes.length); |
| 377 | for (int i = 0; i < mDigestBytes.length; i++) { |
| 378 | out.writeByteArray(mDigestBytes[i]); |
| 379 | } |
| 380 | } |
| 381 | out.writeIntArray(mDigestPrefix); |
Patrick Baumann | 4db6bc1 | 2018-02-06 09:55:36 -0800 | [diff] [blame] | 382 | out.writeIntArray(mDigestPrefixSecure); |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 383 | } |
| 384 | |
| 385 | @SuppressWarnings("hiding") |
Jeff Sharkey | 9e8f83d | 2019-02-28 12:06:45 -0700 | [diff] [blame] | 386 | public static final @android.annotation.NonNull Parcelable.Creator<InstantAppDigest> CREATOR = |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 387 | new Parcelable.Creator<InstantAppDigest>() { |
| 388 | @Override |
| 389 | public InstantAppDigest createFromParcel(Parcel in) { |
Patrick Baumann | 4db6bc1 | 2018-02-06 09:55:36 -0800 | [diff] [blame] | 390 | if (in.readBoolean() /* is undefined */) { |
| 391 | return UNDEFINED; |
| 392 | } |
Todd Kennedy | 1fb3404 | 2017-03-01 13:56:58 -0800 | [diff] [blame] | 393 | return new InstantAppDigest(in); |
| 394 | } |
| 395 | @Override |
| 396 | public InstantAppDigest[] newArray(int size) { |
| 397 | return new InstantAppDigest[size]; |
| 398 | } |
| 399 | }; |
| 400 | } |
| 401 | } |