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