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
17package com.android.managedprovisioning.model;
18
19import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION;
20import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER;
21import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM;
22import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM;
23import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE;
24import static com.android.internal.util.Preconditions.checkNotNull;
25
26import android.os.Parcel;
27import android.os.Parcelable;
28import android.os.PersistableBundle;
29import android.support.annotation.Nullable;
30import android.text.TextUtils;
31import com.android.internal.annotations.Immutable;
32import com.android.managedprovisioning.common.PersistableBundlable;
33import com.android.managedprovisioning.common.StoreUtils;
34import java.io.IOException;
35import java.util.Arrays;
36import java.util.Objects;
37import org.xmlpull.v1.XmlPullParser;
38import org.xmlpull.v1.XmlPullParserException;
39import org.xmlpull.v1.XmlSerializer;
40
41/**
42 * Stores the device admin package download information.
43 */
44@Immutable
45public 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
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
103    private PackageDownloadInfo(Parcel in) {
104        this(createBuilderFromPersistableBundle(
105                PersistableBundlable.getPersistableBundleFromParcel(in)));
106    }
107
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
118    /* package */ static PackageDownloadInfo fromPersistableBundle(PersistableBundle bundle) {
119        return createBuilderFromPersistableBundle(bundle).build();
120    }
121
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
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
163        public Builder setLocation(String location) {
164            mLocation = location;
165            return this;
166        }
167
168        public Builder setCookieHeader(String cookieHeader) {
169            mCookieHeader = cookieHeader;
170            return this;
171        }
172
173        public Builder setPackageChecksum(byte[] packageChecksum) {
174            mPackageChecksum = packageChecksum;
175            return this;
176        }
177
178        public Builder setSignatureChecksum(byte[] signatureChecksum) {
179            mSignatureChecksum = signatureChecksum;
180            return this;
181        }
182
183        public Builder setMinVersion(int minVersion) {
184            mMinVersion = minVersion;
185            return this;
186        }
187
188        // TODO: remove once SHA-1 is fully deprecated.
189        public Builder setPackageChecksumSupportsSha1(boolean packageChecksumSupportsSha1) {
190            mPackageChecksumSupportsSha1 = packageChecksumSupportsSha1;
191            return this;
192        }
193
194        public PackageDownloadInfo build() {
195            return new PackageDownloadInfo(this);
196        }
197
198        public static Builder builder() {
199            return new Builder();
200        }
201    }
202}
203