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