1475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki/* 2475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * Copyright (C) 2017 The Android Open Source Project 3475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * 4475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * Licensed under the Apache License, Version 2.0 (the "License"); 5475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * you may not use this file except in compliance with the License. 6475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * You may obtain a copy of the License at 7475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * 8475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * http://www.apache.org/licenses/LICENSE-2.0 9475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * 10475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * Unless required by applicable law or agreed to in writing, software 11475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * distributed under the License is distributed on an "AS IS" BASIS, 12475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * See the License for the specific language governing permissions and 14475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * limitations under the License. 15475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki */ 16475c36539da9687d75447adb9054c98ce03c754aMakoto Onukipackage com.android.server.pm; 17475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 18475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport android.annotation.NonNull; 19475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport android.annotation.Nullable; 20475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport android.content.pm.ShortcutInfo; 21475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport android.graphics.Bitmap; 22475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport android.graphics.Bitmap.CompressFormat; 23475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport android.graphics.drawable.Icon; 24475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport android.os.SystemClock; 25475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport android.util.Log; 26475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport android.util.Slog; 27475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 28475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport com.android.internal.annotations.GuardedBy; 29475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport com.android.internal.util.Preconditions; 30475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport com.android.server.pm.ShortcutService.FileOutputStreamWithPath; 31475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 32475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport libcore.io.IoUtils; 33475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 34475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport java.io.ByteArrayOutputStream; 35475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport java.io.File; 36475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport java.io.IOException; 37475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport java.io.PrintWriter; 38475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport java.util.Deque; 39475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport java.util.concurrent.CountDownLatch; 40475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport java.util.concurrent.Executor; 41475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport java.util.concurrent.LinkedBlockingDeque; 42475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport java.util.concurrent.LinkedBlockingQueue; 43475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport java.util.concurrent.ThreadPoolExecutor; 44475c36539da9687d75447adb9054c98ce03c754aMakoto Onukiimport java.util.concurrent.TimeUnit; 45475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 46475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki/** 47475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * Class to save shortcut bitmaps on a worker thread. 48475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * 49475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * The methods with the "Locked" prefix must be called with the service lock held. 50475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki */ 51475c36539da9687d75447adb9054c98ce03c754aMakoto Onukipublic class ShortcutBitmapSaver { 52475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki private static final String TAG = ShortcutService.TAG; 53475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki private static final boolean DEBUG = ShortcutService.DEBUG; 54475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 55475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki private static final boolean ADD_DELAY_BEFORE_SAVE_FOR_TEST = false; // DO NOT submit with true. 56475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki private static final long SAVE_DELAY_MS_FOR_TEST = 1000; // DO NOT submit with true. 57475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 58475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki /** 59475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * Before saving shortcuts.xml, and returning icons to the launcher, we wait for all pending 60475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * saves to finish. However if it takes more than this long, we just give up and proceed. 61475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki */ 62475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki private final long SAVE_WAIT_TIMEOUT_MS = 30 * 1000; 63475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 64475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki private final ShortcutService mService; 65475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 66475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki /** 67475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * Bitmaps are saved on this thread. 68475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * 69475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * Note: Just before saving shortcuts into the XML, we need to wait on all pending saves to 70475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * finish, and we need to do it with the service lock held, which would still block incoming 71475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * binder calls, meaning saving bitmaps *will* still actually block API calls too, which is 72475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * not ideal but fixing it would be tricky, so this is still a known issue on the current 73475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * version. 74475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * 75475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * In order to reduce the conflict, we use an own thread for this purpose, rather than 76475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * reusing existing background threads, and also to avoid possible deadlocks. 77475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki */ 78475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki private final Executor mExecutor = new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, 79475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki new LinkedBlockingQueue<>()); 80475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 81475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki /** Represents a bitmap to save. */ 82475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki private static class PendingItem { 83475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki /** Hosting shortcut. */ 84475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki public final ShortcutInfo shortcut; 85475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 86475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki /** Compressed bitmap data. */ 87475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki public final byte[] bytes; 88475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 89475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki /** Instantiated time, only for dogfooding. */ 90475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki private final long mInstantiatedUptimeMillis; // Only for dumpsys. 91475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 92475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki private PendingItem(ShortcutInfo shortcut, byte[] bytes) { 93475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki this.shortcut = shortcut; 94475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki this.bytes = bytes; 95475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki mInstantiatedUptimeMillis = SystemClock.uptimeMillis(); 96475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 97475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 98475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki @Override 99475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki public String toString() { 100475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki return "PendingItem{size=" + bytes.length 101475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki + " age=" + (SystemClock.uptimeMillis() - mInstantiatedUptimeMillis) + "ms" 102475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki + " shortcut=" + shortcut.toInsecureString() 103475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki + "}"; 104475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 105475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 106475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 107475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki @GuardedBy("mPendingItems") 108475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki private final Deque<PendingItem> mPendingItems = new LinkedBlockingDeque<>(); 109475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 110475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki public ShortcutBitmapSaver(ShortcutService service) { 111475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki mService = service; 112475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki // mLock = lock; 113475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 114475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 115475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki public boolean waitForAllSavesLocked() { 116475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki final CountDownLatch latch = new CountDownLatch(1); 117475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 118475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki mExecutor.execute(() -> latch.countDown()); 119475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 120475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki try { 121475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki if (latch.await(SAVE_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 122475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki return true; 123475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 124475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki mService.wtf("Timed out waiting on saving bitmaps."); 125475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } catch (InterruptedException e) { 126475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki Slog.w(TAG, "interrupted"); 127475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 128475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki return false; 129475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 130475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 131475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki /** 132475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * Wait for all pending saves to finish, and then return the given shortcut's bitmap path. 133475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki */ 134475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki @Nullable 135475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki public String getBitmapPathMayWaitLocked(ShortcutInfo shortcut) { 136475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki final boolean success = waitForAllSavesLocked(); 137475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki if (success && shortcut.hasIconFile()) { 138475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki return shortcut.getBitmapPath(); 139475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } else { 140475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki return null; 141475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 142475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 143475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 144475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki public void removeIcon(ShortcutInfo shortcut) { 145475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki // Do not remove the actual bitmap file yet, because if the device crashes before saving 146475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki // the XML we'd lose the icon. We just remove all dangling files after saving the XML. 147475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki shortcut.setIconResourceId(0); 148475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki shortcut.setIconResName(null); 149475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki shortcut.setBitmapPath(null); 150475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE | 151475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki ShortcutInfo.FLAG_ADAPTIVE_BITMAP | ShortcutInfo.FLAG_HAS_ICON_RES | 152475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE); 153475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 154475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 155475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki public void saveBitmapLocked(ShortcutInfo shortcut, 156475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki int maxDimension, CompressFormat format, int quality) { 157475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki final Icon icon = shortcut.getIcon(); 158475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki Preconditions.checkNotNull(icon); 159475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 160475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki final Bitmap original = icon.getBitmap(); 161475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki if (original == null) { 162475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki Log.e(TAG, "Missing icon: " + shortcut); 163475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki return; 164475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 165475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 166475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki // Compress it and enqueue to the requests. 167475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki final byte[] bytes; 168475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki try { 169475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki final Bitmap shrunk = mService.shrinkBitmap(original, maxDimension); 170475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki try { 171475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki try (final ByteArrayOutputStream out = new ByteArrayOutputStream(64 * 1024)) { 172475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki if (!shrunk.compress(format, quality, out)) { 173475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki Slog.wtf(ShortcutService.TAG, "Unable to compress bitmap"); 174475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 175475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki out.flush(); 176475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki bytes = out.toByteArray(); 177475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki out.close(); 178475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 179475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } finally { 180475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki if (shrunk != original) { 181475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki shrunk.recycle(); 182475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 183475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 184475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } catch (IOException | RuntimeException | OutOfMemoryError e) { 185475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki Slog.wtf(ShortcutService.TAG, "Unable to write bitmap to file", e); 186475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki return; 187475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 188475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 189475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki shortcut.addFlags( 190475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE); 191475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 192475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki if (icon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) { 193475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki shortcut.addFlags(ShortcutInfo.FLAG_ADAPTIVE_BITMAP); 194475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 195475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 196475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki // Enqueue a pending save. 197475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki final PendingItem item = new PendingItem(shortcut, bytes); 198475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki synchronized (mPendingItems) { 199475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki mPendingItems.add(item); 200475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 201475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 202475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki if (DEBUG) { 203475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki Slog.d(TAG, "Scheduling to save: " + item); 204475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 205475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 206475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki mExecutor.execute(mRunnable); 207475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 208475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 209475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki private final Runnable mRunnable = () -> { 210475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki // Process all pending items. 211475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki while (processPendingItems()) { 212475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 213475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki }; 214475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 215475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki /** 216475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * Takes a {@link PendingItem} from {@link #mPendingItems} and process it. 217475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * 218475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * Must be called {@link #mExecutor}. 219475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * 220475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki * @return true if it processed an item, false if the queue is empty. 221475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki */ 222475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki private boolean processPendingItems() { 223475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki if (ADD_DELAY_BEFORE_SAVE_FOR_TEST) { 224475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki Slog.w(TAG, "*** ARTIFICIAL SLEEP ***"); 225475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki try { 226475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki Thread.sleep(SAVE_DELAY_MS_FOR_TEST); 227475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } catch (InterruptedException e) { 228475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 229475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 230475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 231475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki // NOTE: 232475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki // Ideally we should be holding the service lock when accessing shortcut instances, 233475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki // but that could cause a deadlock so we don't do it. 234475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki // 235475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki // Instead, waitForAllSavesLocked() uses a latch to make sure changes made on this 236475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki // thread is visible on the caller thread. 237475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 238475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki ShortcutInfo shortcut = null; 239475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki try { 240475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki final PendingItem item; 241475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 242475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki synchronized (mPendingItems) { 243475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki if (mPendingItems.size() == 0) { 244475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki return false; 245475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 246475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki item = mPendingItems.pop(); 247475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 248475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 249475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki shortcut = item.shortcut; 250475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 251475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki // See if the shortcut is still relevant. (It might have been removed already.) 252475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki if (!shortcut.isIconPendingSave()) { 253475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki return true; 254475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 255475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 256475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki if (DEBUG) { 257475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki Slog.d(TAG, "Saving bitmap: " + item); 258475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 259475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 260475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki File file = null; 261475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki try { 262475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki final FileOutputStreamWithPath out = mService.openIconFileForWrite( 263475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki shortcut.getUserId(), shortcut); 264475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki file = out.getFile(); 265475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 266475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki try { 267475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki out.write(item.bytes); 268475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } finally { 269475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki IoUtils.closeQuietly(out); 270475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 271475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 272475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki shortcut.setBitmapPath(file.getAbsolutePath()); 273475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 274475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } catch (IOException | RuntimeException e) { 275475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki Slog.e(ShortcutService.TAG, "Unable to write bitmap to file", e); 276475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 277475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki if (file != null && file.exists()) { 278475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki file.delete(); 279475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 280475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki return true; 281475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 282475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } finally { 283475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki if (DEBUG) { 284475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki Slog.d(TAG, "Saved bitmap."); 285475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 286475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki if (shortcut != null) { 287475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki if (shortcut.getBitmapPath() == null) { 288475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki removeIcon(shortcut); 289475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 290475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 291475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki // Whatever happened, remove this flag. 292475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki shortcut.clearFlags(ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE); 293475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 294475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 295475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki return true; 296475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 297475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 298475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki public void dumpLocked(@NonNull PrintWriter pw, @NonNull String prefix) { 299475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki synchronized (mPendingItems) { 300475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki final int N = mPendingItems.size(); 301475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki pw.print(prefix); 302475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki pw.println("Pending saves: Num=" + N + " Executor=" + mExecutor); 303475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki 304475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki for (PendingItem item : mPendingItems) { 305475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki pw.print(prefix); 306475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki pw.print(" "); 307475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki pw.println(item); 308475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 309475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 310475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki } 311475c36539da9687d75447adb9054c98ce03c754aMakoto Onuki} 312