ShortcutPackageInfo.java revision e3fffa9615286cb88a62b49ac5babaeff8b732db
1/* 2 * Copyright (C) 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 */ 16package com.android.server.pm; 17 18import android.annotation.NonNull; 19import android.annotation.UserIdInt; 20import android.content.pm.PackageInfo; 21import android.content.pm.ShortcutInfo; 22import android.util.Slog; 23 24import com.android.internal.annotations.VisibleForTesting; 25import com.android.server.backup.BackupUtils; 26 27import libcore.util.HexEncoding; 28 29import org.xmlpull.v1.XmlPullParser; 30import org.xmlpull.v1.XmlPullParserException; 31import org.xmlpull.v1.XmlSerializer; 32 33import java.io.IOException; 34import java.io.PrintWriter; 35import java.util.ArrayList; 36import java.util.Base64; 37 38/** 39 * Package information used by {@link android.content.pm.ShortcutManager} for backup / restore. 40 * 41 * All methods should be guarded by {@code ShortcutService.mLock}. 42 */ 43class ShortcutPackageInfo { 44 private static final String TAG = ShortcutService.TAG; 45 46 static final String TAG_ROOT = "package-info"; 47 private static final String ATTR_VERSION = "version"; 48 private static final String ATTR_LAST_UPDATE_TIME = "last_udpate_time"; 49 private static final String ATTR_BACKUP_SOURCE_VERSION = "bk_src_version"; 50 private static final String ATTR_BACKUP_ALLOWED = "allow-backup"; 51 private static final String ATTR_BACKUP_ALLOWED_INITIALIZED = "allow-backup-initialized"; 52 private static final String ATTR_BACKUP_SOURCE_BACKUP_ALLOWED = "bk_src_backup-allowed"; 53 private static final String ATTR_SHADOW = "shadow"; 54 55 private static final String TAG_SIGNATURE = "signature"; 56 private static final String ATTR_SIGNATURE_HASH = "hash"; 57 58 /** 59 * When true, this package information was restored from the previous device, and the app hasn't 60 * been installed yet. 61 */ 62 private boolean mIsShadow; 63 private long mVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN; 64 private long mBackupSourceVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN; 65 private long mLastUpdateTime; 66 private ArrayList<byte[]> mSigHashes; 67 68 // mBackupAllowed didn't used to be parsisted, so we don't restore it from a file. 69 // mBackupAllowed will always start with false, and will have been updated before making a 70 // backup next time, which works file. 71 // We just don't want to print an uninitialzied mBackupAlldowed value on dumpsys, so 72 // we use this boolean to control dumpsys. 73 private boolean mBackupAllowedInitialized; 74 private boolean mBackupAllowed; 75 private boolean mBackupSourceBackupAllowed; 76 77 private ShortcutPackageInfo(long versionCode, long lastUpdateTime, 78 ArrayList<byte[]> sigHashes, boolean isShadow) { 79 mVersionCode = versionCode; 80 mLastUpdateTime = lastUpdateTime; 81 mIsShadow = isShadow; 82 mSigHashes = sigHashes; 83 mBackupAllowed = false; // By default, we assume false. 84 mBackupSourceBackupAllowed = false; 85 } 86 87 public static ShortcutPackageInfo newEmpty() { 88 return new ShortcutPackageInfo(ShortcutInfo.VERSION_CODE_UNKNOWN, /* last update time =*/ 0, 89 new ArrayList<>(0), /* isShadow */ false); 90 } 91 92 public boolean isShadow() { 93 return mIsShadow; 94 } 95 96 public void setShadow(boolean shadow) { 97 mIsShadow = shadow; 98 } 99 100 public long getVersionCode() { 101 return mVersionCode; 102 } 103 104 public long getBackupSourceVersionCode() { 105 return mBackupSourceVersionCode; 106 } 107 108 @VisibleForTesting 109 public boolean isBackupSourceBackupAllowed() { 110 return mBackupSourceBackupAllowed; 111 } 112 113 public long getLastUpdateTime() { 114 return mLastUpdateTime; 115 } 116 117 public boolean isBackupAllowed() { 118 return mBackupAllowed; 119 } 120 121 /** 122 * Set {@link #mVersionCode}, {@link #mLastUpdateTime} and {@link #mBackupAllowed} 123 * from a {@link PackageInfo}. 124 */ 125 public void updateFromPackageInfo(@NonNull PackageInfo pi) { 126 if (pi != null) { 127 mVersionCode = pi.getLongVersionCode(); 128 mLastUpdateTime = pi.lastUpdateTime; 129 mBackupAllowed = ShortcutService.shouldBackupApp(pi); 130 mBackupAllowedInitialized = true; 131 } 132 } 133 134 public boolean hasSignatures() { 135 return mSigHashes.size() > 0; 136 } 137 138 //@DisabledReason 139 public int canRestoreTo(ShortcutService s, PackageInfo currentPackage, boolean anyVersionOkay) { 140 if (!BackupUtils.signaturesMatch(mSigHashes, currentPackage)) { 141 Slog.w(TAG, "Can't restore: Package signature mismatch"); 142 return ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH; 143 } 144 if (!ShortcutService.shouldBackupApp(currentPackage) || !mBackupSourceBackupAllowed) { 145 // "allowBackup" was true when backed up, but now false. 146 Slog.w(TAG, "Can't restore: package didn't or doesn't allow backup"); 147 return ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED; 148 } 149 if (!anyVersionOkay && (currentPackage.getLongVersionCode() < mBackupSourceVersionCode)) { 150 Slog.w(TAG, String.format( 151 "Can't restore: package current version %d < backed up version %d", 152 currentPackage.getLongVersionCode(), mBackupSourceVersionCode)); 153 return ShortcutInfo.DISABLED_REASON_VERSION_LOWER; 154 } 155 return ShortcutInfo.DISABLED_REASON_NOT_DISABLED; 156 } 157 158 @VisibleForTesting 159 public static ShortcutPackageInfo generateForInstalledPackageForTest( 160 ShortcutService s, String packageName, @UserIdInt int packageUserId) { 161 final PackageInfo pi = s.getPackageInfoWithSignatures(packageName, packageUserId); 162 if (pi.signatures == null || pi.signatures.length == 0) { 163 Slog.e(TAG, "Can't get signatures: package=" + packageName); 164 return null; 165 } 166 final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.getLongVersionCode(), 167 pi.lastUpdateTime, BackupUtils.hashSignatureArray(pi.signatures), 168 /* shadow=*/ false); 169 170 ret.mBackupSourceBackupAllowed = s.shouldBackupApp(pi); 171 ret.mBackupSourceVersionCode = pi.getLongVersionCode(); 172 return ret; 173 } 174 175 public void refreshSignature(ShortcutService s, ShortcutPackageItem pkg) { 176 if (mIsShadow) { 177 s.wtf("Attempted to refresh package info for shadow package " + pkg.getPackageName() 178 + ", user=" + pkg.getOwnerUserId()); 179 return; 180 } 181 // Note use mUserId here, rather than userId. 182 final PackageInfo pi = s.getPackageInfoWithSignatures( 183 pkg.getPackageName(), pkg.getPackageUserId()); 184 if (pi == null) { 185 Slog.w(TAG, "Package not found: " + pkg.getPackageName()); 186 return; 187 } 188 mSigHashes = BackupUtils.hashSignatureArray(pi.signatures); 189 } 190 191 public void saveToXml(ShortcutService s, XmlSerializer out, boolean forBackup) 192 throws IOException { 193 if (forBackup && !mBackupAllowedInitialized) { 194 s.wtf("Backup happened before mBackupAllowed is initialized."); 195 } 196 197 out.startTag(null, TAG_ROOT); 198 199 ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode); 200 ShortcutService.writeAttr(out, ATTR_LAST_UPDATE_TIME, mLastUpdateTime); 201 ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow); 202 ShortcutService.writeAttr(out, ATTR_BACKUP_ALLOWED, mBackupAllowed); 203 204 // We don't need to save this field (we don't even read it back), but it'll show up 205 // in the dumpsys in the backup / restore payload. 206 ShortcutService.writeAttr(out, ATTR_BACKUP_ALLOWED_INITIALIZED, mBackupAllowedInitialized); 207 208 ShortcutService.writeAttr(out, ATTR_BACKUP_SOURCE_VERSION, mBackupSourceVersionCode); 209 ShortcutService.writeAttr(out, 210 ATTR_BACKUP_SOURCE_BACKUP_ALLOWED, mBackupSourceBackupAllowed); 211 212 213 for (int i = 0; i < mSigHashes.size(); i++) { 214 out.startTag(null, TAG_SIGNATURE); 215 final String encoded = Base64.getEncoder().encodeToString(mSigHashes.get(i)); 216 ShortcutService.writeAttr(out, ATTR_SIGNATURE_HASH, encoded); 217 out.endTag(null, TAG_SIGNATURE); 218 } 219 out.endTag(null, TAG_ROOT); 220 } 221 222 public void loadFromXml(XmlPullParser parser, boolean fromBackup) 223 throws IOException, XmlPullParserException { 224 225 // Don't use the version code from the backup file. 226 final long versionCode = ShortcutService.parseLongAttribute(parser, ATTR_VERSION, 227 ShortcutInfo.VERSION_CODE_UNKNOWN); 228 229 final long lastUpdateTime = ShortcutService.parseLongAttribute( 230 parser, ATTR_LAST_UPDATE_TIME); 231 232 // When restoring from backup, it's always shadow. 233 final boolean shadow = 234 fromBackup || ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW); 235 236 // We didn't used to save these attributes, and all backed up shortcuts were from 237 // apps that support backups, so the default values take this fact into consideration. 238 final long backupSourceVersion = ShortcutService.parseLongAttribute(parser, 239 ATTR_BACKUP_SOURCE_VERSION, ShortcutInfo.VERSION_CODE_UNKNOWN); 240 241 // Note the only time these "true" default value is used is when restoring from an old 242 // build that didn't save ATTR_BACKUP_ALLOWED, and that means all the data included in 243 // a backup file were from apps that support backup, so we can just use "true" as the 244 // default. 245 final boolean backupAllowed = ShortcutService.parseBooleanAttribute( 246 parser, ATTR_BACKUP_ALLOWED, true); 247 final boolean backupSourceBackupAllowed = ShortcutService.parseBooleanAttribute( 248 parser, ATTR_BACKUP_SOURCE_BACKUP_ALLOWED, true); 249 250 final ArrayList<byte[]> hashes = new ArrayList<>(); 251 252 final int outerDepth = parser.getDepth(); 253 int type; 254 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 255 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 256 if (type != XmlPullParser.START_TAG) { 257 continue; 258 } 259 final int depth = parser.getDepth(); 260 final String tag = parser.getName(); 261 262 if (depth == outerDepth + 1) { 263 switch (tag) { 264 case TAG_SIGNATURE: { 265 final String hash = ShortcutService.parseStringAttribute( 266 parser, ATTR_SIGNATURE_HASH); 267 // Throws IllegalArgumentException if hash is invalid base64 data 268 final byte[] decoded = Base64.getDecoder().decode(hash); 269 hashes.add(decoded); 270 continue; 271 } 272 } 273 } 274 ShortcutService.warnForInvalidTag(depth, tag); 275 } 276 277 // Successfully loaded; replace the fields. 278 if (fromBackup) { 279 mVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN; 280 mBackupSourceVersionCode = versionCode; 281 mBackupSourceBackupAllowed = backupAllowed; 282 } else { 283 mVersionCode = versionCode; 284 mBackupSourceVersionCode = backupSourceVersion; 285 mBackupSourceBackupAllowed = backupSourceBackupAllowed; 286 } 287 mLastUpdateTime = lastUpdateTime; 288 mIsShadow = shadow; 289 mSigHashes = hashes; 290 291 // Note we don't restore it from the file because it didn't used to be saved. 292 // We always start by assuming backup is disabled for the current package, 293 // and this field will have been updated before we actually create a backup, at the same 294 // time when we update the version code. 295 // Until then, the value of mBackupAllowed shouldn't matter, but we don't want to print 296 // a false flag on dumpsys, so set mBackupAllowedInitialized to false. 297 mBackupAllowed = false; 298 mBackupAllowedInitialized = false; 299 } 300 301 public void dump(PrintWriter pw, String prefix) { 302 pw.println(); 303 304 pw.print(prefix); 305 pw.println("PackageInfo:"); 306 307 pw.print(prefix); 308 pw.print(" IsShadow: "); 309 pw.print(mIsShadow); 310 pw.print(mIsShadow ? " (not installed)" : " (installed)"); 311 pw.println(); 312 313 pw.print(prefix); 314 pw.print(" Version: "); 315 pw.print(mVersionCode); 316 pw.println(); 317 318 if (mBackupAllowedInitialized) { 319 pw.print(prefix); 320 pw.print(" Backup Allowed: "); 321 pw.print(mBackupAllowed); 322 pw.println(); 323 } 324 325 if (mBackupSourceVersionCode != ShortcutInfo.VERSION_CODE_UNKNOWN) { 326 pw.print(prefix); 327 pw.print(" Backup source version: "); 328 pw.print(mBackupSourceVersionCode); 329 pw.println(); 330 331 pw.print(prefix); 332 pw.print(" Backup source backup allowed: "); 333 pw.print(mBackupSourceBackupAllowed); 334 pw.println(); 335 } 336 337 pw.print(prefix); 338 pw.print(" Last package update time: "); 339 pw.print(mLastUpdateTime); 340 pw.println(); 341 342 for (int i = 0; i < mSigHashes.size(); i++) { 343 pw.print(prefix); 344 pw.print(" "); 345 pw.print("SigHash: "); 346 pw.println(HexEncoding.encode(mSigHashes.get(i))); 347 } 348 } 349} 350