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