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