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.content.pm.PackageInfo; 20 import android.content.pm.ShortcutInfo; 21 import android.util.AtomicFile; 22 import android.util.Slog; 23 24 import com.android.internal.util.FastXmlSerializer; 25 import com.android.internal.util.Preconditions; 26 27 import org.json.JSONException; 28 import org.json.JSONObject; 29 import org.xmlpull.v1.XmlPullParserException; 30 import org.xmlpull.v1.XmlSerializer; 31 32 import java.io.BufferedOutputStream; 33 import java.io.File; 34 import java.io.FileOutputStream; 35 import java.io.IOException; 36 import java.nio.charset.StandardCharsets; 37 import java.util.Objects; 38 39 /** 40 * All methods should be guarded by {@code #mShortcutUser.mService.mLock}. 41 */ 42 abstract class ShortcutPackageItem { 43 private static final String TAG = ShortcutService.TAG; 44 private static final String KEY_NAME = "name"; 45 46 private final int mPackageUserId; 47 private final String mPackageName; 48 49 private final ShortcutPackageInfo mPackageInfo; 50 51 protected ShortcutUser mShortcutUser; 52 ShortcutPackageItem(@onNull ShortcutUser shortcutUser, int packageUserId, @NonNull String packageName, @NonNull ShortcutPackageInfo packageInfo)53 protected ShortcutPackageItem(@NonNull ShortcutUser shortcutUser, 54 int packageUserId, @NonNull String packageName, 55 @NonNull ShortcutPackageInfo packageInfo) { 56 mShortcutUser = shortcutUser; 57 mPackageUserId = packageUserId; 58 mPackageName = Preconditions.checkStringNotEmpty(packageName); 59 mPackageInfo = Objects.requireNonNull(packageInfo); 60 } 61 62 /** 63 * Change the parent {@link ShortcutUser}. Need it in the restore code. 64 */ replaceUser(ShortcutUser user)65 public void replaceUser(ShortcutUser user) { 66 mShortcutUser = user; 67 } 68 getUser()69 public ShortcutUser getUser() { 70 return mShortcutUser; 71 } 72 73 /** 74 * ID of the user who actually has this package running on. For {@link ShortcutPackage}, 75 * this is the same thing as {@link #getOwnerUserId}, but if it's a {@link ShortcutLauncher} and 76 * {@link #getOwnerUserId} is of work profile, then this ID is of the primary user. 77 */ getPackageUserId()78 public int getPackageUserId() { 79 return mPackageUserId; 80 } 81 82 /** 83 * ID of the user who sees the shortcuts from this instance. 84 */ getOwnerUserId()85 public abstract int getOwnerUserId(); 86 87 @NonNull getPackageName()88 public String getPackageName() { 89 return mPackageName; 90 } 91 getPackageInfo()92 public ShortcutPackageInfo getPackageInfo() { 93 return mPackageInfo; 94 } 95 refreshPackageSignatureAndSave()96 public void refreshPackageSignatureAndSave() { 97 if (mPackageInfo.isShadow()) { 98 return; // Don't refresh for shadow user. 99 } 100 final ShortcutService s = mShortcutUser.mService; 101 mPackageInfo.refreshSignature(s, this); 102 s.scheduleSaveUser(getOwnerUserId()); 103 } 104 attemptToRestoreIfNeededAndSave()105 public void attemptToRestoreIfNeededAndSave() { 106 if (!mPackageInfo.isShadow()) { 107 return; // Already installed, nothing to do. 108 } 109 final ShortcutService s = mShortcutUser.mService; 110 if (!s.isPackageInstalled(mPackageName, mPackageUserId)) { 111 if (ShortcutService.DEBUG) { 112 Slog.d(TAG, String.format("Package still not installed: %s/u%d", 113 mPackageName, mPackageUserId)); 114 } 115 return; // Not installed, no need to restore yet. 116 } 117 int restoreBlockReason; 118 long currentVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN; 119 120 if (!mPackageInfo.hasSignatures()) { 121 s.wtf("Attempted to restore package " + mPackageName + "/u" + mPackageUserId 122 + " but signatures not found in the restore data."); 123 restoreBlockReason = ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH; 124 } else { 125 final PackageInfo pi = s.getPackageInfoWithSignatures(mPackageName, mPackageUserId); 126 currentVersionCode = pi.getLongVersionCode(); 127 restoreBlockReason = mPackageInfo.canRestoreTo(s, pi, canRestoreAnyVersion()); 128 } 129 130 if (ShortcutService.DEBUG) { 131 Slog.d(TAG, String.format("Restoring package: %s/u%d (version=%d) %s for u%d", 132 mPackageName, mPackageUserId, currentVersionCode, 133 ShortcutInfo.getDisabledReasonDebugString(restoreBlockReason), 134 getOwnerUserId())); 135 } 136 137 onRestored(restoreBlockReason); 138 139 // Either way, it's no longer a shadow. 140 mPackageInfo.setShadow(false); 141 142 s.scheduleSaveUser(mPackageUserId); 143 } 144 canRestoreAnyVersion()145 protected abstract boolean canRestoreAnyVersion(); 146 onRestored(int restoreBlockReason)147 protected abstract void onRestored(int restoreBlockReason); 148 saveToXml(@onNull XmlSerializer out, boolean forBackup)149 public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup) 150 throws IOException, XmlPullParserException; 151 saveToFile(File path, boolean forBackup)152 public void saveToFile(File path, boolean forBackup) { 153 final AtomicFile file = new AtomicFile(path); 154 FileOutputStream os = null; 155 try { 156 os = file.startWrite(); 157 final BufferedOutputStream bos = new BufferedOutputStream(os); 158 159 // Write to XML 160 XmlSerializer itemOut = new FastXmlSerializer(); 161 itemOut.setOutput(bos, StandardCharsets.UTF_8.name()); 162 itemOut.startDocument(null, true); 163 164 saveToXml(itemOut, forBackup); 165 166 itemOut.endDocument(); 167 168 bos.flush(); 169 os.flush(); 170 file.finishWrite(os); 171 } catch (XmlPullParserException | IOException e) { 172 Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e); 173 file.failWrite(os); 174 } 175 } 176 dumpCheckin(boolean clear)177 public JSONObject dumpCheckin(boolean clear) throws JSONException { 178 final JSONObject result = new JSONObject(); 179 result.put(KEY_NAME, mPackageName); 180 return result; 181 } 182 183 /** 184 * Verify various internal states. 185 */ verifyStates()186 public void verifyStates() { 187 } 188 } 189