• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.Nullable;
20 import android.content.pm.PackageInfo;
21 import android.content.pm.ShortcutInfo;
22 import android.graphics.Bitmap;
23 import android.os.FileUtils;
24 import android.util.Slog;
25 import android.util.Xml;
26 
27 import com.android.internal.annotations.GuardedBy;
28 import com.android.internal.util.Preconditions;
29 import com.android.modules.utils.TypedXmlSerializer;
30 
31 import org.json.JSONException;
32 import org.json.JSONObject;
33 import org.xmlpull.v1.XmlPullParserException;
34 
35 import java.io.File;
36 import java.io.FileOutputStream;
37 import java.io.IOException;
38 import java.nio.charset.StandardCharsets;
39 import java.util.Objects;
40 
41 /**
42  * All methods should be either guarded by {@code #mPackageItemLock}.
43  */
44 abstract class ShortcutPackageItem {
45     private static final String TAG = ShortcutService.TAG;
46     private static final String KEY_NAME = "name";
47 
48     private final int mPackageUserId;
49     private final String mPackageName;
50 
51     private final ShortcutPackageInfo mPackageInfo;
52 
53     protected ShortcutUser mShortcutUser;
54 
55     @GuardedBy("mPackageItemLock")
56     protected final ShortcutBitmapSaver mShortcutBitmapSaver;
57 
58     protected final Object mPackageItemLock = new Object();
59 
ShortcutPackageItem(@onNull ShortcutUser shortcutUser, int packageUserId, @NonNull String packageName, @NonNull ShortcutPackageInfo packageInfo)60     protected ShortcutPackageItem(@NonNull ShortcutUser shortcutUser,
61             int packageUserId, @NonNull String packageName,
62             @NonNull ShortcutPackageInfo packageInfo) {
63         mShortcutUser = shortcutUser;
64         mPackageUserId = packageUserId;
65         mPackageName = Preconditions.checkStringNotEmpty(packageName);
66         mPackageInfo = Objects.requireNonNull(packageInfo);
67         mShortcutBitmapSaver = new ShortcutBitmapSaver(shortcutUser.mService);
68     }
69 
70     /**
71      * Change the parent {@link ShortcutUser}.  Need it in the restore code.
72      */
replaceUser(ShortcutUser user)73     public void replaceUser(ShortcutUser user) {
74         mShortcutUser = user;
75     }
76 
getUser()77     public ShortcutUser getUser() {
78         return mShortcutUser;
79     }
80 
81     /**
82      * ID of the user who actually has this package running on.  For {@link ShortcutPackage},
83      * this is the same thing as {@link #getOwnerUserId}, but if it's a {@link ShortcutLauncher} and
84      * {@link #getOwnerUserId} is of work profile, then this ID is of the primary user.
85      */
getPackageUserId()86     public int getPackageUserId() {
87         return mPackageUserId;
88     }
89 
90     /**
91      * ID of the user who sees the shortcuts from this instance.
92      */
getOwnerUserId()93     public abstract int getOwnerUserId();
94 
95     @NonNull
getPackageName()96     public String getPackageName() {
97         return mPackageName;
98     }
99 
getPackageInfo()100     public ShortcutPackageInfo getPackageInfo() {
101         return mPackageInfo;
102     }
103 
refreshPackageSignatureAndSave()104     public void refreshPackageSignatureAndSave() {
105         if (mPackageInfo.isShadow()) {
106             return; // Don't refresh for shadow user.
107         }
108         final ShortcutService s = mShortcutUser.mService;
109         mPackageInfo.refreshSignature(s, this);
110         scheduleSave();
111     }
112 
attemptToRestoreIfNeededAndSave()113     public void attemptToRestoreIfNeededAndSave() {
114         if (!mPackageInfo.isShadow()) {
115             return; // Already installed, nothing to do.
116         }
117         final ShortcutService s = mShortcutUser.mService;
118         if (!s.isPackageInstalled(mPackageName, mPackageUserId)) {
119             if (ShortcutService.DEBUG) {
120                 Slog.d(TAG, String.format("Package still not installed: %s/u%d",
121                         mPackageName, mPackageUserId));
122             }
123             return; // Not installed, no need to restore yet.
124         }
125         int restoreBlockReason;
126         long currentVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
127 
128         if (!mPackageInfo.hasSignatures()) {
129             s.wtf("Attempted to restore package " + mPackageName + "/u" + mPackageUserId
130                     + " but signatures not found in the restore data.");
131             restoreBlockReason = ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH;
132         } else {
133             final PackageInfo pi = s.getPackageInfoWithSignatures(mPackageName, mPackageUserId);
134             currentVersionCode = pi.getLongVersionCode();
135             restoreBlockReason = mPackageInfo.canRestoreTo(s, pi, canRestoreAnyVersion());
136         }
137 
138         if (ShortcutService.DEBUG) {
139             Slog.d(TAG, String.format("Restoring package: %s/u%d (version=%d) %s for u%d",
140                     mPackageName, mPackageUserId, currentVersionCode,
141                     ShortcutInfo.getDisabledReasonDebugString(restoreBlockReason),
142                     getOwnerUserId()));
143         }
144 
145         onRestored(restoreBlockReason);
146 
147         // Either way, it's no longer a shadow.
148         mPackageInfo.setShadow(false);
149 
150         scheduleSave();
151     }
152 
canRestoreAnyVersion()153     protected abstract boolean canRestoreAnyVersion();
154 
onRestored(int restoreBlockReason)155     protected abstract void onRestored(int restoreBlockReason);
156 
saveToXml(@onNull TypedXmlSerializer out, boolean forBackup)157     public abstract void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup)
158             throws IOException, XmlPullParserException;
159 
160     @GuardedBy("mPackageItemLock")
saveToFileLocked(File path, boolean forBackup)161     public void saveToFileLocked(File path, boolean forBackup) {
162         try (ResilientAtomicFile file = getResilientFile(path)) {
163             FileOutputStream os = null;
164             try {
165                 os = file.startWrite();
166 
167                 // Write to XML
168                 final TypedXmlSerializer itemOut;
169                 if (forBackup) {
170                     itemOut = Xml.newFastSerializer();
171                     itemOut.setOutput(os, StandardCharsets.UTF_8.name());
172                 } else {
173                     itemOut = Xml.resolveSerializer(os);
174                 }
175                 itemOut.startDocument(null, true);
176 
177                 saveToXml(itemOut, forBackup);
178 
179                 itemOut.endDocument();
180 
181                 os.flush();
182                 mShortcutUser.mService.injectFinishWrite(file, os);
183             } catch (XmlPullParserException | IOException e) {
184                 Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
185                 file.failWrite(os);
186             }
187         }
188     }
189 
dumpCheckin(boolean clear)190     public JSONObject dumpCheckin(boolean clear) throws JSONException {
191         final JSONObject result = new JSONObject();
192         result.put(KEY_NAME, mPackageName);
193         return result;
194     }
195 
196     /**
197      * Verify various internal states.
198      */
verifyStates()199     public void verifyStates() {
200     }
201 
scheduleSave()202     public void scheduleSave() {
203         mShortcutUser.mService.injectPostToHandlerDebounced(
204                 mSaveShortcutPackageRunner, mSaveShortcutPackageRunner);
205     }
206 
207     private final Runnable mSaveShortcutPackageRunner = this::saveShortcutPackageItem;
208 
saveShortcutPackageItem()209     void saveShortcutPackageItem() {
210         // Wait for bitmap saves to conclude before proceeding to saving shortcuts.
211         waitForBitmapSaves();
212         // Save each ShortcutPackageItem in a separate Xml file.
213         final File path = getShortcutPackageItemFile();
214         if (ShortcutService.DEBUG || ShortcutService.DEBUG_REBOOT) {
215             Slog.d(TAG, "Saving package item " + getPackageName() + " to " + path);
216         }
217         synchronized (mPackageItemLock) {
218             path.getParentFile().mkdirs();
219             saveToFileLocked(path, false /*forBackup*/);
220         }
221     }
222 
waitForBitmapSaves()223     public boolean waitForBitmapSaves() {
224         synchronized (mPackageItemLock) {
225             return mShortcutBitmapSaver.waitForAllSavesLocked();
226         }
227     }
228 
saveBitmap(ShortcutInfo shortcut, int maxDimension, Bitmap.CompressFormat format, int quality)229     public void saveBitmap(ShortcutInfo shortcut,
230             int maxDimension, Bitmap.CompressFormat format, int quality) {
231         synchronized (mPackageItemLock) {
232             mShortcutBitmapSaver.saveBitmapLocked(shortcut, maxDimension, format, quality);
233         }
234     }
235 
236     /**
237      * Wait for all pending saves to finish, and then return the given shortcut's bitmap path.
238      */
239     @Nullable
getBitmapPathMayWait(ShortcutInfo shortcut)240     public String getBitmapPathMayWait(ShortcutInfo shortcut) {
241         synchronized (mPackageItemLock) {
242             return mShortcutBitmapSaver.getBitmapPathMayWaitLocked(shortcut);
243         }
244     }
245 
removeIcon(ShortcutInfo shortcut)246     public void removeIcon(ShortcutInfo shortcut) {
247         synchronized (mPackageItemLock) {
248             mShortcutBitmapSaver.removeIcon(shortcut);
249         }
250     }
251 
removeShortcutPackageItem()252     void removeShortcutPackageItem() {
253         synchronized (mPackageItemLock) {
254             getResilientFile(getShortcutPackageItemFile()).delete();
255         }
256     }
257 
getShortcutPackageItemFile()258     protected abstract File getShortcutPackageItemFile();
259 
getResilientFile(File file)260     protected static ResilientAtomicFile getResilientFile(File file) {
261         String path = file.getPath();
262         File temporaryBackup = new File(path + ".backup");
263         File reserveCopy = new File(path + ".reservecopy");
264         int fileMode = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH;
265         return new ResilientAtomicFile(file, temporaryBackup, reserveCopy, fileMode,
266                 "shortcut package item", null);
267     }
268 }
269