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.app.admin; 18 19 import android.annotation.IntDef; 20 import android.annotation.Nullable; 21 import android.os.Build; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 25 import org.xmlpull.v1.XmlPullParser; 26 import org.xmlpull.v1.XmlSerializer; 27 28 import java.io.IOException; 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 import java.util.Objects; 32 33 /** 34 * A class containing information about a pending system update. 35 */ 36 public final class SystemUpdateInfo implements Parcelable { 37 38 /** 39 * Represents it is unknown whether the system update is a security patch. 40 */ 41 public static final int SECURITY_PATCH_STATE_UNKNOWN = 0; 42 43 /** 44 * Represents the system update is not a security patch. 45 */ 46 public static final int SECURITY_PATCH_STATE_FALSE = 1; 47 48 /** 49 * Represents the system update is a security patch. 50 */ 51 public static final int SECURITY_PATCH_STATE_TRUE = 2; 52 53 /** @hide */ 54 @Retention(RetentionPolicy.SOURCE) 55 @IntDef(prefix = { "SECURITY_PATCH_STATE_" }, value = { 56 SECURITY_PATCH_STATE_FALSE, 57 SECURITY_PATCH_STATE_TRUE, 58 SECURITY_PATCH_STATE_UNKNOWN 59 }) 60 public @interface SecurityPatchState {} 61 62 private static final String ATTR_RECEIVED_TIME = "received-time"; 63 private static final String ATTR_SECURITY_PATCH_STATE = "security-patch-state"; 64 // Tag used to store original build fingerprint to detect when the update is applied. 65 private static final String ATTR_ORIGINAL_BUILD = "original-build"; 66 67 private final long mReceivedTime; 68 @SecurityPatchState 69 private final int mSecurityPatchState; 70 SystemUpdateInfo(long receivedTime, @SecurityPatchState int securityPatchState)71 private SystemUpdateInfo(long receivedTime, @SecurityPatchState int securityPatchState) { 72 this.mReceivedTime = receivedTime; 73 this.mSecurityPatchState = securityPatchState; 74 } 75 SystemUpdateInfo(Parcel in)76 private SystemUpdateInfo(Parcel in) { 77 mReceivedTime = in.readLong(); 78 mSecurityPatchState = in.readInt(); 79 } 80 81 /** @hide */ 82 @Nullable of(long receivedTime)83 public static SystemUpdateInfo of(long receivedTime) { 84 return receivedTime == -1 85 ? null : new SystemUpdateInfo(receivedTime, SECURITY_PATCH_STATE_UNKNOWN); 86 } 87 88 /** @hide */ 89 @Nullable of(long receivedTime, boolean isSecurityPatch)90 public static SystemUpdateInfo of(long receivedTime, boolean isSecurityPatch) { 91 return receivedTime == -1 ? null : new SystemUpdateInfo(receivedTime, 92 isSecurityPatch ? SECURITY_PATCH_STATE_TRUE : SECURITY_PATCH_STATE_FALSE); 93 } 94 95 /** 96 * Gets time when the update was first available in milliseconds since midnight, January 1, 97 * 1970 UTC. 98 * @return Time in milliseconds as given by {@link System#currentTimeMillis()} 99 */ getReceivedTime()100 public long getReceivedTime() { 101 return mReceivedTime; 102 } 103 104 /** 105 * Gets whether the update is a security patch. 106 * @return {@link #SECURITY_PATCH_STATE_FALSE}, {@link #SECURITY_PATCH_STATE_TRUE}, or 107 * {@link #SECURITY_PATCH_STATE_UNKNOWN}. 108 */ 109 @SecurityPatchState getSecurityPatchState()110 public int getSecurityPatchState() { 111 return mSecurityPatchState; 112 } 113 114 public static final Creator<SystemUpdateInfo> CREATOR = 115 new Creator<SystemUpdateInfo>() { 116 @Override 117 public SystemUpdateInfo createFromParcel(Parcel in) { 118 return new SystemUpdateInfo(in); 119 } 120 121 @Override 122 public SystemUpdateInfo[] newArray(int size) { 123 return new SystemUpdateInfo[size]; 124 } 125 }; 126 127 /** @hide */ writeToXml(XmlSerializer out, String tag)128 public void writeToXml(XmlSerializer out, String tag) throws IOException { 129 out.startTag(null, tag); 130 out.attribute(null, ATTR_RECEIVED_TIME, String.valueOf(mReceivedTime)); 131 out.attribute(null, ATTR_SECURITY_PATCH_STATE, String.valueOf(mSecurityPatchState)); 132 out.attribute(null, ATTR_ORIGINAL_BUILD , Build.FINGERPRINT); 133 out.endTag(null, tag); 134 } 135 136 /** @hide */ 137 @Nullable readFromXml(XmlPullParser parser)138 public static SystemUpdateInfo readFromXml(XmlPullParser parser) { 139 // If an OTA has been applied (build fingerprint has changed), discard stale info. 140 final String buildFingerprint = parser.getAttributeValue(null, ATTR_ORIGINAL_BUILD ); 141 if (!Build.FINGERPRINT.equals(buildFingerprint)) { 142 return null; 143 } 144 final long receivedTime = 145 Long.parseLong(parser.getAttributeValue(null, ATTR_RECEIVED_TIME)); 146 final int securityPatchState = 147 Integer.parseInt(parser.getAttributeValue(null, ATTR_SECURITY_PATCH_STATE)); 148 return new SystemUpdateInfo(receivedTime, securityPatchState); 149 } 150 151 @Override describeContents()152 public int describeContents() { 153 return 0; 154 } 155 156 @Override writeToParcel(Parcel dest, int flags)157 public void writeToParcel(Parcel dest, int flags) { 158 dest.writeLong(getReceivedTime()); 159 dest.writeInt(getSecurityPatchState()); 160 } 161 162 @Override toString()163 public String toString() { 164 return String.format("SystemUpdateInfo (receivedTime = %d, securityPatchState = %s)", 165 mReceivedTime, securityPatchStateToString(mSecurityPatchState)); 166 } 167 securityPatchStateToString(@ecurityPatchState int state)168 private static String securityPatchStateToString(@SecurityPatchState int state) { 169 switch (state) { 170 case SECURITY_PATCH_STATE_FALSE: 171 return "false"; 172 case SECURITY_PATCH_STATE_TRUE: 173 return "true"; 174 case SECURITY_PATCH_STATE_UNKNOWN: 175 return "unknown"; 176 default: 177 throw new IllegalArgumentException("Unrecognized security patch state: " + state); 178 } 179 } 180 181 @Override equals(Object o)182 public boolean equals(Object o) { 183 if (this == o) return true; 184 if (o == null || getClass() != o.getClass()) return false; 185 SystemUpdateInfo that = (SystemUpdateInfo) o; 186 return mReceivedTime == that.mReceivedTime 187 && mSecurityPatchState == that.mSecurityPatchState; 188 } 189 190 @Override hashCode()191 public int hashCode() { 192 return Objects.hash(mReceivedTime, mSecurityPatchState); 193 } 194 } 195