• 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.util.AtomicFile;
24 import android.util.Slog;
25 import android.util.TypedXmlSerializer;
26 import android.util.Xml;
27 
28 import com.android.internal.annotations.GuardedBy;
29 import com.android.internal.util.Preconditions;
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 #mShortcutUser.mService.mLock} or {@code #mLock}.
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("mLock")
56     protected ShortcutBitmapSaver mShortcutBitmapSaver;
57 
58     protected final Object mLock = 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("mLock")
saveToFileLocked(File path, boolean forBackup)161     public void saveToFileLocked(File path, boolean forBackup) {
162         final AtomicFile file = new AtomicFile(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             file.finishWrite(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     @GuardedBy("mLock")
scheduleSaveToAppSearchLocked()190     void scheduleSaveToAppSearchLocked() {
191 
192     }
193 
dumpCheckin(boolean clear)194     public JSONObject dumpCheckin(boolean clear) throws JSONException {
195         final JSONObject result = new JSONObject();
196         result.put(KEY_NAME, mPackageName);
197         return result;
198     }
199 
200     /**
201      * Verify various internal states.
202      */
verifyStates()203     public void verifyStates() {
204     }
205 
scheduleSave()206     public void scheduleSave() {
207         mShortcutUser.mService.injectPostToHandlerDebounced(
208                 mSaveShortcutPackageRunner, mSaveShortcutPackageRunner);
209     }
210 
211     private final Runnable mSaveShortcutPackageRunner = this::saveShortcutPackageItem;
212 
saveShortcutPackageItem()213     void saveShortcutPackageItem() {
214         // Wait for bitmap saves to conclude before proceeding to saving shortcuts.
215         waitForBitmapSaves();
216         // Save each ShortcutPackageItem in a separate Xml file.
217         final File path = getShortcutPackageItemFile();
218         if (ShortcutService.DEBUG || ShortcutService.DEBUG_REBOOT) {
219             Slog.d(TAG, "Saving package item " + getPackageName() + " to " + path);
220         }
221         synchronized (mLock) {
222             path.getParentFile().mkdirs();
223             // TODO: Since we are persisting shortcuts into AppSearch, we should read from/write to
224             //  AppSearch as opposed to maintaining a separate XML file.
225             saveToFileLocked(path, false /*forBackup*/);
226             scheduleSaveToAppSearchLocked();
227         }
228     }
229 
waitForBitmapSaves()230     public boolean waitForBitmapSaves() {
231         synchronized (mLock) {
232             return mShortcutBitmapSaver.waitForAllSavesLocked();
233         }
234     }
235 
saveBitmap(ShortcutInfo shortcut, int maxDimension, Bitmap.CompressFormat format, int quality)236     public void saveBitmap(ShortcutInfo shortcut,
237             int maxDimension, Bitmap.CompressFormat format, int quality) {
238         synchronized (mLock) {
239             mShortcutBitmapSaver.saveBitmapLocked(shortcut, maxDimension, format, quality);
240         }
241     }
242 
243     /**
244      * Wait for all pending saves to finish, and then return the given shortcut's bitmap path.
245      */
246     @Nullable
getBitmapPathMayWait(ShortcutInfo shortcut)247     public String getBitmapPathMayWait(ShortcutInfo shortcut) {
248         synchronized (mLock) {
249             return mShortcutBitmapSaver.getBitmapPathMayWaitLocked(shortcut);
250         }
251     }
252 
removeIcon(ShortcutInfo shortcut)253     public void removeIcon(ShortcutInfo shortcut) {
254         synchronized (mLock) {
255             mShortcutBitmapSaver.removeIcon(shortcut);
256         }
257     }
258 
removeShortcutPackageItem()259     void removeShortcutPackageItem() {
260         synchronized (mLock) {
261             getShortcutPackageItemFile().delete();
262         }
263     }
264 
getShortcutPackageItemFile()265     protected abstract File getShortcutPackageItemFile();
266 }
267