1 /*
2  * Copyright 2016, 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.managedprovisioning.model;
18 
19 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION;
20 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER;
21 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM;
22 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM;
23 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE;
24 import static com.android.internal.util.Preconditions.checkNotNull;
25 
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.os.PersistableBundle;
29 import android.support.annotation.Nullable;
30 import android.text.TextUtils;
31 import com.android.internal.annotations.Immutable;
32 import com.android.managedprovisioning.common.PersistableBundlable;
33 import com.android.managedprovisioning.common.StoreUtils;
34 import java.io.IOException;
35 import java.util.Arrays;
36 import java.util.Objects;
37 import org.xmlpull.v1.XmlPullParser;
38 import org.xmlpull.v1.XmlPullParserException;
39 import org.xmlpull.v1.XmlSerializer;
40 
41 /**
42  * Stores the device admin package download information.
43  */
44 @Immutable
45 public final class PackageDownloadInfo extends PersistableBundlable {
46     public static final byte[] DEFAULT_PACKAGE_CHECKSUM = new byte[0];
47     public static final byte[] DEFAULT_SIGNATURE_CHECKSUM = new byte[0];
48     public static final boolean DEFAULT_PACKAGE_CHECKSUM_SUPPORTS_SHA1 = false;
49     // Always download packages if no minimum version given.
50     public static final int DEFAULT_MINIMUM_VERSION = Integer.MAX_VALUE;
51 
52     private static final String TAG_PROVISIONING_DEVICE_ADMIN_SUPPORT_SHA1_PACKAGE_CHECKSUM
53             = "supports-sha1-checksum";
54 
55     public static final Parcelable.Creator<PackageDownloadInfo> CREATOR
56             = new Parcelable.Creator<PackageDownloadInfo>() {
57         @Override
58         public PackageDownloadInfo createFromParcel(Parcel in) {
59             return new PackageDownloadInfo(in);
60         }
61 
62         @Override
63         public PackageDownloadInfo[] newArray(int size) {
64             return new PackageDownloadInfo[size];
65         }
66     };
67 
68     /**
69      * Url where the package (.apk) can be downloaded from. {@code null} if there is no download
70      * location specified.
71      */
72     public final String location;
73     /** Cookie header for http request. */
74     @Nullable
75     public final String cookieHeader;
76     /**
77      * One of the following two checksums should be non empty. SHA-256 or SHA-1 hash of the
78      * .apk file, or empty array if not used.
79      */
80     public final byte[] packageChecksum;
81     /** SHA-256 hash of the signature in the .apk file, or empty array if not used. */
82     public final byte[] signatureChecksum;
83     /** Minimum supported version code of the downloaded package. */
84     public final int minVersion;
85     /**
86      * If this is false, packageChecksum can only be SHA-256 hash, otherwise SHA-1 is also
87      * supported.
88      */
89     public final boolean packageChecksumSupportsSha1;
90 
PackageDownloadInfo(Builder builder)91     private PackageDownloadInfo(Builder builder) {
92         location = builder.mLocation;
93         cookieHeader = builder.mCookieHeader;
94         packageChecksum = checkNotNull(builder.mPackageChecksum, "package checksum can't be null");
95         signatureChecksum = checkNotNull(builder.mSignatureChecksum,
96                 "signature checksum can't be null");
97         minVersion = builder.mMinVersion;
98         packageChecksumSupportsSha1 = builder.mPackageChecksumSupportsSha1;
99 
100         validateFields();
101     }
102 
PackageDownloadInfo(Parcel in)103     private PackageDownloadInfo(Parcel in) {
104         this(createBuilderFromPersistableBundle(
105                 PersistableBundlable.getPersistableBundleFromParcel(in)));
106     }
107 
validateFields()108     private void validateFields() {
109         if (TextUtils.isEmpty(location)) {
110             throw new IllegalArgumentException("Download location must not be empty.");
111         }
112         if (packageChecksum.length == 0 && signatureChecksum.length == 0) {
113             throw new IllegalArgumentException("Package checksum or signature checksum must be "
114                     + "provided.");
115         }
116     }
117 
fromPersistableBundle(PersistableBundle bundle)118     /* package */ static PackageDownloadInfo fromPersistableBundle(PersistableBundle bundle) {
119         return createBuilderFromPersistableBundle(bundle).build();
120     }
121 
createBuilderFromPersistableBundle(PersistableBundle bundle)122     private static Builder createBuilderFromPersistableBundle(PersistableBundle bundle) {
123         Builder builder = new Builder();
124         builder.setMinVersion(bundle.getInt(EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE));
125         builder.setLocation(bundle.getString(
126                 EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION));
127         builder.setCookieHeader(bundle.getString(
128                 EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER));
129         builder.setPackageChecksum(StoreUtils.stringToByteArray(bundle.getString(
130                 EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM)));
131         builder.setSignatureChecksum(StoreUtils.stringToByteArray(bundle.getString(
132                 EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM)));
133         builder.setPackageChecksumSupportsSha1(bundle.getBoolean(
134                 TAG_PROVISIONING_DEVICE_ADMIN_SUPPORT_SHA1_PACKAGE_CHECKSUM));
135         return builder;
136     }
137 
138     @Override
toPersistableBundle()139     public PersistableBundle toPersistableBundle() {
140         final PersistableBundle bundle = new PersistableBundle();
141         bundle.putInt(EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE,
142                 minVersion);
143         bundle.putString(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION, location);
144         bundle.putString(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER,
145                 cookieHeader);
146         bundle.putString(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM,
147                 StoreUtils.byteArrayToString(packageChecksum));
148         bundle.putString(EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM,
149                 StoreUtils.byteArrayToString(signatureChecksum));
150         bundle.putBoolean(TAG_PROVISIONING_DEVICE_ADMIN_SUPPORT_SHA1_PACKAGE_CHECKSUM,
151                 packageChecksumSupportsSha1);
152         return bundle;
153     }
154 
155     public final static class Builder {
156         private String mLocation;
157         private String mCookieHeader;
158         private byte[] mPackageChecksum = DEFAULT_PACKAGE_CHECKSUM;
159         private byte[] mSignatureChecksum = DEFAULT_SIGNATURE_CHECKSUM;
160         private int mMinVersion = DEFAULT_MINIMUM_VERSION;
161         private boolean mPackageChecksumSupportsSha1 = DEFAULT_PACKAGE_CHECKSUM_SUPPORTS_SHA1;
162 
setLocation(String location)163         public Builder setLocation(String location) {
164             mLocation = location;
165             return this;
166         }
167 
setCookieHeader(String cookieHeader)168         public Builder setCookieHeader(String cookieHeader) {
169             mCookieHeader = cookieHeader;
170             return this;
171         }
172 
setPackageChecksum(byte[] packageChecksum)173         public Builder setPackageChecksum(byte[] packageChecksum) {
174             mPackageChecksum = packageChecksum;
175             return this;
176         }
177 
setSignatureChecksum(byte[] signatureChecksum)178         public Builder setSignatureChecksum(byte[] signatureChecksum) {
179             mSignatureChecksum = signatureChecksum;
180             return this;
181         }
182 
setMinVersion(int minVersion)183         public Builder setMinVersion(int minVersion) {
184             mMinVersion = minVersion;
185             return this;
186         }
187 
188         // TODO: remove once SHA-1 is fully deprecated.
setPackageChecksumSupportsSha1(boolean packageChecksumSupportsSha1)189         public Builder setPackageChecksumSupportsSha1(boolean packageChecksumSupportsSha1) {
190             mPackageChecksumSupportsSha1 = packageChecksumSupportsSha1;
191             return this;
192         }
193 
build()194         public PackageDownloadInfo build() {
195             return new PackageDownloadInfo(this);
196         }
197 
builder()198         public static Builder builder() {
199             return new Builder();
200         }
201     }
202 }
203