blob: 78eab0b0a21e427034d23b7c8aca9908a182af70 [file] [log] [blame]
Sudheer Shankaf5b36962019-10-04 16:16:13 -07001/*
2 * Copyright 2019 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 */
16package com.android.server.blob;
17
Sudheer Shankaf6e23b92020-01-15 01:51:15 -080018import static android.app.blob.BlobStoreManager.COMMIT_RESULT_ERROR;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080019import static android.app.blob.BlobStoreManager.COMMIT_RESULT_SUCCESS;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -080020import static android.app.blob.XmlTags.ATTR_VERSION;
21import static android.app.blob.XmlTags.TAG_BLOB;
22import static android.app.blob.XmlTags.TAG_BLOBS;
23import static android.app.blob.XmlTags.TAG_SESSION;
24import static android.app.blob.XmlTags.TAG_SESSIONS;
Sudheer Shanka22f0b162020-01-21 13:32:04 -080025import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
26import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
27import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
Sudheer Shanka7d4bd022020-05-20 13:49:17 -070028import static android.os.UserHandle.USER_CURRENT;
Sudheer Shanka22f0b162020-01-21 13:32:04 -080029import static android.os.UserHandle.USER_NULL;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080030
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -070031import static com.android.server.blob.BlobStoreConfig.INVALID_BLOB_ID;
32import static com.android.server.blob.BlobStoreConfig.INVALID_BLOB_SIZE;
Sudheer Shankae53e1ed2020-02-03 17:15:24 -080033import static com.android.server.blob.BlobStoreConfig.LOGV;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080034import static com.android.server.blob.BlobStoreConfig.TAG;
Sudheer Shanka1406bc82020-02-03 12:27:24 -080035import static com.android.server.blob.BlobStoreConfig.XML_VERSION_CURRENT;
Sudheer Shankaaf3f8872020-03-24 11:26:23 -070036import static com.android.server.blob.BlobStoreConfig.getAdjustedCommitTimeMs;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080037import static com.android.server.blob.BlobStoreSession.STATE_ABANDONED;
38import static com.android.server.blob.BlobStoreSession.STATE_COMMITTED;
39import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_INVALID;
40import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_VALID;
41import static com.android.server.blob.BlobStoreSession.stateToString;
Sudheer Shanka130d79c2020-03-04 13:54:36 -080042import static com.android.server.blob.BlobStoreUtils.getDescriptionResourceId;
43import static com.android.server.blob.BlobStoreUtils.getPackageResources;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080044
Sudheer Shankad2da0672019-12-20 15:51:20 -080045import android.annotation.CurrentTimeSecondsLong;
46import android.annotation.IdRes;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080047import android.annotation.IntRange;
Sudheer Shankad2da0672019-12-20 15:51:20 -080048import android.annotation.NonNull;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -080049import android.annotation.Nullable;
Sudheer Shanka5caeed52020-02-01 12:54:33 -080050import android.annotation.UserIdInt;
Sudheer Shanka7d4bd022020-05-20 13:49:17 -070051import android.app.ActivityManager;
52import android.app.ActivityManagerInternal;
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -070053import android.app.StatsManager;
Sudheer Shankad2da0672019-12-20 15:51:20 -080054import android.app.blob.BlobHandle;
Sudheer Shanka130d79c2020-03-04 13:54:36 -080055import android.app.blob.BlobInfo;
Sudheer Shankaf5b36962019-10-04 16:16:13 -070056import android.app.blob.IBlobStoreManager;
Sudheer Shankad2da0672019-12-20 15:51:20 -080057import android.app.blob.IBlobStoreSession;
Sudheer Shankac6c79942020-03-12 13:20:46 -070058import android.app.blob.LeaseInfo;
Sudheer Shanka22f0b162020-01-21 13:32:04 -080059import android.content.BroadcastReceiver;
Sudheer Shankaf5b36962019-10-04 16:16:13 -070060import android.content.Context;
Sudheer Shanka22f0b162020-01-21 13:32:04 -080061import android.content.Intent;
62import android.content.IntentFilter;
63import android.content.pm.ApplicationInfo;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080064import android.content.pm.PackageManagerInternal;
Sudheer Shankae9232d62020-01-23 16:55:34 -080065import android.content.pm.PackageStats;
Sudheer Shanka4824e3d2020-01-29 23:50:24 -080066import android.content.res.ResourceId;
Sudheer Shanka1b6b8252020-03-04 22:19:21 -080067import android.content.res.Resources;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080068import android.os.Binder;
69import android.os.Handler;
70import android.os.HandlerThread;
Sudheer Shankaa80a9972020-03-20 00:12:14 -070071import android.os.LimitExceededException;
Sudheer Shankad2da0672019-12-20 15:51:20 -080072import android.os.ParcelFileDescriptor;
Sudheer Shankaa80a9972020-03-20 00:12:14 -070073import android.os.ParcelableException;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080074import android.os.Process;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -080075import android.os.RemoteCallback;
76import android.os.SystemClock;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080077import android.os.UserHandle;
Sudheer Shanka22f0b162020-01-21 13:32:04 -080078import android.os.UserManagerInternal;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080079import android.util.ArrayMap;
Sudheer Shankaae53d112020-01-31 14:20:53 -080080import android.util.ArraySet;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -080081import android.util.AtomicFile;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080082import android.util.ExceptionUtils;
83import android.util.LongSparseArray;
84import android.util.Slog;
85import android.util.SparseArray;
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -070086import android.util.StatsEvent;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -080087import android.util.Xml;
Sudheer Shankaf5b36962019-10-04 16:16:13 -070088
Sudheer Shankaab1d4162020-01-07 10:37:50 -080089import com.android.internal.annotations.GuardedBy;
Sudheer Shankabda89c12020-01-30 15:19:24 -080090import com.android.internal.annotations.VisibleForTesting;
Sudheer Shankab76f7662020-02-27 15:17:43 -080091import com.android.internal.os.BackgroundThread;
Sudheer Shanka5caeed52020-02-01 12:54:33 -080092import com.android.internal.util.CollectionUtils;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -080093import com.android.internal.util.DumpUtils;
94import com.android.internal.util.FastXmlSerializer;
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -070095import com.android.internal.util.FrameworkStatsLog;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -080096import com.android.internal.util.IndentingPrintWriter;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080097import com.android.internal.util.Preconditions;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -080098import com.android.internal.util.XmlUtils;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080099import com.android.internal.util.function.pooled.PooledLambda;
100import com.android.server.LocalServices;
101import com.android.server.ServiceThread;
Sudheer Shankaf5b36962019-10-04 16:16:13 -0700102import com.android.server.SystemService;
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800103import com.android.server.Watchdog;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800104import com.android.server.blob.BlobMetadata.Committer;
Sudheer Shankae9232d62020-01-23 16:55:34 -0800105import com.android.server.usage.StorageStatsManagerInternal;
106import com.android.server.usage.StorageStatsManagerInternal.StorageStatsAugmenter;
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800107
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800108import org.xmlpull.v1.XmlPullParser;
109import org.xmlpull.v1.XmlSerializer;
110
111import java.io.File;
112import java.io.FileDescriptor;
113import java.io.FileInputStream;
114import java.io.FileOutputStream;
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800115import java.io.IOException;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800116import java.io.PrintWriter;
Sudheer Shanka130d79c2020-03-04 13:54:36 -0800117import java.lang.ref.WeakReference;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800118import java.nio.charset.StandardCharsets;
Sudheer Shanka60803032020-02-13 12:47:59 -0800119import java.security.SecureRandom;
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800120import java.util.ArrayList;
Sudheer Shankae53e1ed2020-02-03 17:15:24 -0800121import java.util.Arrays;
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800122import java.util.List;
Sudheer Shanka4824e3d2020-01-29 23:50:24 -0800123import java.util.Objects;
Sudheer Shanka60803032020-02-13 12:47:59 -0800124import java.util.Random;
Sudheer Shankaae53d112020-01-31 14:20:53 -0800125import java.util.Set;
Sudheer Shankae9232d62020-01-23 16:55:34 -0800126import java.util.concurrent.atomic.AtomicLong;
127import java.util.function.Consumer;
Sudheer Shanka130d79c2020-03-04 13:54:36 -0800128import java.util.function.Function;
Sudheer Shankaf5b36962019-10-04 16:16:13 -0700129
130/**
131 * Service responsible for maintaining and facilitating access to data blobs published by apps.
132 */
133public class BlobStoreManagerService extends SystemService {
134
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800135 private final Object mBlobsLock = new Object();
136
137 // Contains data of userId -> {sessionId -> {BlobStoreSession}}.
138 @GuardedBy("mBlobsLock")
139 private final SparseArray<LongSparseArray<BlobStoreSession>> mSessions = new SparseArray<>();
140
141 @GuardedBy("mBlobsLock")
142 private long mCurrentMaxSessionId;
143
144 // Contains data of userId -> {BlobHandle -> {BlobMetadata}}
145 @GuardedBy("mBlobsLock")
146 private final SparseArray<ArrayMap<BlobHandle, BlobMetadata>> mBlobsMap = new SparseArray<>();
147
Sudheer Shankaae53d112020-01-31 14:20:53 -0800148 // Contains all ids that are currently in use.
149 @GuardedBy("mBlobsLock")
Sudheer Shanka60803032020-02-13 12:47:59 -0800150 private final ArraySet<Long> mActiveBlobIds = new ArraySet<>();
151 // Contains all ids that are currently in use and those that were in use but got deleted in the
152 // current boot session.
153 @GuardedBy("mBlobsLock")
Sudheer Shankaae53d112020-01-31 14:20:53 -0800154 private final ArraySet<Long> mKnownBlobIds = new ArraySet<>();
155
Sudheer Shanka60803032020-02-13 12:47:59 -0800156 // Random number generator for new session ids.
157 private final Random mRandom = new SecureRandom();
158
Sudheer Shankad2da0672019-12-20 15:51:20 -0800159 private final Context mContext;
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800160 private final Handler mHandler;
Sudheer Shankab76f7662020-02-27 15:17:43 -0800161 private final Handler mBackgroundHandler;
Sudheer Shankabda89c12020-01-30 15:19:24 -0800162 private final Injector mInjector;
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800163 private final SessionStateChangeListener mSessionStateChangeListener =
164 new SessionStateChangeListener();
165
166 private PackageManagerInternal mPackageManagerInternal;
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -0700167 private StatsManager mStatsManager;
168 private StatsPullAtomCallbackImpl mStatsCallbackImpl = new StatsPullAtomCallbackImpl();
Sudheer Shankad2da0672019-12-20 15:51:20 -0800169
Sudheer Shanka5caeed52020-02-01 12:54:33 -0800170 private final Runnable mSaveBlobsInfoRunnable = this::writeBlobsInfo;
171 private final Runnable mSaveSessionsRunnable = this::writeBlobSessions;
172
Sudheer Shankaf5b36962019-10-04 16:16:13 -0700173 public BlobStoreManagerService(Context context) {
Sudheer Shankabda89c12020-01-30 15:19:24 -0800174 this(context, new Injector());
175 }
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800176
Sudheer Shankabda89c12020-01-30 15:19:24 -0800177 @VisibleForTesting
178 BlobStoreManagerService(Context context, Injector injector) {
179 super(context);
180
181 mContext = context;
182 mInjector = injector;
183 mHandler = mInjector.initializeMessageHandler();
Sudheer Shankab76f7662020-02-27 15:17:43 -0800184 mBackgroundHandler = mInjector.getBackgroundHandler();
Sudheer Shankabda89c12020-01-30 15:19:24 -0800185 }
186
187 private static Handler initializeMessageHandler() {
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800188 final HandlerThread handlerThread = new ServiceThread(TAG,
Sudheer Shankab76f7662020-02-27 15:17:43 -0800189 Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */);
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800190 handlerThread.start();
Sudheer Shankabda89c12020-01-30 15:19:24 -0800191 final Handler handler = new Handler(handlerThread.getLooper());
192 Watchdog.getInstance().addThread(handler);
193 return handler;
Sudheer Shankaf5b36962019-10-04 16:16:13 -0700194 }
195
196 @Override
197 public void onStart() {
198 publishBinderService(Context.BLOB_STORE_SERVICE, new Stub());
Sudheer Shankaae53d112020-01-31 14:20:53 -0800199 LocalServices.addService(BlobStoreManagerInternal.class, new LocalService());
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800200
201 mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -0700202 mStatsManager = getContext().getSystemService(StatsManager.class);
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800203 registerReceivers();
Sudheer Shankae9232d62020-01-23 16:55:34 -0800204 LocalServices.getService(StorageStatsManagerInternal.class)
205 .registerStorageStatsAugmenter(new BlobStorageStatsAugmenter(), TAG);
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800206 }
207
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800208 @Override
209 public void onBootPhase(int phase) {
Sudheer Shanka364364b2020-02-19 17:56:09 -0800210 if (phase == PHASE_ACTIVITY_MANAGER_READY) {
211 BlobStoreConfig.initialize(mContext);
212 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800213 synchronized (mBlobsLock) {
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800214 final SparseArray<SparseArray<String>> allPackages = getAllPackages();
215 readBlobSessionsLocked(allPackages);
216 readBlobsInfoLocked(allPackages);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800217 }
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -0700218 registerBlobStorePuller();
Sudheer Shankaae53d112020-01-31 14:20:53 -0800219 } else if (phase == PHASE_BOOT_COMPLETED) {
220 BlobStoreIdleJobService.schedule(mContext);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800221 }
222 }
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800223
224 @GuardedBy("mBlobsLock")
225 private long generateNextSessionIdLocked() {
Sudheer Shanka60803032020-02-13 12:47:59 -0800226 // Logic borrowed from PackageInstallerService.
227 int n = 0;
228 long sessionId;
229 do {
Sudheer Shanka36b25a12020-06-13 22:27:28 -0700230 final long randomLong = mRandom.nextLong();
231 sessionId = (randomLong == Long.MIN_VALUE) ? INVALID_BLOB_ID : Math.abs(randomLong);
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -0700232 if (mKnownBlobIds.indexOf(sessionId) < 0 && sessionId != INVALID_BLOB_ID) {
Sudheer Shanka60803032020-02-13 12:47:59 -0800233 return sessionId;
234 }
235 } while (n++ < 32);
236 throw new IllegalStateException("Failed to allocate session ID");
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800237 }
238
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800239 private void registerReceivers() {
Sudheer Shankafd2611e2020-03-05 02:18:15 -0800240 final IntentFilter packageChangedFilter = new IntentFilter();
241 packageChangedFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
242 packageChangedFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
243 packageChangedFilter.addDataScheme("package");
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800244 mContext.registerReceiverAsUser(new PackageChangedReceiver(), UserHandle.ALL,
Sudheer Shankafd2611e2020-03-05 02:18:15 -0800245 packageChangedFilter, null, mHandler);
246
247 final IntentFilter userActionFilter = new IntentFilter();
248 userActionFilter.addAction(Intent.ACTION_USER_REMOVED);
249 mContext.registerReceiverAsUser(new UserActionReceiver(), UserHandle.ALL,
250 userActionFilter, null, mHandler);
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800251 }
252
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800253 @GuardedBy("mBlobsLock")
254 private LongSparseArray<BlobStoreSession> getUserSessionsLocked(int userId) {
255 LongSparseArray<BlobStoreSession> userSessions = mSessions.get(userId);
256 if (userSessions == null) {
257 userSessions = new LongSparseArray<>();
258 mSessions.put(userId, userSessions);
259 }
260 return userSessions;
261 }
262
263 @GuardedBy("mBlobsLock")
264 private ArrayMap<BlobHandle, BlobMetadata> getUserBlobsLocked(int userId) {
265 ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.get(userId);
266 if (userBlobs == null) {
267 userBlobs = new ArrayMap<>();
268 mBlobsMap.put(userId, userBlobs);
269 }
270 return userBlobs;
271 }
272
Sudheer Shankabda89c12020-01-30 15:19:24 -0800273 @VisibleForTesting
274 void addUserSessionsForTest(LongSparseArray<BlobStoreSession> userSessions, int userId) {
275 synchronized (mBlobsLock) {
276 mSessions.put(userId, userSessions);
277 }
278 }
279
280 @VisibleForTesting
281 void addUserBlobsForTest(ArrayMap<BlobHandle, BlobMetadata> userBlobs, int userId) {
282 synchronized (mBlobsLock) {
283 mBlobsMap.put(userId, userBlobs);
284 }
285 }
286
Sudheer Shankaae53d112020-01-31 14:20:53 -0800287 @VisibleForTesting
Sudheer Shanka60803032020-02-13 12:47:59 -0800288 void addActiveIdsForTest(long... activeIds) {
Sudheer Shankaae53d112020-01-31 14:20:53 -0800289 synchronized (mBlobsLock) {
Sudheer Shanka60803032020-02-13 12:47:59 -0800290 for (long id : activeIds) {
291 addActiveBlobIdLocked(id);
Sudheer Shankaae53d112020-01-31 14:20:53 -0800292 }
293 }
294 }
295
296 @VisibleForTesting
Sudheer Shanka60803032020-02-13 12:47:59 -0800297 Set<Long> getActiveIdsForTest() {
298 synchronized (mBlobsLock) {
299 return mActiveBlobIds;
300 }
301 }
302
303 @VisibleForTesting
Sudheer Shankaae53d112020-01-31 14:20:53 -0800304 Set<Long> getKnownIdsForTest() {
305 synchronized (mBlobsLock) {
306 return mKnownBlobIds;
307 }
308 }
309
310 @GuardedBy("mBlobsLock")
311 private void addSessionForUserLocked(BlobStoreSession session, int userId) {
312 getUserSessionsLocked(userId).put(session.getSessionId(), session);
Sudheer Shanka60803032020-02-13 12:47:59 -0800313 addActiveBlobIdLocked(session.getSessionId());
Sudheer Shankaae53d112020-01-31 14:20:53 -0800314 }
315
316 @GuardedBy("mBlobsLock")
317 private void addBlobForUserLocked(BlobMetadata blobMetadata, int userId) {
318 addBlobForUserLocked(blobMetadata, getUserBlobsLocked(userId));
319 }
320
321 @GuardedBy("mBlobsLock")
322 private void addBlobForUserLocked(BlobMetadata blobMetadata,
323 ArrayMap<BlobHandle, BlobMetadata> userBlobs) {
324 userBlobs.put(blobMetadata.getBlobHandle(), blobMetadata);
Sudheer Shanka60803032020-02-13 12:47:59 -0800325 addActiveBlobIdLocked(blobMetadata.getBlobId());
326 }
327
328 @GuardedBy("mBlobsLock")
329 private void addActiveBlobIdLocked(long id) {
330 mActiveBlobIds.add(id);
331 mKnownBlobIds.add(id);
Sudheer Shankaae53d112020-01-31 14:20:53 -0800332 }
333
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800334 private long createSessionInternal(BlobHandle blobHandle,
335 int callingUid, String callingPackage) {
336 synchronized (mBlobsLock) {
337 // TODO: throw if there is already an active session associated with blobHandle.
338 final long sessionId = generateNextSessionIdLocked();
339 final BlobStoreSession session = new BlobStoreSession(mContext,
340 sessionId, blobHandle, callingUid, callingPackage,
341 mSessionStateChangeListener);
Sudheer Shankaae53d112020-01-31 14:20:53 -0800342 addSessionForUserLocked(session, UserHandle.getUserId(callingUid));
Sudheer Shankae53e1ed2020-02-03 17:15:24 -0800343 if (LOGV) {
344 Slog.v(TAG, "Created session for " + blobHandle
345 + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
346 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800347 writeBlobSessionsAsync();
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800348 return sessionId;
349 }
350 }
351
352 private BlobStoreSession openSessionInternal(long sessionId,
353 int callingUid, String callingPackage) {
354 final BlobStoreSession session;
355 synchronized (mBlobsLock) {
356 session = getUserSessionsLocked(
357 UserHandle.getUserId(callingUid)).get(sessionId);
358 if (session == null || !session.hasAccess(callingUid, callingPackage)
359 || session.isFinalized()) {
360 throw new SecurityException("Session not found: " + sessionId);
361 }
362 }
363 session.open();
364 return session;
365 }
366
Sudheer Shanka0b872152020-02-27 10:13:08 -0800367 private void abandonSessionInternal(long sessionId,
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800368 int callingUid, String callingPackage) {
369 synchronized (mBlobsLock) {
370 final BlobStoreSession session = openSessionInternal(sessionId,
371 callingUid, callingPackage);
372 session.open();
373 session.abandon();
Sudheer Shankae53e1ed2020-02-03 17:15:24 -0800374 if (LOGV) {
Sudheer Shanka0b872152020-02-27 10:13:08 -0800375 Slog.v(TAG, "Abandoned session with id " + sessionId
Sudheer Shankae53e1ed2020-02-03 17:15:24 -0800376 + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
377 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800378 writeBlobSessionsAsync();
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800379 }
380 }
381
382 private ParcelFileDescriptor openBlobInternal(BlobHandle blobHandle, int callingUid,
383 String callingPackage) throws IOException {
384 synchronized (mBlobsLock) {
385 final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
386 .get(blobHandle);
387 if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
388 callingPackage, callingUid)) {
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -0700389 if (blobMetadata == null) {
390 FrameworkStatsLog.write(FrameworkStatsLog.BLOB_OPENED, callingUid,
391 INVALID_BLOB_ID, INVALID_BLOB_SIZE,
392 FrameworkStatsLog.BLOB_OPENED__RESULT__BLOB_DNE);
393 } else {
394 FrameworkStatsLog.write(FrameworkStatsLog.BLOB_OPENED, callingUid,
395 blobMetadata.getBlobId(), blobMetadata.getSize(),
396 FrameworkStatsLog.BLOB_LEASED__RESULT__ACCESS_NOT_ALLOWED);
397 }
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800398 throw new SecurityException("Caller not allowed to access " + blobHandle
399 + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
400 }
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -0700401
402 FrameworkStatsLog.write(FrameworkStatsLog.BLOB_OPENED, callingUid,
403 blobMetadata.getBlobId(), blobMetadata.getSize(),
404 FrameworkStatsLog.BLOB_OPENED__RESULT__SUCCESS);
405
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800406 return blobMetadata.openForRead(callingPackage);
407 }
408 }
409
410 private void acquireLeaseInternal(BlobHandle blobHandle, int descriptionResId,
Sudheer Shanka1406bc82020-02-03 12:27:24 -0800411 CharSequence description, long leaseExpiryTimeMillis,
412 int callingUid, String callingPackage) {
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800413 synchronized (mBlobsLock) {
414 final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
415 .get(blobHandle);
416 if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
417 callingPackage, callingUid)) {
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -0700418 if (blobMetadata == null) {
419 FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
420 INVALID_BLOB_ID, INVALID_BLOB_SIZE,
421 FrameworkStatsLog.BLOB_LEASED__RESULT__BLOB_DNE);
422 } else {
423 FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
424 blobMetadata.getBlobId(), blobMetadata.getSize(),
425 FrameworkStatsLog.BLOB_LEASED__RESULT__ACCESS_NOT_ALLOWED);
426 }
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800427 throw new SecurityException("Caller not allowed to access " + blobHandle
428 + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
429 }
Sudheer Shanka51a6c6e2020-02-18 17:28:12 -0800430 if (leaseExpiryTimeMillis != 0 && blobHandle.expiryTimeMillis != 0
431 && leaseExpiryTimeMillis > blobHandle.expiryTimeMillis) {
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -0700432
433 FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
434 blobMetadata.getBlobId(), blobMetadata.getSize(),
435 FrameworkStatsLog.BLOB_LEASED__RESULT__LEASE_EXPIRY_INVALID);
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800436 throw new IllegalArgumentException(
437 "Lease expiry cannot be later than blobs expiry time");
438 }
Sudheer Shankaa80a9972020-03-20 00:12:14 -0700439 if (blobMetadata.getSize()
440 > getRemainingLeaseQuotaBytesInternal(callingUid, callingPackage)) {
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -0700441
442 FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
443 blobMetadata.getBlobId(), blobMetadata.getSize(),
444 FrameworkStatsLog.BLOB_LEASED__RESULT__DATA_SIZE_LIMIT_EXCEEDED);
Sudheer Shankaa80a9972020-03-20 00:12:14 -0700445 throw new LimitExceededException("Total amount of data with an active lease"
Sudheer Shanka364364b2020-02-19 17:56:09 -0800446 + " is exceeding the max limit");
447 }
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -0700448
449 FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
450 blobMetadata.getBlobId(), blobMetadata.getSize(),
451 FrameworkStatsLog.BLOB_LEASED__RESULT__SUCCESS);
452
Sudheer Shanka0d348262020-03-25 11:54:16 -0700453 blobMetadata.addOrReplaceLeasee(callingPackage, callingUid,
Sudheer Shanka1406bc82020-02-03 12:27:24 -0800454 descriptionResId, description, leaseExpiryTimeMillis);
Sudheer Shankae53e1ed2020-02-03 17:15:24 -0800455 if (LOGV) {
456 Slog.v(TAG, "Acquired lease on " + blobHandle
457 + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
458 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800459 writeBlobsInfoAsync();
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800460 }
461 }
462
Sudheer Shanka364364b2020-02-19 17:56:09 -0800463 @VisibleForTesting
464 @GuardedBy("mBlobsLock")
465 long getTotalUsageBytesLocked(int callingUid, String callingPackage) {
466 final AtomicLong totalBytes = new AtomicLong(0);
467 forEachBlobInUser((blobMetadata) -> {
468 if (blobMetadata.isALeasee(callingPackage, callingUid)) {
469 totalBytes.getAndAdd(blobMetadata.getSize());
470 }
471 }, UserHandle.getUserId(callingUid));
472 return totalBytes.get();
473 }
474
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800475 private void releaseLeaseInternal(BlobHandle blobHandle, int callingUid,
476 String callingPackage) {
477 synchronized (mBlobsLock) {
Sudheer Shankac0fd5fa2020-03-15 23:31:37 -0700478 final ArrayMap<BlobHandle, BlobMetadata> userBlobs =
479 getUserBlobsLocked(UserHandle.getUserId(callingUid));
480 final BlobMetadata blobMetadata = userBlobs.get(blobHandle);
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800481 if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
482 callingPackage, callingUid)) {
483 throw new SecurityException("Caller not allowed to access " + blobHandle
484 + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
485 }
486 blobMetadata.removeLeasee(callingPackage, callingUid);
Sudheer Shankae53e1ed2020-02-03 17:15:24 -0800487 if (LOGV) {
488 Slog.v(TAG, "Released lease on " + blobHandle
489 + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
490 }
Sudheer Shankac0fd5fa2020-03-15 23:31:37 -0700491 if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
492 deleteBlobLocked(blobMetadata);
493 userBlobs.remove(blobHandle);
494 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800495 writeBlobsInfoAsync();
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800496 }
497 }
498
Sudheer Shankaa80a9972020-03-20 00:12:14 -0700499 private long getRemainingLeaseQuotaBytesInternal(int callingUid, String callingPackage) {
500 synchronized (mBlobsLock) {
501 final long remainingQuota = BlobStoreConfig.getAppDataBytesLimit()
502 - getTotalUsageBytesLocked(callingUid, callingPackage);
503 return remainingQuota > 0 ? remainingQuota : 0;
504 }
505 }
506
Sudheer Shanka130d79c2020-03-04 13:54:36 -0800507 private List<BlobInfo> queryBlobsForUserInternal(int userId) {
508 final ArrayList<BlobInfo> blobInfos = new ArrayList<>();
509 synchronized (mBlobsLock) {
510 final ArrayMap<String, WeakReference<Resources>> resources = new ArrayMap<>();
511 final Function<String, Resources> resourcesGetter = (packageName) -> {
512 final WeakReference<Resources> resourcesRef = resources.get(packageName);
513 Resources packageResources = resourcesRef == null ? null : resourcesRef.get();
514 if (packageResources == null) {
515 packageResources = getPackageResources(mContext, packageName, userId);
516 resources.put(packageName, new WeakReference<>(packageResources));
517 }
518 return packageResources;
519 };
520 getUserBlobsLocked(userId).forEach((blobHandle, blobMetadata) -> {
Sudheer Shankac6c79942020-03-12 13:20:46 -0700521 final ArrayList<LeaseInfo> leaseInfos = new ArrayList<>();
Sudheer Shanka130d79c2020-03-04 13:54:36 -0800522 blobMetadata.forEachLeasee(leasee -> {
523 final int descriptionResId = leasee.descriptionResEntryName == null
524 ? Resources.ID_NULL
525 : getDescriptionResourceId(resourcesGetter.apply(leasee.packageName),
526 leasee.descriptionResEntryName, leasee.packageName);
Sudheer Shankaf25dd772020-06-05 00:47:26 -0700527 final long expiryTimeMs = leasee.expiryTimeMillis == 0
528 ? blobHandle.getExpiryTimeMillis() : leasee.expiryTimeMillis;
529 leaseInfos.add(new LeaseInfo(leasee.packageName, expiryTimeMs,
Sudheer Shanka130d79c2020-03-04 13:54:36 -0800530 descriptionResId, leasee.description));
531 });
532 blobInfos.add(new BlobInfo(blobMetadata.getBlobId(),
Sudheer Shanka65310792020-06-05 02:03:57 -0700533 blobHandle.getExpiryTimeMillis(), blobHandle.getLabel(),
534 blobMetadata.getSize(), leaseInfos));
Sudheer Shanka130d79c2020-03-04 13:54:36 -0800535 });
536 }
537 return blobInfos;
538 }
539
540 private void deleteBlobInternal(long blobId, int callingUid) {
541 synchronized (mBlobsLock) {
542 final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(
543 UserHandle.getUserId(callingUid));
544 userBlobs.entrySet().removeIf(entry -> {
545 final BlobMetadata blobMetadata = entry.getValue();
546 return blobMetadata.getBlobId() == blobId;
547 });
548 writeBlobsInfoAsync();
549 }
550 }
551
Sudheer Shankac6c79942020-03-12 13:20:46 -0700552 private List<BlobHandle> getLeasedBlobsInternal(int callingUid,
553 @NonNull String callingPackage) {
554 final ArrayList<BlobHandle> leasedBlobs = new ArrayList<>();
555 forEachBlobInUser(blobMetadata -> {
556 if (blobMetadata.isALeasee(callingPackage, callingUid)) {
557 leasedBlobs.add(blobMetadata.getBlobHandle());
558 }
559 }, UserHandle.getUserId(callingUid));
560 return leasedBlobs;
561 }
562
563 private LeaseInfo getLeaseInfoInternal(BlobHandle blobHandle,
564 int callingUid, @NonNull String callingPackage) {
565 synchronized (mBlobsLock) {
566 final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
567 .get(blobHandle);
568 if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
569 callingPackage, callingUid)) {
570 throw new SecurityException("Caller not allowed to access " + blobHandle
571 + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
572 }
573 return blobMetadata.getLeaseInfo(callingPackage, callingUid);
574 }
575 }
576
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800577 private void verifyCallingPackage(int callingUid, String callingPackage) {
578 if (mPackageManagerInternal.getPackageUid(
579 callingPackage, 0, UserHandle.getUserId(callingUid)) != callingUid) {
580 throw new SecurityException("Specified calling package [" + callingPackage
581 + "] does not match the calling uid " + callingUid);
582 }
583 }
584
585 class SessionStateChangeListener {
586 public void onStateChanged(@NonNull BlobStoreSession session) {
587 mHandler.post(PooledLambda.obtainRunnable(
588 BlobStoreManagerService::onStateChangedInternal,
Sudheer Shankab76f7662020-02-27 15:17:43 -0800589 BlobStoreManagerService.this, session).recycleOnUse());
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800590 }
591 }
592
593 private void onStateChangedInternal(@NonNull BlobStoreSession session) {
Sudheer Shankaab4a8ae2020-02-18 15:11:42 -0800594 switch (session.getState()) {
595 case STATE_ABANDONED:
596 case STATE_VERIFIED_INVALID:
597 session.getSessionFile().delete();
598 synchronized (mBlobsLock) {
Sudheer Shankabda89c12020-01-30 15:19:24 -0800599 getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
600 .remove(session.getSessionId());
Sudheer Shanka60803032020-02-13 12:47:59 -0800601 mActiveBlobIds.remove(session.getSessionId());
Sudheer Shankae53e1ed2020-02-03 17:15:24 -0800602 if (LOGV) {
603 Slog.v(TAG, "Session is invalid; deleted " + session);
604 }
Sudheer Shankaab4a8ae2020-02-18 15:11:42 -0800605 }
606 break;
607 case STATE_COMMITTED:
Sudheer Shankab76f7662020-02-27 15:17:43 -0800608 mBackgroundHandler.post(() -> {
609 session.computeDigest();
610 mHandler.post(PooledLambda.obtainRunnable(
611 BlobStoreSession::verifyBlobData, session).recycleOnUse());
612 });
Sudheer Shankaab4a8ae2020-02-18 15:11:42 -0800613 break;
614 case STATE_VERIFIED_VALID:
615 synchronized (mBlobsLock) {
Sudheer Shankabda89c12020-01-30 15:19:24 -0800616 final int userId = UserHandle.getUserId(session.getOwnerUid());
Sudheer Shankaab4a8ae2020-02-18 15:11:42 -0800617 final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(
618 userId);
Sudheer Shankabda89c12020-01-30 15:19:24 -0800619 BlobMetadata blob = userBlobs.get(session.getBlobHandle());
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800620 if (blob == null) {
Sudheer Shankaaf3f8872020-03-24 11:26:23 -0700621 blob = new BlobMetadata(mContext, session.getSessionId(),
622 session.getBlobHandle(), userId);
Sudheer Shankaae53d112020-01-31 14:20:53 -0800623 addBlobForUserLocked(blob, userBlobs);
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800624 }
Sudheer Shankaaf3f8872020-03-24 11:26:23 -0700625 final Committer existingCommitter = blob.getExistingCommitter(
626 session.getOwnerPackageName(), session.getOwnerUid());
627 final long existingCommitTimeMs =
628 (existingCommitter == null) ? 0 : existingCommitter.getCommitTimeMs();
Sudheer Shankabda89c12020-01-30 15:19:24 -0800629 final Committer newCommitter = new Committer(session.getOwnerPackageName(),
Sudheer Shankaaf3f8872020-03-24 11:26:23 -0700630 session.getOwnerUid(), session.getBlobAccessMode(),
631 getAdjustedCommitTimeMs(existingCommitTimeMs,
632 System.currentTimeMillis()));
Sudheer Shanka0d348262020-03-25 11:54:16 -0700633 blob.addOrReplaceCommitter(newCommitter);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800634 try {
635 writeBlobsInfoLocked();
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -0700636 FrameworkStatsLog.write(FrameworkStatsLog.BLOB_COMMITTED,
637 session.getOwnerUid(), blob.getBlobId(), blob.getSize(),
638 FrameworkStatsLog.BLOB_COMMITTED__RESULT__SUCCESS);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800639 session.sendCommitCallbackResult(COMMIT_RESULT_SUCCESS);
640 } catch (Exception e) {
Sudheer Shanka0d348262020-03-25 11:54:16 -0700641 if (existingCommitter == null) {
642 blob.removeCommitter(newCommitter);
643 } else {
644 blob.addOrReplaceCommitter(existingCommitter);
645 }
Sudheer Shanka16806422020-06-05 15:36:37 -0700646 Slog.d(TAG, "Error committing the blob", e);
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -0700647 FrameworkStatsLog.write(FrameworkStatsLog.BLOB_COMMITTED,
648 session.getOwnerUid(), blob.getBlobId(), blob.getSize(),
649 FrameworkStatsLog.BLOB_COMMITTED__RESULT__ERROR_DURING_COMMIT);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800650 session.sendCommitCallbackResult(COMMIT_RESULT_ERROR);
Sudheer Shanka36b25a12020-06-13 22:27:28 -0700651 // If the commit fails and this blob data didn't exist before, delete it.
652 // But if it is a recommit, just leave it as is.
653 if (session.getSessionId() == blob.getBlobId()) {
654 deleteBlobLocked(blob);
655 userBlobs.remove(blob.getBlobHandle());
656 }
657 }
658 // Delete redundant data from recommits.
659 if (session.getSessionId() != blob.getBlobId()) {
660 session.getSessionFile().delete();
661 mActiveBlobIds.remove(session.getSessionId());
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800662 }
Sudheer Shankabda89c12020-01-30 15:19:24 -0800663 getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
664 .remove(session.getSessionId());
Sudheer Shankae53e1ed2020-02-03 17:15:24 -0800665 if (LOGV) {
666 Slog.v(TAG, "Successfully committed session " + session);
667 }
Sudheer Shankaab4a8ae2020-02-18 15:11:42 -0800668 }
669 break;
670 default:
671 Slog.wtf(TAG, "Invalid session state: "
672 + stateToString(session.getState()));
673 }
674 synchronized (mBlobsLock) {
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800675 try {
676 writeBlobSessionsLocked();
677 } catch (Exception e) {
678 // already logged, ignore.
679 }
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800680 }
Sudheer Shankaf5b36962019-10-04 16:16:13 -0700681 }
682
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800683 @GuardedBy("mBlobsLock")
684 private void writeBlobSessionsLocked() throws Exception {
685 final AtomicFile sessionsIndexFile = prepareSessionsIndexFile();
686 if (sessionsIndexFile == null) {
687 Slog.wtf(TAG, "Error creating sessions index file");
688 return;
689 }
690 FileOutputStream fos = null;
691 try {
692 fos = sessionsIndexFile.startWrite(SystemClock.uptimeMillis());
693 final XmlSerializer out = new FastXmlSerializer();
694 out.setOutput(fos, StandardCharsets.UTF_8.name());
695 out.startDocument(null, true);
696 out.startTag(null, TAG_SESSIONS);
Sudheer Shanka1406bc82020-02-03 12:27:24 -0800697 XmlUtils.writeIntAttribute(out, ATTR_VERSION, XML_VERSION_CURRENT);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800698
699 for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
700 final LongSparseArray<BlobStoreSession> userSessions =
701 mSessions.valueAt(i);
702 for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
703 out.startTag(null, TAG_SESSION);
704 userSessions.valueAt(j).writeToXml(out);
705 out.endTag(null, TAG_SESSION);
706 }
707 }
708
709 out.endTag(null, TAG_SESSIONS);
710 out.endDocument();
711 sessionsIndexFile.finishWrite(fos);
Sudheer Shankae53e1ed2020-02-03 17:15:24 -0800712 if (LOGV) {
713 Slog.v(TAG, "Finished persisting sessions data");
714 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800715 } catch (Exception e) {
716 sessionsIndexFile.failWrite(fos);
717 Slog.wtf(TAG, "Error writing sessions data", e);
718 throw e;
719 }
720 }
721
722 @GuardedBy("mBlobsLock")
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800723 private void readBlobSessionsLocked(SparseArray<SparseArray<String>> allPackages) {
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800724 if (!BlobStoreConfig.getBlobStoreRootDir().exists()) {
725 return;
726 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800727 final AtomicFile sessionsIndexFile = prepareSessionsIndexFile();
728 if (sessionsIndexFile == null) {
729 Slog.wtf(TAG, "Error creating sessions index file");
730 return;
Sudheer Shanka9c7eed42020-02-19 11:45:59 -0800731 } else if (!sessionsIndexFile.exists()) {
732 Slog.w(TAG, "Sessions index file not available: " + sessionsIndexFile.getBaseFile());
733 return;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800734 }
735
736 mSessions.clear();
737 try (FileInputStream fis = sessionsIndexFile.openRead()) {
738 final XmlPullParser in = Xml.newPullParser();
739 in.setInput(fis, StandardCharsets.UTF_8.name());
740 XmlUtils.beginDocument(in, TAG_SESSIONS);
Sudheer Shanka1406bc82020-02-03 12:27:24 -0800741 final int version = XmlUtils.readIntAttribute(in, ATTR_VERSION);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800742 while (true) {
743 XmlUtils.nextElement(in);
744 if (in.getEventType() == XmlPullParser.END_DOCUMENT) {
745 break;
746 }
747
748 if (TAG_SESSION.equals(in.getName())) {
749 final BlobStoreSession session = BlobStoreSession.createFromXml(
Sudheer Shanka1406bc82020-02-03 12:27:24 -0800750 in, version, mContext, mSessionStateChangeListener);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800751 if (session == null) {
752 continue;
753 }
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800754 final SparseArray<String> userPackages = allPackages.get(
Sudheer Shankabda89c12020-01-30 15:19:24 -0800755 UserHandle.getUserId(session.getOwnerUid()));
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800756 if (userPackages != null
Sudheer Shankabda89c12020-01-30 15:19:24 -0800757 && session.getOwnerPackageName().equals(
758 userPackages.get(session.getOwnerUid()))) {
Sudheer Shankaae53d112020-01-31 14:20:53 -0800759 addSessionForUserLocked(session,
760 UserHandle.getUserId(session.getOwnerUid()));
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800761 } else {
762 // Unknown package or the session data does not belong to this package.
763 session.getSessionFile().delete();
764 }
Sudheer Shankabda89c12020-01-30 15:19:24 -0800765 mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, session.getSessionId());
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800766 }
767 }
Sudheer Shankae53e1ed2020-02-03 17:15:24 -0800768 if (LOGV) {
769 Slog.v(TAG, "Finished reading sessions data");
770 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800771 } catch (Exception e) {
772 Slog.wtf(TAG, "Error reading sessions data", e);
773 }
774 }
775
776 @GuardedBy("mBlobsLock")
777 private void writeBlobsInfoLocked() throws Exception {
778 final AtomicFile blobsIndexFile = prepareBlobsIndexFile();
779 if (blobsIndexFile == null) {
780 Slog.wtf(TAG, "Error creating blobs index file");
781 return;
782 }
783 FileOutputStream fos = null;
784 try {
785 fos = blobsIndexFile.startWrite(SystemClock.uptimeMillis());
786 final XmlSerializer out = new FastXmlSerializer();
787 out.setOutput(fos, StandardCharsets.UTF_8.name());
788 out.startDocument(null, true);
789 out.startTag(null, TAG_BLOBS);
Sudheer Shanka1406bc82020-02-03 12:27:24 -0800790 XmlUtils.writeIntAttribute(out, ATTR_VERSION, XML_VERSION_CURRENT);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800791
792 for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
793 final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
794 for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
795 out.startTag(null, TAG_BLOB);
796 userBlobs.valueAt(j).writeToXml(out);
797 out.endTag(null, TAG_BLOB);
798 }
799 }
800
801 out.endTag(null, TAG_BLOBS);
802 out.endDocument();
803 blobsIndexFile.finishWrite(fos);
Sudheer Shankae53e1ed2020-02-03 17:15:24 -0800804 if (LOGV) {
805 Slog.v(TAG, "Finished persisting blobs data");
806 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800807 } catch (Exception e) {
808 blobsIndexFile.failWrite(fos);
809 Slog.wtf(TAG, "Error writing blobs data", e);
810 throw e;
811 }
812 }
813
814 @GuardedBy("mBlobsLock")
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800815 private void readBlobsInfoLocked(SparseArray<SparseArray<String>> allPackages) {
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800816 if (!BlobStoreConfig.getBlobStoreRootDir().exists()) {
817 return;
818 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800819 final AtomicFile blobsIndexFile = prepareBlobsIndexFile();
820 if (blobsIndexFile == null) {
821 Slog.wtf(TAG, "Error creating blobs index file");
822 return;
Sudheer Shanka9c7eed42020-02-19 11:45:59 -0800823 } else if (!blobsIndexFile.exists()) {
824 Slog.w(TAG, "Blobs index file not available: " + blobsIndexFile.getBaseFile());
825 return;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800826 }
827
828 mBlobsMap.clear();
829 try (FileInputStream fis = blobsIndexFile.openRead()) {
830 final XmlPullParser in = Xml.newPullParser();
831 in.setInput(fis, StandardCharsets.UTF_8.name());
832 XmlUtils.beginDocument(in, TAG_BLOBS);
Sudheer Shanka1406bc82020-02-03 12:27:24 -0800833 final int version = XmlUtils.readIntAttribute(in, ATTR_VERSION);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800834 while (true) {
835 XmlUtils.nextElement(in);
836 if (in.getEventType() == XmlPullParser.END_DOCUMENT) {
837 break;
838 }
839
840 if (TAG_BLOB.equals(in.getName())) {
Sudheer Shanka1406bc82020-02-03 12:27:24 -0800841 final BlobMetadata blobMetadata = BlobMetadata.createFromXml(
842 in, version, mContext);
Sudheer Shankaae53d112020-01-31 14:20:53 -0800843 final SparseArray<String> userPackages = allPackages.get(
844 blobMetadata.getUserId());
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800845 if (userPackages == null) {
846 blobMetadata.getBlobFile().delete();
847 } else {
Sudheer Shankaae53d112020-01-31 14:20:53 -0800848 addBlobForUserLocked(blobMetadata, blobMetadata.getUserId());
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800849 blobMetadata.removeInvalidCommitters(userPackages);
850 blobMetadata.removeInvalidLeasees(userPackages);
851 }
Sudheer Shankaae53d112020-01-31 14:20:53 -0800852 mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, blobMetadata.getBlobId());
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800853 }
854 }
Sudheer Shankae53e1ed2020-02-03 17:15:24 -0800855 if (LOGV) {
856 Slog.v(TAG, "Finished reading blobs data");
857 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800858 } catch (Exception e) {
859 Slog.wtf(TAG, "Error reading blobs data", e);
860 }
861 }
862
863 private void writeBlobsInfo() {
864 synchronized (mBlobsLock) {
865 try {
866 writeBlobsInfoLocked();
867 } catch (Exception e) {
868 // Already logged, ignore
869 }
870 }
871 }
872
873 private void writeBlobsInfoAsync() {
Sudheer Shanka5caeed52020-02-01 12:54:33 -0800874 if (!mHandler.hasCallbacks(mSaveBlobsInfoRunnable)) {
875 mHandler.post(mSaveBlobsInfoRunnable);
876 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800877 }
878
879 private void writeBlobSessions() {
880 synchronized (mBlobsLock) {
881 try {
882 writeBlobSessionsLocked();
883 } catch (Exception e) {
884 // Already logged, ignore
885 }
886 }
887 }
888
889 private void writeBlobSessionsAsync() {
Sudheer Shanka5caeed52020-02-01 12:54:33 -0800890 if (!mHandler.hasCallbacks(mSaveSessionsRunnable)) {
891 mHandler.post(mSaveSessionsRunnable);
892 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800893 }
894
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800895 private int getPackageUid(String packageName, int userId) {
896 final int uid = mPackageManagerInternal.getPackageUid(
897 packageName,
898 MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_UNINSTALLED_PACKAGES,
899 userId);
900 return uid;
901 }
902
903 private SparseArray<SparseArray<String>> getAllPackages() {
904 final SparseArray<SparseArray<String>> allPackages = new SparseArray<>();
905 final int[] allUsers = LocalServices.getService(UserManagerInternal.class).getUserIds();
906 for (int userId : allUsers) {
907 final SparseArray<String> userPackages = new SparseArray<>();
908 allPackages.put(userId, userPackages);
909 final List<ApplicationInfo> applicationInfos = mPackageManagerInternal
910 .getInstalledApplications(
911 MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
912 | MATCH_UNINSTALLED_PACKAGES,
913 userId, Process.myUid());
914 for (int i = 0, count = applicationInfos.size(); i < count; ++i) {
915 final ApplicationInfo applicationInfo = applicationInfos.get(i);
916 userPackages.put(applicationInfo.uid, applicationInfo.packageName);
917 }
918 }
919 return allPackages;
920 }
921
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800922 AtomicFile prepareSessionsIndexFile() {
923 final File file = BlobStoreConfig.prepareSessionIndexFile();
924 if (file == null) {
925 return null;
926 }
927 return new AtomicFile(file, "session_index" /* commitLogTag */);
928 }
929
930 AtomicFile prepareBlobsIndexFile() {
931 final File file = BlobStoreConfig.prepareBlobsIndexFile();
932 if (file == null) {
933 return null;
934 }
935 return new AtomicFile(file, "blobs_index" /* commitLogTag */);
936 }
937
Sudheer Shanka5caeed52020-02-01 12:54:33 -0800938 @VisibleForTesting
Sudheer Shankabda89c12020-01-30 15:19:24 -0800939 void handlePackageRemoved(String packageName, int uid) {
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800940 synchronized (mBlobsLock) {
941 // Clean up any pending sessions
942 final LongSparseArray<BlobStoreSession> userSessions =
943 getUserSessionsLocked(UserHandle.getUserId(uid));
Sudheer Shankafd2611e2020-03-05 02:18:15 -0800944 userSessions.removeIf((sessionId, blobStoreSession) -> {
945 if (blobStoreSession.getOwnerUid() == uid
946 && blobStoreSession.getOwnerPackageName().equals(packageName)) {
947 blobStoreSession.getSessionFile().delete();
948 mActiveBlobIds.remove(blobStoreSession.getSessionId());
949 return true;
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800950 }
Sudheer Shankafd2611e2020-03-05 02:18:15 -0800951 return false;
952 });
Sudheer Shankabda89c12020-01-30 15:19:24 -0800953 writeBlobSessionsAsync();
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800954
955 // Remove the package from the committer and leasee list
956 final ArrayMap<BlobHandle, BlobMetadata> userBlobs =
957 getUserBlobsLocked(UserHandle.getUserId(uid));
Sudheer Shankafd2611e2020-03-05 02:18:15 -0800958 userBlobs.entrySet().removeIf(entry -> {
959 final BlobMetadata blobMetadata = entry.getValue();
Sudheer Shankac0fd5fa2020-03-15 23:31:37 -0700960 final boolean isACommitter = blobMetadata.isACommitter(packageName, uid);
961 if (isACommitter) {
962 blobMetadata.removeCommitter(packageName, uid);
963 }
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800964 blobMetadata.removeLeasee(packageName, uid);
Sudheer Shankac0fd5fa2020-03-15 23:31:37 -0700965 // Regardless of when the blob is committed, we need to delete
966 // it if it was from the deleted package to ensure we delete all traces of it.
967 if (blobMetadata.shouldBeDeleted(isACommitter /* respectLeaseWaitTime */)) {
968 deleteBlobLocked(blobMetadata);
Sudheer Shankafd2611e2020-03-05 02:18:15 -0800969 return true;
Sudheer Shankabda89c12020-01-30 15:19:24 -0800970 }
Sudheer Shankafd2611e2020-03-05 02:18:15 -0800971 return false;
972 });
Sudheer Shankabda89c12020-01-30 15:19:24 -0800973 writeBlobsInfoAsync();
Sudheer Shankafd2611e2020-03-05 02:18:15 -0800974
Sudheer Shankae53e1ed2020-02-03 17:15:24 -0800975 if (LOGV) {
976 Slog.v(TAG, "Removed blobs data associated with pkg="
977 + packageName + ", uid=" + uid);
978 }
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800979 }
980 }
981
982 private void handleUserRemoved(int userId) {
983 synchronized (mBlobsLock) {
984 final LongSparseArray<BlobStoreSession> userSessions =
985 mSessions.removeReturnOld(userId);
986 if (userSessions != null) {
987 for (int i = 0, count = userSessions.size(); i < count; ++i) {
988 final BlobStoreSession session = userSessions.valueAt(i);
989 session.getSessionFile().delete();
Sudheer Shanka60803032020-02-13 12:47:59 -0800990 mActiveBlobIds.remove(session.getSessionId());
Sudheer Shanka22f0b162020-01-21 13:32:04 -0800991 }
992 }
993
994 final ArrayMap<BlobHandle, BlobMetadata> userBlobs =
995 mBlobsMap.removeReturnOld(userId);
996 if (userBlobs != null) {
997 for (int i = 0, count = userBlobs.size(); i < count; ++i) {
998 final BlobMetadata blobMetadata = userBlobs.valueAt(i);
Sudheer Shankac0fd5fa2020-03-15 23:31:37 -0700999 deleteBlobLocked(blobMetadata);
Sudheer Shanka22f0b162020-01-21 13:32:04 -08001000 }
1001 }
Sudheer Shankae53e1ed2020-02-03 17:15:24 -08001002 if (LOGV) {
1003 Slog.v(TAG, "Removed blobs data in user " + userId);
1004 }
Sudheer Shanka22f0b162020-01-21 13:32:04 -08001005 }
1006 }
1007
Sudheer Shankaae53d112020-01-31 14:20:53 -08001008 @GuardedBy("mBlobsLock")
1009 @VisibleForTesting
1010 void handleIdleMaintenanceLocked() {
1011 // Cleanup any left over data on disk that is not part of index.
Sudheer Shankae53e1ed2020-02-03 17:15:24 -08001012 final ArrayList<Long> deletedBlobIds = new ArrayList<>();
Sudheer Shankaae53d112020-01-31 14:20:53 -08001013 final ArrayList<File> filesToDelete = new ArrayList<>();
1014 final File blobsDir = BlobStoreConfig.getBlobsDir();
1015 if (blobsDir.exists()) {
1016 for (File file : blobsDir.listFiles()) {
1017 try {
1018 final long id = Long.parseLong(file.getName());
Sudheer Shanka60803032020-02-13 12:47:59 -08001019 if (mActiveBlobIds.indexOf(id) < 0) {
Sudheer Shankaae53d112020-01-31 14:20:53 -08001020 filesToDelete.add(file);
Sudheer Shankae53e1ed2020-02-03 17:15:24 -08001021 deletedBlobIds.add(id);
Sudheer Shankaae53d112020-01-31 14:20:53 -08001022 }
1023 } catch (NumberFormatException e) {
Sudheer Shankae53e1ed2020-02-03 17:15:24 -08001024 Slog.wtf(TAG, "Error parsing the file name: " + file, e);
Sudheer Shankaae53d112020-01-31 14:20:53 -08001025 filesToDelete.add(file);
1026 }
1027 }
1028 for (int i = 0, count = filesToDelete.size(); i < count; ++i) {
1029 filesToDelete.get(i).delete();
1030 }
1031 }
1032
1033 // Cleanup any stale blobs.
1034 for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
1035 final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
1036 userBlobs.entrySet().removeIf(entry -> {
Sudheer Shankaae53d112020-01-31 14:20:53 -08001037 final BlobMetadata blobMetadata = entry.getValue();
Sudheer Shankaae53d112020-01-31 14:20:53 -08001038
Sudheer Shankac0fd5fa2020-03-15 23:31:37 -07001039 if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
1040 deleteBlobLocked(blobMetadata);
Sudheer Shankae53e1ed2020-02-03 17:15:24 -08001041 deletedBlobIds.add(blobMetadata.getBlobId());
Sudheer Shankac0fd5fa2020-03-15 23:31:37 -07001042 return true;
Sudheer Shankaae53d112020-01-31 14:20:53 -08001043 }
Sudheer Shankac0fd5fa2020-03-15 23:31:37 -07001044 return false;
Sudheer Shankaae53d112020-01-31 14:20:53 -08001045 });
1046 }
1047 writeBlobsInfoAsync();
1048
1049 // Cleanup any stale sessions.
Sudheer Shankaae53d112020-01-31 14:20:53 -08001050 for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
1051 final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i);
Sudheer Shanka4acba542020-02-16 13:04:18 -08001052 userSessions.removeIf((sessionId, blobStoreSession) -> {
Sudheer Shankaae53d112020-01-31 14:20:53 -08001053 boolean shouldRemove = false;
1054
1055 // Cleanup sessions which haven't been modified in a while.
Sudheer Shankab2a17b72020-05-28 01:14:10 -07001056 if (blobStoreSession.isExpired()) {
Sudheer Shankaae53d112020-01-31 14:20:53 -08001057 shouldRemove = true;
1058 }
1059
1060 // Cleanup sessions with already expired data.
1061 if (blobStoreSession.getBlobHandle().isExpired()) {
1062 shouldRemove = true;
1063 }
1064
1065 if (shouldRemove) {
1066 blobStoreSession.getSessionFile().delete();
Sudheer Shanka60803032020-02-13 12:47:59 -08001067 mActiveBlobIds.remove(blobStoreSession.getSessionId());
Sudheer Shankae53e1ed2020-02-03 17:15:24 -08001068 deletedBlobIds.add(blobStoreSession.getSessionId());
Sudheer Shankaae53d112020-01-31 14:20:53 -08001069 }
Sudheer Shanka4acba542020-02-16 13:04:18 -08001070 return shouldRemove;
1071 });
Sudheer Shankaae53d112020-01-31 14:20:53 -08001072 }
Sudheer Shanka75351542020-06-05 22:42:56 -07001073 Slog.d(TAG, "Completed idle maintenance; deleted "
1074 + Arrays.toString(deletedBlobIds.toArray()));
Sudheer Shankaae53d112020-01-31 14:20:53 -08001075 writeBlobSessionsAsync();
1076 }
1077
Sudheer Shankac0fd5fa2020-03-15 23:31:37 -07001078 @GuardedBy("mBlobsLock")
1079 private void deleteBlobLocked(BlobMetadata blobMetadata) {
1080 blobMetadata.getBlobFile().delete();
1081 mActiveBlobIds.remove(blobMetadata.getBlobId());
1082 }
1083
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001084 void runClearAllSessions(@UserIdInt int userId) {
1085 synchronized (mBlobsLock) {
1086 if (userId == UserHandle.USER_ALL) {
1087 mSessions.clear();
1088 } else {
1089 mSessions.remove(userId);
1090 }
1091 writeBlobSessionsAsync();
1092 }
1093 }
1094
1095 void runClearAllBlobs(@UserIdInt int userId) {
1096 synchronized (mBlobsLock) {
1097 if (userId == UserHandle.USER_ALL) {
1098 mBlobsMap.clear();
1099 } else {
1100 mBlobsMap.remove(userId);
1101 }
1102 writeBlobsInfoAsync();
1103 }
1104 }
1105
Sudheer Shanka285848d2020-02-16 19:46:17 -08001106 void deleteBlob(@NonNull BlobHandle blobHandle, @UserIdInt int userId) {
1107 synchronized (mBlobsLock) {
1108 final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId);
1109 final BlobMetadata blobMetadata = userBlobs.get(blobHandle);
1110 if (blobMetadata == null) {
1111 return;
1112 }
Sudheer Shankac0fd5fa2020-03-15 23:31:37 -07001113 deleteBlobLocked(blobMetadata);
Sudheer Shanka285848d2020-02-16 19:46:17 -08001114 userBlobs.remove(blobHandle);
Sudheer Shanka285848d2020-02-16 19:46:17 -08001115 writeBlobsInfoAsync();
1116 }
1117 }
1118
Sudheer Shankab96c18b2020-03-05 01:03:50 -08001119 void runIdleMaintenance() {
1120 synchronized (mBlobsLock) {
1121 handleIdleMaintenanceLocked();
1122 }
1123 }
1124
Sudheer Shanka8992c052020-05-26 18:10:09 -07001125 boolean isBlobAvailable(long blobId, int userId) {
1126 synchronized (mBlobsLock) {
1127 final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId);
1128 for (BlobMetadata blobMetadata : userBlobs.values()) {
1129 if (blobMetadata.getBlobId() == blobId) {
1130 return true;
1131 }
1132 }
1133 return false;
1134 }
1135 }
1136
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001137 @GuardedBy("mBlobsLock")
1138 private void dumpSessionsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) {
1139 for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
1140 final int userId = mSessions.keyAt(i);
1141 if (!dumpArgs.shouldDumpUser(userId)) {
1142 continue;
1143 }
1144 final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i);
1145 fout.println("List of sessions in user #"
1146 + userId + " (" + userSessions.size() + "):");
1147 fout.increaseIndent();
1148 for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
1149 final long sessionId = userSessions.keyAt(j);
1150 final BlobStoreSession session = userSessions.valueAt(j);
1151 if (!dumpArgs.shouldDumpSession(session.getOwnerPackageName(),
1152 session.getOwnerUid(), session.getSessionId())) {
1153 continue;
1154 }
1155 fout.println("Session #" + sessionId);
1156 fout.increaseIndent();
1157 session.dump(fout, dumpArgs);
1158 fout.decreaseIndent();
1159 }
1160 fout.decreaseIndent();
1161 }
1162 }
1163
1164 @GuardedBy("mBlobsLock")
1165 private void dumpBlobsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) {
1166 for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
1167 final int userId = mBlobsMap.keyAt(i);
1168 if (!dumpArgs.shouldDumpUser(userId)) {
1169 continue;
1170 }
1171 final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
1172 fout.println("List of blobs in user #"
1173 + userId + " (" + userBlobs.size() + "):");
1174 fout.increaseIndent();
1175 for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
1176 final BlobMetadata blobMetadata = userBlobs.valueAt(j);
Sudheer Shankaae53d112020-01-31 14:20:53 -08001177 if (!dumpArgs.shouldDumpBlob(blobMetadata.getBlobId())) {
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001178 continue;
1179 }
Sudheer Shankaae53d112020-01-31 14:20:53 -08001180 fout.println("Blob #" + blobMetadata.getBlobId());
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001181 fout.increaseIndent();
1182 blobMetadata.dump(fout, dumpArgs);
1183 fout.decreaseIndent();
1184 }
1185 fout.decreaseIndent();
1186 }
1187 }
1188
Sudheer Shankae9232d62020-01-23 16:55:34 -08001189 private class BlobStorageStatsAugmenter implements StorageStatsAugmenter {
1190 @Override
1191 public void augmentStatsForPackage(@NonNull PackageStats stats, @NonNull String packageName,
1192 @UserIdInt int userId, boolean callerHasStatsPermission) {
1193 final AtomicLong blobsDataSize = new AtomicLong(0);
1194 forEachSessionInUser(session -> {
1195 if (session.getOwnerPackageName().equals(packageName)) {
1196 blobsDataSize.getAndAdd(session.getSize());
1197 }
1198 }, userId);
1199
1200 forEachBlobInUser(blobMetadata -> {
1201 if (blobMetadata.isALeasee(packageName)) {
1202 if (!blobMetadata.hasOtherLeasees(packageName) || !callerHasStatsPermission) {
1203 blobsDataSize.getAndAdd(blobMetadata.getSize());
1204 }
1205 }
1206 }, userId);
1207
1208 stats.dataSize += blobsDataSize.get();
1209 }
1210
1211 @Override
1212 public void augmentStatsForUid(@NonNull PackageStats stats, int uid,
1213 boolean callerHasStatsPermission) {
1214 final int userId = UserHandle.getUserId(uid);
1215 final AtomicLong blobsDataSize = new AtomicLong(0);
1216 forEachSessionInUser(session -> {
1217 if (session.getOwnerUid() == uid) {
1218 blobsDataSize.getAndAdd(session.getSize());
1219 }
1220 }, userId);
1221
1222 forEachBlobInUser(blobMetadata -> {
1223 if (blobMetadata.isALeasee(uid)) {
1224 if (!blobMetadata.hasOtherLeasees(uid) || !callerHasStatsPermission) {
1225 blobsDataSize.getAndAdd(blobMetadata.getSize());
1226 }
1227 }
1228 }, userId);
1229
1230 stats.dataSize += blobsDataSize.get();
1231 }
1232 }
1233
1234 private void forEachSessionInUser(Consumer<BlobStoreSession> consumer, int userId) {
1235 synchronized (mBlobsLock) {
1236 final LongSparseArray<BlobStoreSession> userSessions = getUserSessionsLocked(userId);
1237 for (int i = 0, count = userSessions.size(); i < count; ++i) {
1238 final BlobStoreSession session = userSessions.valueAt(i);
1239 consumer.accept(session);
1240 }
1241 }
1242 }
1243
1244 private void forEachBlobInUser(Consumer<BlobMetadata> consumer, int userId) {
1245 synchronized (mBlobsLock) {
1246 final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId);
1247 for (int i = 0, count = userBlobs.size(); i < count; ++i) {
1248 final BlobMetadata blobMetadata = userBlobs.valueAt(i);
1249 consumer.accept(blobMetadata);
1250 }
1251 }
1252 }
1253
Sudheer Shanka22f0b162020-01-21 13:32:04 -08001254 private class PackageChangedReceiver extends BroadcastReceiver {
1255 @Override
1256 public void onReceive(Context context, Intent intent) {
Sudheer Shankae53e1ed2020-02-03 17:15:24 -08001257 if (LOGV) {
1258 Slog.v(TAG, "Received " + intent);
1259 }
Sudheer Shanka22f0b162020-01-21 13:32:04 -08001260 switch (intent.getAction()) {
1261 case Intent.ACTION_PACKAGE_FULLY_REMOVED:
1262 case Intent.ACTION_PACKAGE_DATA_CLEARED:
1263 final String packageName = intent.getData().getSchemeSpecificPart();
1264 if (packageName == null) {
1265 Slog.wtf(TAG, "Package name is missing in the intent: " + intent);
1266 return;
1267 }
1268 final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1269 if (uid == -1) {
1270 Slog.wtf(TAG, "uid is missing in the intent: " + intent);
1271 return;
1272 }
1273 handlePackageRemoved(packageName, uid);
1274 break;
Sudheer Shankafd2611e2020-03-05 02:18:15 -08001275 default:
1276 Slog.wtf(TAG, "Received unknown intent: " + intent);
1277 }
1278 }
1279 }
1280
1281 private class UserActionReceiver extends BroadcastReceiver {
1282 @Override
1283 public void onReceive(Context context, Intent intent) {
1284 if (LOGV) {
1285 Slog.v(TAG, "Received: " + intent);
1286 }
1287 switch (intent.getAction()) {
Sudheer Shanka22f0b162020-01-21 13:32:04 -08001288 case Intent.ACTION_USER_REMOVED:
1289 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
1290 USER_NULL);
1291 if (userId == USER_NULL) {
1292 Slog.wtf(TAG, "userId is missing in the intent: " + intent);
1293 return;
1294 }
1295 handleUserRemoved(userId);
1296 break;
1297 default:
1298 Slog.wtf(TAG, "Received unknown intent: " + intent);
1299 }
1300 }
1301 }
1302
Sudheer Shankaf5b36962019-10-04 16:16:13 -07001303 private class Stub extends IBlobStoreManager.Stub {
Sudheer Shankad2da0672019-12-20 15:51:20 -08001304 @Override
Sudheer Shankaab1d4162020-01-07 10:37:50 -08001305 @IntRange(from = 1)
1306 public long createSession(@NonNull BlobHandle blobHandle,
1307 @NonNull String packageName) {
Sudheer Shanka4824e3d2020-01-29 23:50:24 -08001308 Objects.requireNonNull(blobHandle, "blobHandle must not be null");
1309 blobHandle.assertIsValid();
1310 Objects.requireNonNull(packageName, "packageName must not be null");
Sudheer Shankaab1d4162020-01-07 10:37:50 -08001311
1312 final int callingUid = Binder.getCallingUid();
1313 verifyCallingPackage(callingUid, packageName);
1314
1315 if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
1316 packageName, UserHandle.getUserId(callingUid))) {
1317 throw new SecurityException("Caller not allowed to create session; "
1318 + "callingUid=" + callingUid + ", callingPackage=" + packageName);
1319 }
1320
1321 // TODO: Verify caller request is within limits (no. of calls/blob sessions/blobs)
1322 return createSessionInternal(blobHandle, callingUid, packageName);
Sudheer Shankad2da0672019-12-20 15:51:20 -08001323 }
1324
1325 @Override
Sudheer Shankaab1d4162020-01-07 10:37:50 -08001326 @NonNull
1327 public IBlobStoreSession openSession(@IntRange(from = 1) long sessionId,
1328 @NonNull String packageName) {
1329 Preconditions.checkArgumentPositive(sessionId,
1330 "sessionId must be positive: " + sessionId);
Sudheer Shanka4824e3d2020-01-29 23:50:24 -08001331 Objects.requireNonNull(packageName, "packageName must not be null");
Sudheer Shankaab1d4162020-01-07 10:37:50 -08001332
1333 final int callingUid = Binder.getCallingUid();
1334 verifyCallingPackage(callingUid, packageName);
1335
1336 return openSessionInternal(sessionId, callingUid, packageName);
1337 }
1338
1339 @Override
Sudheer Shanka0b872152020-02-27 10:13:08 -08001340 public void abandonSession(@IntRange(from = 1) long sessionId,
Sudheer Shankaab1d4162020-01-07 10:37:50 -08001341 @NonNull String packageName) {
1342 Preconditions.checkArgumentPositive(sessionId,
1343 "sessionId must be positive: " + sessionId);
Sudheer Shanka4824e3d2020-01-29 23:50:24 -08001344 Objects.requireNonNull(packageName, "packageName must not be null");
Sudheer Shankaab1d4162020-01-07 10:37:50 -08001345
1346 final int callingUid = Binder.getCallingUid();
1347 verifyCallingPackage(callingUid, packageName);
1348
Sudheer Shanka0b872152020-02-27 10:13:08 -08001349 abandonSessionInternal(sessionId, callingUid, packageName);
Sudheer Shankad2da0672019-12-20 15:51:20 -08001350 }
1351
1352 @Override
1353 public ParcelFileDescriptor openBlob(@NonNull BlobHandle blobHandle,
1354 @NonNull String packageName) {
Sudheer Shanka4824e3d2020-01-29 23:50:24 -08001355 Objects.requireNonNull(blobHandle, "blobHandle must not be null");
1356 blobHandle.assertIsValid();
1357 Objects.requireNonNull(packageName, "packageName must not be null");
Sudheer Shankaab1d4162020-01-07 10:37:50 -08001358
1359 final int callingUid = Binder.getCallingUid();
1360 verifyCallingPackage(callingUid, packageName);
1361
1362 if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
1363 packageName, UserHandle.getUserId(callingUid))) {
1364 throw new SecurityException("Caller not allowed to open blob; "
1365 + "callingUid=" + callingUid + ", callingPackage=" + packageName);
1366 }
1367
1368 try {
1369 return openBlobInternal(blobHandle, callingUid, packageName);
1370 } catch (IOException e) {
1371 throw ExceptionUtils.wrap(e);
1372 }
Sudheer Shankad2da0672019-12-20 15:51:20 -08001373 }
1374
1375 @Override
1376 public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId,
Sudheer Shanka1406bc82020-02-03 12:27:24 -08001377 @Nullable CharSequence description,
Sudheer Shanka4824e3d2020-01-29 23:50:24 -08001378 @CurrentTimeSecondsLong long leaseExpiryTimeMillis, @NonNull String packageName) {
1379 Objects.requireNonNull(blobHandle, "blobHandle must not be null");
1380 blobHandle.assertIsValid();
Sudheer Shanka1406bc82020-02-03 12:27:24 -08001381 Preconditions.checkArgument(
1382 ResourceId.isValid(descriptionResId) || description != null,
1383 "Description must be valid; descriptionId=" + descriptionResId
1384 + ", description=" + description);
Sudheer Shanka4824e3d2020-01-29 23:50:24 -08001385 Preconditions.checkArgumentNonnegative(leaseExpiryTimeMillis,
1386 "leaseExpiryTimeMillis must not be negative");
1387 Objects.requireNonNull(packageName, "packageName must not be null");
Sudheer Shankaab1d4162020-01-07 10:37:50 -08001388
1389 final int callingUid = Binder.getCallingUid();
1390 verifyCallingPackage(callingUid, packageName);
1391
Sudheer Shankac6c79942020-03-12 13:20:46 -07001392 if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
1393 packageName, UserHandle.getUserId(callingUid))) {
1394 throw new SecurityException("Caller not allowed to open blob; "
1395 + "callingUid=" + callingUid + ", callingPackage=" + packageName);
1396 }
1397
Sudheer Shanka1b6b8252020-03-04 22:19:21 -08001398 try {
1399 acquireLeaseInternal(blobHandle, descriptionResId, description,
1400 leaseExpiryTimeMillis, callingUid, packageName);
1401 } catch (Resources.NotFoundException e) {
1402 throw new IllegalArgumentException(e);
Sudheer Shankaa80a9972020-03-20 00:12:14 -07001403 } catch (LimitExceededException e) {
1404 throw new ParcelableException(e);
Sudheer Shanka1b6b8252020-03-04 22:19:21 -08001405 }
Sudheer Shankad2da0672019-12-20 15:51:20 -08001406 }
1407
1408 @Override
1409 public void releaseLease(@NonNull BlobHandle blobHandle, @NonNull String packageName) {
Sudheer Shanka4824e3d2020-01-29 23:50:24 -08001410 Objects.requireNonNull(blobHandle, "blobHandle must not be null");
1411 blobHandle.assertIsValid();
1412 Objects.requireNonNull(packageName, "packageName must not be null");
Sudheer Shankaab1d4162020-01-07 10:37:50 -08001413
1414 final int callingUid = Binder.getCallingUid();
1415 verifyCallingPackage(callingUid, packageName);
1416
Sudheer Shankac6c79942020-03-12 13:20:46 -07001417 if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
1418 packageName, UserHandle.getUserId(callingUid))) {
1419 throw new SecurityException("Caller not allowed to open blob; "
1420 + "callingUid=" + callingUid + ", callingPackage=" + packageName);
1421 }
1422
Sudheer Shankaab1d4162020-01-07 10:37:50 -08001423 releaseLeaseInternal(blobHandle, callingUid, packageName);
Sudheer Shankad2da0672019-12-20 15:51:20 -08001424 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -08001425
1426 @Override
Sudheer Shankaa80a9972020-03-20 00:12:14 -07001427 public long getRemainingLeaseQuotaBytes(@NonNull String packageName) {
1428 final int callingUid = Binder.getCallingUid();
1429 verifyCallingPackage(callingUid, packageName);
1430
1431 return getRemainingLeaseQuotaBytesInternal(callingUid, packageName);
1432 }
1433
1434 @Override
Sudheer Shankaf6e23b92020-01-15 01:51:15 -08001435 public void waitForIdle(@NonNull RemoteCallback remoteCallback) {
Sudheer Shanka4824e3d2020-01-29 23:50:24 -08001436 Objects.requireNonNull(remoteCallback, "remoteCallback must not be null");
Sudheer Shankaf6e23b92020-01-15 01:51:15 -08001437
1438 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
1439 "Caller is not allowed to call this; caller=" + Binder.getCallingUid());
Sudheer Shankafb11dc72020-03-19 11:56:50 -07001440 // We post messages back and forth between mHandler thread and mBackgroundHandler
1441 // thread while committing a blob. We need to replicate the same pattern here to
1442 // ensure pending messages have been handled.
1443 mHandler.post(() -> {
1444 mBackgroundHandler.post(() -> {
1445 mHandler.post(PooledLambda.obtainRunnable(remoteCallback::sendResult, null)
1446 .recycleOnUse());
1447 });
1448 });
Sudheer Shankaf6e23b92020-01-15 01:51:15 -08001449 }
1450
1451 @Override
Sudheer Shanka130d79c2020-03-04 13:54:36 -08001452 @NonNull
1453 public List<BlobInfo> queryBlobsForUser(@UserIdInt int userId) {
1454 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
1455 throw new SecurityException("Only system uid is allowed to call "
1456 + "queryBlobsForUser()");
1457 }
1458
Sudheer Shanka7d4bd022020-05-20 13:49:17 -07001459 final int resolvedUserId = userId == USER_CURRENT
1460 ? ActivityManager.getCurrentUser() : userId;
1461 // Don't allow any other special user ids apart from USER_CURRENT
1462 final ActivityManagerInternal amInternal = LocalServices.getService(
1463 ActivityManagerInternal.class);
1464 amInternal.ensureNotSpecialUser(resolvedUserId);
1465
1466 return queryBlobsForUserInternal(resolvedUserId);
Sudheer Shanka130d79c2020-03-04 13:54:36 -08001467 }
1468
1469 @Override
1470 public void deleteBlob(long blobId) {
1471 final int callingUid = Binder.getCallingUid();
1472 if (callingUid != Process.SYSTEM_UID) {
1473 throw new SecurityException("Only system uid is allowed to call "
1474 + "deleteBlob()");
1475 }
1476
1477 deleteBlobInternal(blobId, callingUid);
1478 }
1479
1480 @Override
Sudheer Shankac6c79942020-03-12 13:20:46 -07001481 @NonNull
1482 public List<BlobHandle> getLeasedBlobs(@NonNull String packageName) {
1483 Objects.requireNonNull(packageName, "packageName must not be null");
1484
1485 final int callingUid = Binder.getCallingUid();
1486 verifyCallingPackage(callingUid, packageName);
1487
1488 return getLeasedBlobsInternal(callingUid, packageName);
1489 }
1490
1491 @Override
1492 @Nullable
1493 public LeaseInfo getLeaseInfo(@NonNull BlobHandle blobHandle, @NonNull String packageName) {
1494 Objects.requireNonNull(blobHandle, "blobHandle must not be null");
1495 blobHandle.assertIsValid();
1496 Objects.requireNonNull(packageName, "packageName must not be null");
1497
1498 final int callingUid = Binder.getCallingUid();
1499 verifyCallingPackage(callingUid, packageName);
1500
1501 if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
1502 packageName, UserHandle.getUserId(callingUid))) {
1503 throw new SecurityException("Caller not allowed to open blob; "
1504 + "callingUid=" + callingUid + ", callingPackage=" + packageName);
1505 }
1506
1507 return getLeaseInfoInternal(blobHandle, callingUid, packageName);
1508 }
1509
1510 @Override
Sudheer Shankaf6e23b92020-01-15 01:51:15 -08001511 public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
1512 @Nullable String[] args) {
1513 // TODO: add proto-based version of this.
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001514 if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, writer)) return;
1515
1516 final DumpArgs dumpArgs = DumpArgs.parse(args);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -08001517
1518 final IndentingPrintWriter fout = new IndentingPrintWriter(writer, " ");
Sudheer Shankae53e1ed2020-02-03 17:15:24 -08001519 if (dumpArgs.shouldDumpHelp()) {
1520 writer.println("dumpsys blob_store [options]:");
1521 fout.increaseIndent();
1522 dumpArgs.dumpArgsUsage(fout);
1523 fout.decreaseIndent();
1524 return;
1525 }
1526
Sudheer Shankaf6e23b92020-01-15 01:51:15 -08001527 synchronized (mBlobsLock) {
Sudheer Shanka364364b2020-02-19 17:56:09 -08001528 if (dumpArgs.shouldDumpAllSections()) {
1529 fout.println("mCurrentMaxSessionId: " + mCurrentMaxSessionId);
1530 fout.println();
1531 }
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001532
1533 if (dumpArgs.shouldDumpSessions()) {
1534 dumpSessionsLocked(fout, dumpArgs);
1535 fout.println();
Sudheer Shankaf6e23b92020-01-15 01:51:15 -08001536 }
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001537 if (dumpArgs.shouldDumpBlobs()) {
1538 dumpBlobsLocked(fout, dumpArgs);
1539 fout.println();
Sudheer Shankaf6e23b92020-01-15 01:51:15 -08001540 }
1541 }
Sudheer Shanka364364b2020-02-19 17:56:09 -08001542
1543 if (dumpArgs.shouldDumpConfig()) {
1544 fout.println("BlobStore config:");
1545 fout.increaseIndent();
1546 BlobStoreConfig.dump(fout, mContext);
1547 fout.decreaseIndent();
1548 fout.println();
1549 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -08001550 }
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001551
1552 @Override
1553 public int handleShellCommand(@NonNull ParcelFileDescriptor in,
1554 @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
1555 @NonNull String[] args) {
Sudheer Shanka36b25a12020-06-13 22:27:28 -07001556 return new BlobStoreManagerShellCommand(BlobStoreManagerService.this).exec(this,
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001557 in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args);
1558 }
1559 }
1560
1561 static final class DumpArgs {
Sudheer Shanka364364b2020-02-19 17:56:09 -08001562 private static final int FLAG_DUMP_SESSIONS = 1 << 0;
1563 private static final int FLAG_DUMP_BLOBS = 1 << 1;
1564 private static final int FLAG_DUMP_CONFIG = 1 << 2;
1565
1566 private int mSelectedSectionFlags;
Sudheer Shanka7d4bd022020-05-20 13:49:17 -07001567 private boolean mDumpUnredacted;
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001568 private final ArrayList<String> mDumpPackages = new ArrayList<>();
1569 private final ArrayList<Integer> mDumpUids = new ArrayList<>();
1570 private final ArrayList<Integer> mDumpUserIds = new ArrayList<>();
1571 private final ArrayList<Long> mDumpBlobIds = new ArrayList<>();
Sudheer Shankae53e1ed2020-02-03 17:15:24 -08001572 private boolean mDumpHelp;
Sudheer Shanka7d4bd022020-05-20 13:49:17 -07001573 private boolean mDumpAll;
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001574
1575 public boolean shouldDumpSession(String packageName, int uid, long blobId) {
1576 if (!CollectionUtils.isEmpty(mDumpPackages)
1577 && mDumpPackages.indexOf(packageName) < 0) {
1578 return false;
1579 }
1580 if (!CollectionUtils.isEmpty(mDumpUids)
1581 && mDumpUids.indexOf(uid) < 0) {
1582 return false;
1583 }
1584 if (!CollectionUtils.isEmpty(mDumpBlobIds)
1585 && mDumpBlobIds.indexOf(blobId) < 0) {
1586 return false;
1587 }
1588 return true;
1589 }
1590
Sudheer Shanka364364b2020-02-19 17:56:09 -08001591 public boolean shouldDumpAllSections() {
Sudheer Shanka7d4bd022020-05-20 13:49:17 -07001592 return mDumpAll || (mSelectedSectionFlags == 0);
Sudheer Shanka364364b2020-02-19 17:56:09 -08001593 }
1594
1595 public void allowDumpSessions() {
1596 mSelectedSectionFlags |= FLAG_DUMP_SESSIONS;
1597 }
1598
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001599 public boolean shouldDumpSessions() {
Sudheer Shanka364364b2020-02-19 17:56:09 -08001600 if (shouldDumpAllSections()) {
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001601 return true;
1602 }
Sudheer Shanka364364b2020-02-19 17:56:09 -08001603 return (mSelectedSectionFlags & FLAG_DUMP_SESSIONS) != 0;
1604 }
1605
1606 public void allowDumpBlobs() {
1607 mSelectedSectionFlags |= FLAG_DUMP_BLOBS;
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001608 }
1609
1610 public boolean shouldDumpBlobs() {
Sudheer Shanka364364b2020-02-19 17:56:09 -08001611 if (shouldDumpAllSections()) {
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001612 return true;
1613 }
Sudheer Shanka364364b2020-02-19 17:56:09 -08001614 return (mSelectedSectionFlags & FLAG_DUMP_BLOBS) != 0;
1615 }
1616
1617 public void allowDumpConfig() {
1618 mSelectedSectionFlags |= FLAG_DUMP_CONFIG;
1619 }
1620
1621 public boolean shouldDumpConfig() {
1622 if (shouldDumpAllSections()) {
1623 return true;
1624 }
1625 return (mSelectedSectionFlags & FLAG_DUMP_CONFIG) != 0;
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001626 }
1627
1628 public boolean shouldDumpBlob(long blobId) {
1629 return CollectionUtils.isEmpty(mDumpBlobIds)
1630 || mDumpBlobIds.indexOf(blobId) >= 0;
1631 }
1632
1633 public boolean shouldDumpFull() {
Sudheer Shanka7d4bd022020-05-20 13:49:17 -07001634 return mDumpUnredacted;
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001635 }
1636
1637 public boolean shouldDumpUser(int userId) {
1638 return CollectionUtils.isEmpty(mDumpUserIds)
1639 || mDumpUserIds.indexOf(userId) >= 0;
1640 }
1641
Sudheer Shankae53e1ed2020-02-03 17:15:24 -08001642 public boolean shouldDumpHelp() {
1643 return mDumpHelp;
1644 }
1645
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001646 private DumpArgs() {}
1647
1648 public static DumpArgs parse(String[] args) {
1649 final DumpArgs dumpArgs = new DumpArgs();
1650 if (args == null) {
1651 return dumpArgs;
1652 }
1653
1654 for (int i = 0; i < args.length; ++i) {
1655 final String opt = args[i];
Sudheer Shanka7d4bd022020-05-20 13:49:17 -07001656 if ("--all".equals(opt) || "-a".equals(opt)) {
1657 dumpArgs.mDumpAll = true;
1658 } else if ("--unredacted".equals(opt) || "-u".equals(opt)) {
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001659 final int callingUid = Binder.getCallingUid();
1660 if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
Sudheer Shanka7d4bd022020-05-20 13:49:17 -07001661 dumpArgs.mDumpUnredacted = true;
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001662 }
1663 } else if ("--sessions".equals(opt)) {
Sudheer Shanka364364b2020-02-19 17:56:09 -08001664 dumpArgs.allowDumpSessions();
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001665 } else if ("--blobs".equals(opt)) {
Sudheer Shanka364364b2020-02-19 17:56:09 -08001666 dumpArgs.allowDumpBlobs();
1667 } else if ("--config".equals(opt)) {
1668 dumpArgs.allowDumpConfig();
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001669 } else if ("--package".equals(opt) || "-p".equals(opt)) {
1670 dumpArgs.mDumpPackages.add(getStringArgRequired(args, ++i, "packageName"));
Sudheer Shanka7d4bd022020-05-20 13:49:17 -07001671 } else if ("--uid".equals(opt)) {
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001672 dumpArgs.mDumpUids.add(getIntArgRequired(args, ++i, "uid"));
1673 } else if ("--user".equals(opt)) {
1674 dumpArgs.mDumpUserIds.add(getIntArgRequired(args, ++i, "userId"));
1675 } else if ("--blob".equals(opt) || "-b".equals(opt)) {
1676 dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, ++i, "blobId"));
Sudheer Shankae53e1ed2020-02-03 17:15:24 -08001677 } else if ("--help".equals(opt) || "-h".equals(opt)) {
1678 dumpArgs.mDumpHelp = true;
Sudheer Shanka5caeed52020-02-01 12:54:33 -08001679 } else {
1680 // Everything else is assumed to be blob ids.
1681 dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, i, "blobId"));
1682 }
1683 }
1684 return dumpArgs;
1685 }
1686
1687 private static String getStringArgRequired(String[] args, int index, String argName) {
1688 if (index >= args.length) {
1689 throw new IllegalArgumentException("Missing " + argName);
1690 }
1691 return args[index];
1692 }
1693
1694 private static int getIntArgRequired(String[] args, int index, String argName) {
1695 if (index >= args.length) {
1696 throw new IllegalArgumentException("Missing " + argName);
1697 }
1698 final int value;
1699 try {
1700 value = Integer.parseInt(args[index]);
1701 } catch (NumberFormatException e) {
1702 throw new IllegalArgumentException("Invalid " + argName + ": " + args[index]);
1703 }
1704 return value;
1705 }
1706
1707 private static long getLongArgRequired(String[] args, int index, String argName) {
1708 if (index >= args.length) {
1709 throw new IllegalArgumentException("Missing " + argName);
1710 }
1711 final long value;
1712 try {
1713 value = Long.parseLong(args[index]);
1714 } catch (NumberFormatException e) {
1715 throw new IllegalArgumentException("Invalid " + argName + ": " + args[index]);
1716 }
1717 return value;
1718 }
Sudheer Shankae53e1ed2020-02-03 17:15:24 -08001719
1720 private void dumpArgsUsage(IndentingPrintWriter pw) {
1721 pw.println("--help | -h");
1722 printWithIndent(pw, "Dump this help text");
1723 pw.println("--sessions");
1724 printWithIndent(pw, "Dump only the sessions info");
1725 pw.println("--blobs");
1726 printWithIndent(pw, "Dump only the committed blobs info");
Sudheer Shanka364364b2020-02-19 17:56:09 -08001727 pw.println("--config");
1728 printWithIndent(pw, "Dump only the config values");
Sudheer Shankae53e1ed2020-02-03 17:15:24 -08001729 pw.println("--package | -p [package-name]");
1730 printWithIndent(pw, "Dump blobs info associated with the given package");
1731 pw.println("--uid | -u [uid]");
1732 printWithIndent(pw, "Dump blobs info associated with the given uid");
1733 pw.println("--user [user-id]");
1734 printWithIndent(pw, "Dump blobs info in the given user");
1735 pw.println("--blob | -b [session-id | blob-id]");
1736 printWithIndent(pw, "Dump blob info corresponding to the given ID");
1737 pw.println("--full | -f");
1738 printWithIndent(pw, "Dump full unredacted blobs data");
1739 }
1740
1741 private void printWithIndent(IndentingPrintWriter pw, String str) {
1742 pw.increaseIndent();
1743 pw.println(str);
1744 pw.decreaseIndent();
1745 }
Sudheer Shankaf5b36962019-10-04 16:16:13 -07001746 }
Sudheer Shankabda89c12020-01-30 15:19:24 -08001747
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -07001748 private void registerBlobStorePuller() {
1749 mStatsManager.setPullAtomCallback(
1750 FrameworkStatsLog.BLOB_INFO,
1751 null, // use default PullAtomMetadata values
1752 BackgroundThread.getExecutor(),
1753 mStatsCallbackImpl
1754 );
1755 }
1756
1757 private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
1758 @Override
1759 public int onPullAtom(int atomTag, List<StatsEvent> data) {
1760 switch (atomTag) {
1761 case FrameworkStatsLog.BLOB_INFO:
1762 return pullBlobData(atomTag, data);
1763 default:
1764 throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
1765 }
1766 }
1767 }
1768
1769 private int pullBlobData(int atomTag, List<StatsEvent> data) {
1770 synchronized (mBlobsLock) {
1771 for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
1772 final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
1773 for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
1774 final BlobMetadata blob = userBlobs.valueAt(j);
1775 data.add(blob.dumpAsStatsEvent(atomTag));
1776 }
1777 }
1778 }
1779 return StatsManager.PULL_SUCCESS;
1780 }
1781
Sudheer Shankaae53d112020-01-31 14:20:53 -08001782 private class LocalService extends BlobStoreManagerInternal {
1783 @Override
1784 public void onIdleMaintenance() {
Sudheer Shankab96c18b2020-03-05 01:03:50 -08001785 runIdleMaintenance();
Sudheer Shankaae53d112020-01-31 14:20:53 -08001786 }
1787 }
1788
Sudheer Shankabda89c12020-01-30 15:19:24 -08001789 @VisibleForTesting
1790 static class Injector {
1791 public Handler initializeMessageHandler() {
1792 return BlobStoreManagerService.initializeMessageHandler();
1793 }
Sudheer Shankab76f7662020-02-27 15:17:43 -08001794
1795 public Handler getBackgroundHandler() {
1796 return BackgroundThread.getHandler();
1797 }
Sudheer Shankabda89c12020-01-30 15:19:24 -08001798 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -08001799}