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 */ 16 package com.android.server.pm; 17 18 import android.annotation.NonNull; 19 import android.annotation.UserIdInt; 20 import android.content.pm.PackageInfo; 21 import android.util.Slog; 22 23 import com.android.internal.annotations.VisibleForTesting; 24 import com.android.server.backup.BackupUtils; 25 26 import libcore.util.HexEncoding; 27 28 import org.xmlpull.v1.XmlPullParser; 29 import org.xmlpull.v1.XmlPullParserException; 30 import org.xmlpull.v1.XmlSerializer; 31 32 import java.io.IOException; 33 import java.io.PrintWriter; 34 import java.util.ArrayList; 35 import java.util.Base64; 36 37 /** 38 * Package information used by {@link android.content.pm.ShortcutManager} for backup / restore. 39 * 40 * All methods should be guarded by {@code ShortcutService.mLock}. 41 */ 42 class ShortcutPackageInfo { 43 private static final String TAG = ShortcutService.TAG; 44 45 static final String TAG_ROOT = "package-info"; 46 private static final String ATTR_VERSION = "version"; 47 private static final String ATTR_LAST_UPDATE_TIME = "last_udpate_time"; 48 private static final String ATTR_SHADOW = "shadow"; 49 50 private static final String TAG_SIGNATURE = "signature"; 51 private static final String ATTR_SIGNATURE_HASH = "hash"; 52 53 private static final int VERSION_UNKNOWN = -1; 54 55 /** 56 * When true, this package information was restored from the previous device, and the app hasn't 57 * been installed yet. 58 */ 59 private boolean mIsShadow; 60 private int mVersionCode = VERSION_UNKNOWN; 61 private long mLastUpdateTime; 62 private ArrayList<byte[]> mSigHashes; 63 ShortcutPackageInfo(int versionCode, long lastUpdateTime, ArrayList<byte[]> sigHashes, boolean isShadow)64 private ShortcutPackageInfo(int versionCode, long lastUpdateTime, 65 ArrayList<byte[]> sigHashes, boolean isShadow) { 66 mVersionCode = versionCode; 67 mLastUpdateTime = lastUpdateTime; 68 mIsShadow = isShadow; 69 mSigHashes = sigHashes; 70 } 71 newEmpty()72 public static ShortcutPackageInfo newEmpty() { 73 return new ShortcutPackageInfo(VERSION_UNKNOWN, /* last update time =*/ 0, 74 new ArrayList<>(0), /* isShadow */ false); 75 } 76 isShadow()77 public boolean isShadow() { 78 return mIsShadow; 79 } 80 setShadow(boolean shadow)81 public void setShadow(boolean shadow) { 82 mIsShadow = shadow; 83 } 84 getVersionCode()85 public int getVersionCode() { 86 return mVersionCode; 87 } 88 getLastUpdateTime()89 public long getLastUpdateTime() { 90 return mLastUpdateTime; 91 } 92 93 /** Set {@link #mVersionCode} and {@link #mLastUpdateTime} from a {@link PackageInfo}. */ updateVersionInfo(@onNull PackageInfo pi)94 public void updateVersionInfo(@NonNull PackageInfo pi) { 95 if (pi != null) { 96 mVersionCode = pi.versionCode; 97 mLastUpdateTime = pi.lastUpdateTime; 98 } 99 } 100 hasSignatures()101 public boolean hasSignatures() { 102 return mSigHashes.size() > 0; 103 } 104 canRestoreTo(ShortcutService s, PackageInfo target)105 public boolean canRestoreTo(ShortcutService s, PackageInfo target) { 106 if (!s.shouldBackupApp(target)) { 107 // "allowBackup" was true when backed up, but now false. 108 Slog.w(TAG, "Can't restore: package no longer allows backup"); 109 return false; 110 } 111 if (target.versionCode < mVersionCode) { 112 Slog.w(TAG, String.format( 113 "Can't restore: package current version %d < backed up version %d", 114 target.versionCode, mVersionCode)); 115 return false; 116 } 117 if (!BackupUtils.signaturesMatch(mSigHashes, target)) { 118 Slog.w(TAG, "Can't restore: Package signature mismatch"); 119 return false; 120 } 121 return true; 122 } 123 124 @VisibleForTesting generateForInstalledPackageForTest( ShortcutService s, String packageName, @UserIdInt int packageUserId)125 public static ShortcutPackageInfo generateForInstalledPackageForTest( 126 ShortcutService s, String packageName, @UserIdInt int packageUserId) { 127 final PackageInfo pi = s.getPackageInfoWithSignatures(packageName, packageUserId); 128 if (pi.signatures == null || pi.signatures.length == 0) { 129 Slog.e(TAG, "Can't get signatures: package=" + packageName); 130 return null; 131 } 132 final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode, pi.lastUpdateTime, 133 BackupUtils.hashSignatureArray(pi.signatures), /* shadow=*/ false); 134 135 return ret; 136 } 137 refreshSignature(ShortcutService s, ShortcutPackageItem pkg)138 public void refreshSignature(ShortcutService s, ShortcutPackageItem pkg) { 139 if (mIsShadow) { 140 s.wtf("Attempted to refresh package info for shadow package " + pkg.getPackageName() 141 + ", user=" + pkg.getOwnerUserId()); 142 return; 143 } 144 // Note use mUserId here, rather than userId. 145 final PackageInfo pi = s.getPackageInfoWithSignatures( 146 pkg.getPackageName(), pkg.getPackageUserId()); 147 if (pi == null) { 148 Slog.w(TAG, "Package not found: " + pkg.getPackageName()); 149 return; 150 } 151 mSigHashes = BackupUtils.hashSignatureArray(pi.signatures); 152 } 153 saveToXml(XmlSerializer out)154 public void saveToXml(XmlSerializer out) throws IOException { 155 156 out.startTag(null, TAG_ROOT); 157 158 ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode); 159 ShortcutService.writeAttr(out, ATTR_LAST_UPDATE_TIME, mLastUpdateTime); 160 ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow); 161 162 for (int i = 0; i < mSigHashes.size(); i++) { 163 out.startTag(null, TAG_SIGNATURE); 164 final String encoded = Base64.getEncoder().encodeToString(mSigHashes.get(i)); 165 ShortcutService.writeAttr(out, ATTR_SIGNATURE_HASH, encoded); 166 out.endTag(null, TAG_SIGNATURE); 167 } 168 out.endTag(null, TAG_ROOT); 169 } 170 loadFromXml(XmlPullParser parser, boolean fromBackup)171 public void loadFromXml(XmlPullParser parser, boolean fromBackup) 172 throws IOException, XmlPullParserException { 173 174 final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION); 175 176 final long lastUpdateTime = ShortcutService.parseLongAttribute( 177 parser, ATTR_LAST_UPDATE_TIME); 178 179 // When restoring from backup, it's always shadow. 180 final boolean shadow = 181 fromBackup || ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW); 182 183 final ArrayList<byte[]> hashes = new ArrayList<>(); 184 185 final int outerDepth = parser.getDepth(); 186 int type; 187 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 188 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 189 if (type != XmlPullParser.START_TAG) { 190 continue; 191 } 192 final int depth = parser.getDepth(); 193 final String tag = parser.getName(); 194 195 if (depth == outerDepth + 1) { 196 switch (tag) { 197 case TAG_SIGNATURE: { 198 final String hash = ShortcutService.parseStringAttribute( 199 parser, ATTR_SIGNATURE_HASH); 200 // Throws IllegalArgumentException if hash is invalid base64 data 201 final byte[] decoded = Base64.getDecoder().decode(hash); 202 hashes.add(decoded); 203 continue; 204 } 205 } 206 } 207 ShortcutService.warnForInvalidTag(depth, tag); 208 } 209 210 // Successfully loaded; replace the feilds. 211 mVersionCode = versionCode; 212 mLastUpdateTime = lastUpdateTime; 213 mIsShadow = shadow; 214 mSigHashes = hashes; 215 } 216 dump(PrintWriter pw, String prefix)217 public void dump(PrintWriter pw, String prefix) { 218 pw.println(); 219 220 pw.print(prefix); 221 pw.println("PackageInfo:"); 222 223 pw.print(prefix); 224 pw.print(" IsShadow: "); 225 pw.print(mIsShadow); 226 pw.println(); 227 228 pw.print(prefix); 229 pw.print(" Version: "); 230 pw.print(mVersionCode); 231 pw.println(); 232 233 pw.print(prefix); 234 pw.print(" Last package update time: "); 235 pw.print(mLastUpdateTime); 236 pw.println(); 237 238 for (int i = 0; i < mSigHashes.size(); i++) { 239 pw.print(prefix); 240 pw.print(" "); 241 pw.print("SigHash: "); 242 pw.println(HexEncoding.encode(mSigHashes.get(i))); 243 } 244 } 245 } 246