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