2 * Copyright (C) 2016 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.android.server.pm;
18 import android.annotation.NonNull;
19 import android.annotation.UserIdInt;
20 import android.content.pm.PackageInfo;
21 import android.util.Slog;
23 import com.android.server.backup.BackupUtils;
25 import libcore.io.Base64;
26 import libcore.util.HexEncoding;
28 import org.xmlpull.v1.XmlPullParser;
29 import org.xmlpull.v1.XmlPullParserException;
30 import org.xmlpull.v1.XmlSerializer;
32 import java.io.IOException;
33 import java.io.PrintWriter;
34 import java.util.ArrayList;
37 * Package information used by {@link android.content.pm.ShortcutManager} for backup / restore.
39 * All methods should be guarded by {@code ShortcutService.mLock}.
41 class ShortcutPackageInfo {
42 private static final String TAG = ShortcutService.TAG;
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";
49 private static final String TAG_SIGNATURE = "signature";
50 private static final String ATTR_SIGNATURE_HASH = "hash";
52 private static final int VERSION_UNKNOWN = -1;
55 * When true, this package information was restored from the previous device, and the app hasn't
58 private boolean mIsShadow;
59 private int mVersionCode = VERSION_UNKNOWN;
60 private long mLastUpdateTime;
61 private ArrayList<byte[]> mSigHashes;
63 private ShortcutPackageInfo(int versionCode, long lastUpdateTime,
64 ArrayList<byte[]> sigHashes, boolean isShadow) {
65 mVersionCode = versionCode;
66 mLastUpdateTime = lastUpdateTime;
68 mSigHashes = sigHashes;
71 public static ShortcutPackageInfo newEmpty() {
72 return new ShortcutPackageInfo(VERSION_UNKNOWN, /* last update time =*/ 0,
73 new ArrayList<>(0), /* isShadow */ false);
76 public boolean isShadow() {
80 public void setShadow(boolean shadow) {
84 public int getVersionCode() {
88 public long getLastUpdateTime() {
89 return mLastUpdateTime;
92 public void updateVersionInfo(@NonNull PackageInfo pi) {
94 mVersionCode = pi.versionCode;
95 mLastUpdateTime = pi.lastUpdateTime;
99 public boolean hasSignatures() {
100 return mSigHashes.size() > 0;
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");
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));
115 if (!BackupUtils.signaturesMatch(mSigHashes, target)) {
116 Slog.w(TAG, "Can't restore: Package signature mismatch");
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);
129 final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode, pi.lastUpdateTime,
130 BackupUtils.hashSignatureArray(pi.signatures), /* shadow=*/ false);
135 public void refresh(ShortcutService s, ShortcutPackageItem pkg) {
137 s.wtf("Attempted to refresh package info for shadow package " + pkg.getPackageName()
138 + ", user=" + pkg.getOwnerUserId());
141 // Note use mUserId here, rather than userId.
142 final PackageInfo pi = s.getPackageInfoWithSignatures(
143 pkg.getPackageName(), pkg.getPackageUserId());
145 Slog.w(TAG, "Package not found: " + pkg.getPackageName());
148 mVersionCode = pi.versionCode;
149 mLastUpdateTime = pi.lastUpdateTime;
150 mSigHashes = BackupUtils.hashSignatureArray(pi.signatures);
153 public void saveToXml(XmlSerializer out) throws IOException {
155 out.startTag(null, TAG_ROOT);
157 ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode);
158 ShortcutService.writeAttr(out, ATTR_LAST_UPDATE_TIME, mLastUpdateTime);
159 ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow);
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);
166 out.endTag(null, TAG_ROOT);
169 public void loadFromXml(XmlPullParser parser, boolean fromBackup)
170 throws IOException, XmlPullParserException {
172 final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
174 final long lastUpdateTime = ShortcutService.parseLongAttribute(
175 parser, ATTR_LAST_UPDATE_TIME);
177 // When restoring from backup, it's always shadow.
178 final boolean shadow =
179 fromBackup || ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
181 final ArrayList<byte[]> hashes = new ArrayList<>();
183 final int outerDepth = parser.getDepth();
185 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
186 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
187 if (type != XmlPullParser.START_TAG) {
190 final int depth = parser.getDepth();
191 final String tag = parser.getName();
193 if (depth == outerDepth + 1) {
195 case TAG_SIGNATURE: {
196 final String hash = ShortcutService.parseStringAttribute(
197 parser, ATTR_SIGNATURE_HASH);
198 hashes.add(Base64.decode(hash.getBytes()));
203 ShortcutService.warnForInvalidTag(depth, tag);
206 // Successfully loaded; replace the feilds.
207 mVersionCode = versionCode;
208 mLastUpdateTime = lastUpdateTime;
213 public void dump(PrintWriter pw, String prefix) {
217 pw.println("PackageInfo:");
220 pw.print(" IsShadow: ");
225 pw.print(" Version: ");
226 pw.print(mVersionCode);
230 pw.print(" Last package update time: ");
231 pw.print(mLastUpdateTime);
234 for (int i = 0; i < mSigHashes.size(); i++) {
237 pw.print("SigHash: ");
238 pw.println(HexEncoding.encode(mSigHashes.get(i)));