blob: 6fb7b267c285bc9557305467313f1ef5aff390e8 [file] [log] [blame]
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
Jorim Jaggi7119800f2018-07-09 17:57:10 +020019import static android.app.ActivityManager.UID_OBSERVER_ACTIVE;
20import static android.app.ActivityManager.UID_OBSERVER_GONE;
21
22import android.annotation.IntDef;
Daniel Colascione9779b5e2018-03-21 19:13:57 -070023import android.annotation.Nullable;
Jorim Jaggi7119800f2018-07-09 17:57:10 +020024import android.app.ActivityManager;
25import android.app.ActivityManagerInternal;
26import android.app.IActivityManager;
27import android.app.IUidObserver;
Valentin Iftime6a652e12019-07-24 16:51:38 +020028import android.app.SearchManager;
Carmen Jacksonf107a232017-05-16 10:37:26 -070029import android.content.BroadcastReceiver;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070030import android.content.Context;
Jeff Sharkey847bd852016-08-03 17:20:03 -060031import android.content.Intent;
Carmen Jacksonf107a232017-05-16 10:37:26 -070032import android.content.IntentFilter;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -070033import android.content.pm.ActivityInfo;
34import android.content.pm.ApplicationInfo;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070035import android.content.pm.PackageManager;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -070036import android.content.pm.ResolveInfo;
Jorim Jaggi0c849962018-07-15 14:24:38 +020037import android.database.ContentObserver;
Carmen Jacksonf107a232017-05-16 10:37:26 -070038import android.net.Uri;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070039import android.os.Binder;
40import android.os.Build;
Philip Cuadraa95cea02016-07-06 16:00:32 -070041import android.os.Handler;
42import android.os.Looper;
43import android.os.Message;
Jorim Jaggi7119800f2018-07-09 17:57:10 +020044import android.os.RemoteException;
Nicolas Geoffray8b5976e2019-02-20 15:41:03 +000045import android.os.SystemProperties;
Jeff Sharkey847bd852016-08-03 17:20:03 -060046import android.os.UserHandle;
Carmen Jackson0ff99462018-08-09 14:51:39 -070047import android.os.UserManager;
Mathieu Chartier678dbef2019-12-02 13:58:04 -080048import android.provider.DeviceConfig;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -070049import android.provider.MediaStore;
Jorim Jaggi0c849962018-07-15 14:24:38 +020050import android.provider.Settings;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070051import android.system.ErrnoException;
52import android.system.Os;
53import android.system.OsConstants;
Jorim Jaggi7119800f2018-07-09 17:57:10 +020054import android.util.ArrayMap;
Calin Juravle31ce3a82017-05-22 17:49:01 -070055import android.util.ArraySet;
Jeff Sharkey847bd852016-08-03 17:20:03 -060056import android.util.Slog;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070057
Jorim Jaggi7119800f2018-07-09 17:57:10 +020058import com.android.internal.annotations.GuardedBy;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -070059import com.android.internal.app.ResolverActivity;
Philip Cuadraa95cea02016-07-06 16:00:32 -070060import com.android.internal.os.BackgroundThread;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060061import com.android.internal.util.DumpUtils;
Jorim Jaggi7119800f2018-07-09 17:57:10 +020062import com.android.internal.util.function.pooled.PooledLambda;
Wale Ogunwale214f3482018-10-04 11:00:47 -070063import com.android.server.wm.ActivityTaskManagerInternal;
Valentin Iftimed22452d2019-07-12 15:15:54 +020064
Philip Cuadrad9bd8842016-07-12 17:29:38 -070065import dalvik.system.DexFile;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -070066import dalvik.system.VMRuntime;
67
Daniel Colascione9779b5e2018-03-21 19:13:57 -070068import java.io.Closeable;
Daniel Colascione9779b5e2018-03-21 19:13:57 -070069import java.io.DataInputStream;
Valentin Iftimed22452d2019-07-12 15:15:54 +020070import java.io.FileDescriptor;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070071import java.io.IOException;
Valentin Iftimed22452d2019-07-12 15:15:54 +020072import java.io.InputStream;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070073import java.io.PrintWriter;
Jorim Jaggi7119800f2018-07-09 17:57:10 +020074import java.lang.annotation.Retention;
75import java.lang.annotation.RetentionPolicy;
Jeff Sharkey847bd852016-08-03 17:20:03 -060076import java.util.ArrayList;
Valentin Iftimed22452d2019-07-12 15:15:54 +020077import java.util.List;
Daniel Colascione9779b5e2018-03-21 19:13:57 -070078import java.util.zip.ZipEntry;
Valentin Iftimed22452d2019-07-12 15:15:54 +020079import java.util.zip.ZipFile;
Daniel Colascione9779b5e2018-03-21 19:13:57 -070080
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070081/**
82 * <p>PinnerService pins important files for key processes in memory.</p>
83 * <p>Files to pin are specified in the config_defaultPinnerServiceFiles
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -070084 * overlay.</p>
85 * <p>Pin the default camera application if specified in config_pinnerCameraApp.</p>
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070086 */
87public final class PinnerService extends SystemService {
88 private static final boolean DEBUG = false;
89 private static final String TAG = "PinnerService";
Jorim Jaggi7119800f2018-07-09 17:57:10 +020090
Daniel Colascione9779b5e2018-03-21 19:13:57 -070091 private static final String PIN_META_FILENAME = "pinlist.meta";
92 private static final int PAGE_SIZE = (int) Os.sysconf(OsConstants._SC_PAGESIZE);
Jorim Jaggi7119800f2018-07-09 17:57:10 +020093 private static final int MATCH_FLAGS = PackageManager.MATCH_DEFAULT_ONLY
94 | PackageManager.MATCH_DIRECT_BOOT_AWARE
95 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
96
97 private static final int KEY_CAMERA = 0;
98 private static final int KEY_HOME = 1;
Valentin Iftime6a652e12019-07-24 16:51:38 +020099 private static final int KEY_ASSISTANT = 2;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200100
Mathieu Chartier678dbef2019-12-02 13:58:04 -0800101 // Pin the camera application. Default to the system property only if the experiment phenotype
102 // property is not set.
103 private static boolean PROP_PIN_CAMERA =
104 DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
105 "pin_camera",
106 SystemProperties.getBoolean("pinner.pin_camera", true));
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700107 // Pin using pinlist.meta when pinning apps.
108 private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean(
109 "pinner.use_pinlist", true);
110 // Pin the whole odex/vdex/etc file when pinning apps.
111 private static boolean PROP_PIN_ODEX = SystemProperties.getBoolean(
112 "pinner.whole_odex", true);
113
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200114 private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app.
115 private static final int MAX_HOME_PIN_SIZE = 6 * (1 << 20); // 6MB max for home app.
Valentin Iftime6a652e12019-07-24 16:51:38 +0200116 private static final int MAX_ASSISTANT_PIN_SIZE = 60 * (1 << 20); // 60MB max for assistant app.
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200117
Valentin Iftime6a652e12019-07-24 16:51:38 +0200118 @IntDef({KEY_CAMERA, KEY_HOME, KEY_ASSISTANT})
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200119 @Retention(RetentionPolicy.SOURCE)
120 public @interface AppKey {}
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700121
122 private final Context mContext;
Wale Ogunwale214f3482018-10-04 11:00:47 -0700123 private final ActivityTaskManagerInternal mAtmInternal;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200124 private final ActivityManagerInternal mAmInternal;
125 private final IActivityManager mAm;
Carmen Jackson0ff99462018-08-09 14:51:39 -0700126 private final UserManager mUserManager;
Valentin Iftime6a652e12019-07-24 16:51:38 +0200127 private SearchManager mSearchManager;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700128
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200129 /** The list of the statically pinned files. */
130 @GuardedBy("this")
131 private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<>();
132
133 /** The list of the pinned apps. This is a map from {@link AppKey} to a pinned app. */
134 @GuardedBy("this")
135 private final ArrayMap<Integer, PinnedApp> mPinnedApps = new ArrayMap<>();
136
137 /**
138 * The list of the pinned apps that need to be repinned as soon as the all processes of a given
139 * uid are no longer active. Note that with background dex opt, the new dex/vdex files are only
140 * loaded into the processes once it restarts. So in case background dex opt recompiled these
141 * files, we still need to keep the old ones pinned until the processes restart.
142 * <p>
143 * This is a map from uid to {@link AppKey}
144 */
145 @GuardedBy("this")
146 private final ArrayMap<Integer, Integer> mPendingRepin = new ArrayMap<>();
147
148 /**
149 * A set of {@link AppKey} that are configured to be pinned.
150 */
151 private final ArraySet<Integer> mPinKeys = new ArraySet<>();
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700152
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700153 private BinderService mBinderService;
Philip Cuadraa95cea02016-07-06 16:00:32 -0700154 private PinnerHandler mPinnerHandler = null;
155
Carmen Jacksonf107a232017-05-16 10:37:26 -0700156 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
157 @Override
158 public void onReceive(Context context, Intent intent) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200159 // If an app has updated, update pinned files accordingly.
160 if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) {
Carmen Jacksonf107a232017-05-16 10:37:26 -0700161 Uri packageUri = intent.getData();
Calin Juravle31ce3a82017-05-22 17:49:01 -0700162 String packageName = packageUri.getSchemeSpecificPart();
163 ArraySet<String> updatedPackages = new ArraySet<>();
164 updatedPackages.add(packageName);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200165 update(updatedPackages, true /* force */);
Carmen Jacksonf107a232017-05-16 10:37:26 -0700166 }
167 }
168 };
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700169
170 public PinnerService(Context context) {
171 super(context);
172
173 mContext = context;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200174 boolean shouldPinCamera = context.getResources().getBoolean(
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700175 com.android.internal.R.bool.config_pinnerCameraApp);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200176 boolean shouldPinHome = context.getResources().getBoolean(
177 com.android.internal.R.bool.config_pinnerHomeApp);
Valentin Iftime6a652e12019-07-24 16:51:38 +0200178 boolean shouldPinAssistant = context.getResources().getBoolean(
179 com.android.internal.R.bool.config_pinnerAssistantApp);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200180 if (shouldPinCamera) {
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700181 if (PROP_PIN_CAMERA) {
182 mPinKeys.add(KEY_CAMERA);
183 } else if (DEBUG) {
184 Slog.i(TAG, "Pinner - skip pinning camera app");
185 }
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200186 }
187 if (shouldPinHome) {
188 mPinKeys.add(KEY_HOME);
189 }
Valentin Iftime6a652e12019-07-24 16:51:38 +0200190 if (shouldPinAssistant) {
191 mPinKeys.add(KEY_ASSISTANT);
192 }
Philip Cuadraa95cea02016-07-06 16:00:32 -0700193 mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());
Carmen Jacksonf107a232017-05-16 10:37:26 -0700194
Wale Ogunwale214f3482018-10-04 11:00:47 -0700195 mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200196 mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
197 mAm = ActivityManager.getService();
198
Carmen Jackson0ff99462018-08-09 14:51:39 -0700199 mUserManager = mContext.getSystemService(UserManager.class);
200
Carmen Jacksonf107a232017-05-16 10:37:26 -0700201 IntentFilter filter = new IntentFilter();
202 filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
203 filter.addDataScheme("package");
204 mContext.registerReceiver(mBroadcastReceiver, filter);
Jorim Jaggi0c849962018-07-15 14:24:38 +0200205
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200206 registerUidListener();
Jorim Jaggi0c849962018-07-15 14:24:38 +0200207 registerUserSetupCompleteListener();
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700208 }
209
210 @Override
211 public void onStart() {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700212 if (DEBUG) {
213 Slog.i(TAG, "Starting PinnerService");
214 }
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700215 mBinderService = new BinderService();
216 publishBinderService("pinner", mBinderService);
Carmen Jacksonf107a232017-05-16 10:37:26 -0700217 publishLocalService(PinnerService.class, this);
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700218
Jeff Sharkey847bd852016-08-03 17:20:03 -0600219 mPinnerHandler.obtainMessage(PinnerHandler.PIN_ONSTART_MSG).sendToTarget();
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200220 sendPinAppsMessage(UserHandle.USER_SYSTEM);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700221 }
222
Valentin Iftime6a652e12019-07-24 16:51:38 +0200223 @Override
224 public void onBootPhase(int phase) {
225 // SearchManagerService is started after PinnerService, wait for PHASE_SYSTEM_SERVICES_READY
226 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
227 mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
228 sendPinAppsMessage(UserHandle.USER_SYSTEM);
229 }
230 }
231
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700232 /**
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200233 * Repin apps on user switch.
234 * <p>
235 * If more than one user is using the device each user may set a different preference for the
236 * individual apps. Make sure that user's preference is pinned into memory.
Philip Cuadraa95cea02016-07-06 16:00:32 -0700237 */
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700238 @Override
239 public void onSwitchUser(int userHandle) {
Carmen Jackson0ff99462018-08-09 14:51:39 -0700240 if (!mUserManager.isManagedProfile(userHandle)) {
241 sendPinAppsMessage(userHandle);
242 }
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200243 }
244
245 @Override
246 public void onUnlockUser(int userHandle) {
Carmen Jackson0ff99462018-08-09 14:51:39 -0700247 if (!mUserManager.isManagedProfile(userHandle)) {
248 sendPinAppsMessage(userHandle);
249 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700250 }
251
Philip Cuadraa95cea02016-07-06 16:00:32 -0700252 /**
Carmen Jacksonf107a232017-05-16 10:37:26 -0700253 * Update the currently pinned files.
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200254 * Specifically, this only updates pinning for the apps that need to be pinned.
Carmen Jacksonf107a232017-05-16 10:37:26 -0700255 * The other files pinned in onStart will not need to be updated.
256 */
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200257 public void update(ArraySet<String> updatedPackages, boolean force) {
258 int currentUser = ActivityManager.getCurrentUser();
259 for (int i = mPinKeys.size() - 1; i >= 0; i--) {
260 int key = mPinKeys.valueAt(i);
261 ApplicationInfo info = getInfoForKey(key, currentUser);
262 if (info != null && updatedPackages.contains(info.packageName)) {
263 Slog.i(TAG, "Updating pinned files for " + info.packageName + " force=" + force);
264 sendPinAppMessage(key, currentUser, force);
265 }
Calin Juravle31ce3a82017-05-22 17:49:01 -0700266 }
Carmen Jacksonf107a232017-05-16 10:37:26 -0700267 }
268
269 /**
Philip Cuadraa95cea02016-07-06 16:00:32 -0700270 * Handler for on start pinning message
271 */
272 private void handlePinOnStart() {
Nicolas Geoffray8b5976e2019-02-20 15:41:03 +0000273 final String bootImage = SystemProperties.get("dalvik.vm.boot-image", "");
274 String[] filesToPin = null;
Nicolas Geoffray9ac03a02020-02-07 11:54:18 +0000275 if (bootImage.endsWith("boot-image.prof")) {
276 // Use the files listed for that specific boot image.
277 // TODO: find a better way to know we're using the JIT zygote configuration.
Nicolas Geoffray8b5976e2019-02-20 15:41:03 +0000278 filesToPin = mContext.getResources().getStringArray(
Nicolas Geoffray9ac03a02020-02-07 11:54:18 +0000279 com.android.internal.R.array.config_jitzygoteBootImagePinnerServiceFiles);
Nicolas Geoffray8b5976e2019-02-20 15:41:03 +0000280 } else {
281 // Files to pin come from the overlay and can be specified per-device config
282 filesToPin = mContext.getResources().getStringArray(
283 com.android.internal.R.array.config_defaultPinnerServiceFiles);
284 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700285 // Continue trying to pin each file even if we fail to pin some of them
286 for (String fileToPin : filesToPin) {
287 PinnedFile pf = pinFile(fileToPin,
288 Integer.MAX_VALUE,
289 /*attemptPinIntrospection=*/false);
290 if (pf == null) {
291 Slog.e(TAG, "Failed to pin file = " + fileToPin);
292 continue;
293 }
294
295 synchronized (this) {
296 mPinnedFiles.add(pf);
Philip Cuadraa95cea02016-07-06 16:00:32 -0700297 }
298 }
299 }
300
Jorim Jaggi0c849962018-07-15 14:24:38 +0200301 /**
302 * Registers a listener to repin the home app when user setup is complete, as the home intent
303 * initially resolves to setup wizard, but once setup is complete, it will resolve to the
304 * regular home app.
305 */
306 private void registerUserSetupCompleteListener() {
307 Uri userSetupCompleteUri = Settings.Secure.getUriFor(
308 Settings.Secure.USER_SETUP_COMPLETE);
309 mContext.getContentResolver().registerContentObserver(userSetupCompleteUri,
310 false, new ContentObserver(null) {
311 @Override
312 public void onChange(boolean selfChange, Uri uri) {
313 if (userSetupCompleteUri.equals(uri)) {
314 sendPinAppMessage(KEY_HOME, ActivityManager.getCurrentUser(),
315 true /* force */);
316 }
317 }
318 }, UserHandle.USER_ALL);
319 }
320
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200321 private void registerUidListener() {
322 try {
323 mAm.registerUidObserver(new IUidObserver.Stub() {
324 @Override
325 public void onUidGone(int uid, boolean disabled) throws RemoteException {
326 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
327 PinnerService::handleUidGone, PinnerService.this, uid));
328 }
329
330 @Override
331 public void onUidActive(int uid) throws RemoteException {
332 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
333 PinnerService::handleUidActive, PinnerService.this, uid));
334 }
335
336 @Override
337 public void onUidIdle(int uid, boolean disabled) throws RemoteException {
338 }
339
340 @Override
Hui Yu26969322019-08-21 14:56:35 -0700341 public void onUidStateChanged(int uid, int procState, long procStateSeq,
342 int capability) throws RemoteException {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200343 }
344
345 @Override
346 public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
347 }
348 }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, "system");
349 } catch (RemoteException e) {
350 Slog.e(TAG, "Failed to register uid observer", e);
351 }
352 }
353
354 private void handleUidGone(int uid) {
355 updateActiveState(uid, false /* active */);
356 int key;
357 synchronized (this) {
358
359 // In case we have a pending repin, repin now. See mPendingRepin for more information.
360 key = mPendingRepin.getOrDefault(uid, -1);
361 if (key == -1) {
362 return;
363 }
364 mPendingRepin.remove(uid);
365 }
366 pinApp(key, ActivityManager.getCurrentUser(), false /* force */);
367 }
368
369 private void handleUidActive(int uid) {
370 updateActiveState(uid, true /* active */);
371 }
372
373 private void updateActiveState(int uid, boolean active) {
374 synchronized (this) {
375 for (int i = mPinnedApps.size() - 1; i >= 0; i--) {
376 PinnedApp app = mPinnedApps.valueAt(i);
377 if (app.uid == uid) {
378 app.active = active;
379 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700380 }
381 }
382 }
383
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200384 private void unpinApp(@AppKey int key) {
385 ArrayList<PinnedFile> pinnedAppFiles;
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700386 synchronized (this) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200387 PinnedApp app = mPinnedApps.get(key);
388 if (app == null) {
389 return;
390 }
391 mPinnedApps.remove(key);
392 pinnedAppFiles = new ArrayList<>(app.mFiles);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700393 }
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200394 for (PinnedFile pinnedFile : pinnedAppFiles) {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700395 pinnedFile.close();
396 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700397 }
398
399 private boolean isResolverActivity(ActivityInfo info) {
400 return ResolverActivity.class.getName().equals(info.name);
401 }
402
403 private ApplicationInfo getCameraInfo(int userHandle) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700404 Intent cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
Carmen Jackson879fb682018-07-20 16:43:09 -0700405 ApplicationInfo info = getApplicationInfoForIntent(cameraIntent, userHandle,
406 false /* defaultToSystemApp */);
407
408 // If the STILL_IMAGE_CAMERA intent doesn't resolve, try the _SECURE intent.
409 // We don't use _SECURE first because it will never get set on a device
410 // without File-based Encryption. But if the user has only set the intent
411 // before unlocking their device, we may still be able to identify their
412 // preference using this intent.
413 if (info == null) {
414 cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
415 info = getApplicationInfoForIntent(cameraIntent, userHandle,
416 false /* defaultToSystemApp */);
417 }
418
419 // If the _SECURE intent doesn't resolve, try the original intent but request
420 // the system app for camera if there was more than one result.
421 if (info == null) {
422 cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
423 info = getApplicationInfoForIntent(cameraIntent, userHandle,
424 true /* defaultToSystemApp */);
425 }
426 return info;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200427 }
428
429 private ApplicationInfo getHomeInfo(int userHandle) {
Wale Ogunwale214f3482018-10-04 11:00:47 -0700430 Intent intent = mAtmInternal.getHomeIntent();
Carmen Jackson879fb682018-07-20 16:43:09 -0700431 return getApplicationInfoForIntent(intent, userHandle, false);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200432 }
433
Valentin Iftime6a652e12019-07-24 16:51:38 +0200434 private ApplicationInfo getAssistantInfo(int userHandle) {
435 if (mSearchManager != null) {
436 Intent intent = mSearchManager.getAssistIntent(false);
437 return getApplicationInfoForIntent(intent, userHandle, true);
438 }
439 return null;
440 }
441
Carmen Jackson879fb682018-07-20 16:43:09 -0700442 private ApplicationInfo getApplicationInfoForIntent(Intent intent, int userHandle,
443 boolean defaultToSystemApp) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200444 if (intent == null) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700445 return null;
446 }
Carmen Jackson879fb682018-07-20 16:43:09 -0700447
448 ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivityAsUser(intent,
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200449 MATCH_FLAGS, userHandle);
Carmen Jackson879fb682018-07-20 16:43:09 -0700450
451 // If this intent can resolve to only one app, choose that one.
452 // Otherwise, if we've requested to default to the system app, return it;
453 // if we have not requested that default, return null if there's more than one option.
454 // If there's more than one system app, return null since we don't know which to pick.
455 if (resolveInfo == null) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700456 return null;
457 }
Carmen Jackson879fb682018-07-20 16:43:09 -0700458
459 if (!isResolverActivity(resolveInfo.activityInfo)) {
460 return resolveInfo.activityInfo.applicationInfo;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200461 }
Carmen Jackson879fb682018-07-20 16:43:09 -0700462
463 if (defaultToSystemApp) {
464 List<ResolveInfo> infoList = mContext.getPackageManager()
465 .queryIntentActivitiesAsUser(intent, MATCH_FLAGS, userHandle);
466 ApplicationInfo systemAppInfo = null;
467 for (ResolveInfo info : infoList) {
468 if ((info.activityInfo.applicationInfo.flags
469 & ApplicationInfo.FLAG_SYSTEM) != 0) {
470 if (systemAppInfo == null) {
471 systemAppInfo = info.activityInfo.applicationInfo;
472 } else {
473 // If there's more than one system app, return null due to ambiguity.
474 return null;
475 }
476 }
477 }
478 return systemAppInfo;
479 }
480
481 return null;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200482 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700483
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200484 private void sendPinAppsMessage(int userHandle) {
485 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApps, this,
486 userHandle));
487 }
488
489 private void pinApps(int userHandle) {
490 for (int i = mPinKeys.size() - 1; i >= 0; i--) {
491 int key = mPinKeys.valueAt(i);
492 pinApp(key, userHandle, true /* force */);
493 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700494 }
495
Carmen Jacksonf107a232017-05-16 10:37:26 -0700496 /**
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200497 * @see #pinApp(int, int, boolean)
Carmen Jacksonf107a232017-05-16 10:37:26 -0700498 */
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200499 private void sendPinAppMessage(int key, int userHandle, boolean force) {
500 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApp, this,
501 key, userHandle, force));
502 }
503
504 /**
505 * Pins an app of a specific type {@code key}.
506 *
507 * @param force If false, this will not repin the app if it's currently active. See
508 * {@link #mPendingRepin}.
509 */
510 private void pinApp(int key, int userHandle, boolean force) {
511 int uid = getUidForKey(key);
512
513 // In case the app is currently active, don't repin until next process restart. See
514 // mPendingRepin for more information.
515 if (!force && uid != -1) {
516 synchronized (this) {
517 mPendingRepin.put(uid, key);
518 }
519 return;
520 }
521 unpinApp(key);
522 ApplicationInfo info = getInfoForKey(key, userHandle);
523 if (info != null) {
524 pinApp(key, info);
525 }
526 }
527
528 /**
529 * Checks whether the pinned package with {@code key} is active or not.
530
531 * @return The uid of the pinned app, or {@code -1} otherwise.
532 */
533 private int getUidForKey(@AppKey int key) {
534 synchronized (this) {
535 PinnedApp existing = mPinnedApps.get(key);
536 return existing != null && existing.active
537 ? existing.uid
538 : -1;
539 }
540 }
541
542 /**
543 * Retrieves the current application info for the given app type.
544 *
545 * @param key The app type to retrieve the info for.
546 * @param userHandle The user id of the current user.
547 */
548 private @Nullable ApplicationInfo getInfoForKey(@AppKey int key, int userHandle) {
549 switch (key) {
550 case KEY_CAMERA:
551 return getCameraInfo(userHandle);
552 case KEY_HOME:
553 return getHomeInfo(userHandle);
Valentin Iftime6a652e12019-07-24 16:51:38 +0200554 case KEY_ASSISTANT:
555 return getAssistantInfo(userHandle);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200556 default:
557 return null;
558 }
559 }
560
561 /**
562 * @return The app type name for {@code key}.
563 */
564 private String getNameForKey(@AppKey int key) {
565 switch (key) {
566 case KEY_CAMERA:
567 return "Camera";
568 case KEY_HOME:
569 return "Home";
Valentin Iftime6a652e12019-07-24 16:51:38 +0200570 case KEY_ASSISTANT:
571 return "Assistant";
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200572 default:
573 return null;
574 }
575 }
576
577 /**
578 * @return The maximum amount of bytes to be pinned for an app of type {@code key}.
579 */
580 private int getSizeLimitForKey(@AppKey int key) {
581 switch (key) {
582 case KEY_CAMERA:
583 return MAX_CAMERA_PIN_SIZE;
584 case KEY_HOME:
585 return MAX_HOME_PIN_SIZE;
Valentin Iftime6a652e12019-07-24 16:51:38 +0200586 case KEY_ASSISTANT:
587 return MAX_ASSISTANT_PIN_SIZE;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200588 default:
589 return 0;
590 }
591 }
592
593 /**
594 * Pins an application.
595 *
596 * @param key The key of the app to pin.
597 * @param appInfo The corresponding app info.
598 */
599 private void pinApp(@AppKey int key, @Nullable ApplicationInfo appInfo) {
600 if (appInfo == null) {
601 return;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700602 }
603
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200604 PinnedApp pinnedApp = new PinnedApp(appInfo);
605 synchronized (this) {
606 mPinnedApps.put(key, pinnedApp);
607 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700608
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200609 // pin APK
610 int pinSizeLimit = getSizeLimitForKey(key);
611 String apk = appInfo.sourceDir;
612 PinnedFile pf = pinFile(apk, pinSizeLimit, /*attemptPinIntrospection=*/true);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700613 if (pf == null) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200614 Slog.e(TAG, "Failed to pin " + apk);
615 return;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700616 }
617 if (DEBUG) {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700618 Slog.i(TAG, "Pinned " + pf.fileName);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700619 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700620 synchronized (this) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200621 pinnedApp.mFiles.add(pf);
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700622 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700623
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700624 // determine the ABI from either ApplicationInfo or Build
Valentin Iftimed22452d2019-07-12 15:15:54 +0200625 String abi = appInfo.primaryCpuAbi != null ? appInfo.primaryCpuAbi :
626 Build.SUPPORTED_ABIS[0];
627 String arch = VMRuntime.getInstructionSet(abi);
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700628 // get the path to the odex or oat file
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200629 String baseCodePath = appInfo.getBaseCodePath();
Calin Juravle128721a2017-05-15 20:20:50 -0700630 String[] files = null;
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700631 try {
Calin Juravle128721a2017-05-15 20:20:50 -0700632 files = DexFile.getDexFileOutputPaths(baseCodePath, arch);
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700633 } catch (IOException ioe) {}
Calin Juravle128721a2017-05-15 20:20:50 -0700634 if (files == null) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200635 return;
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700636 }
637
638 //not pinning the oat/odex is not a fatal error
Calin Juravle128721a2017-05-15 20:20:50 -0700639 for (String file : files) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200640 pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false);
Calin Juravle128721a2017-05-15 20:20:50 -0700641 if (pf != null) {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700642 synchronized (this) {
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700643 if (PROP_PIN_ODEX) {
644 pinnedApp.mFiles.add(pf);
645 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700646 }
Calin Juravle128721a2017-05-15 20:20:50 -0700647 if (DEBUG) {
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700648 if (PROP_PIN_ODEX) {
649 Slog.i(TAG, "Pinned " + pf.fileName);
650 } else {
651 Slog.i(TAG, "Pinned [skip] " + pf.fileName);
652 }
Calin Juravle128721a2017-05-15 20:20:50 -0700653 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700654 }
655 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700656 }
657
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700658 /** mlock length bytes of fileToPin in memory
659 *
660 * If attemptPinIntrospection is true, then treat the file to pin as a zip file and
661 * look for a "pinlist.meta" file in the archive root directory. The structure of this
662 * file is a PINLIST_META as described below:
663 *
664 * <pre>
665 * PINLIST_META: PIN_RANGE*
666 * PIN_RANGE: PIN_START PIN_LENGTH
667 * PIN_START: big endian i32: offset in bytes of pin region from file start
668 * PIN_LENGTH: big endian i32: length of pin region in bytes
669 * </pre>
670 *
671 * (We use big endian because that's what DataInputStream is hardcoded to use.)
672 *
673 * If attemptPinIntrospection is false, then we use a single implicit PIN_RANGE of (0,
674 * maxBytesToPin); that is, we attempt to pin the first maxBytesToPin bytes of the file.
675 *
676 * After we open a file, we march through the list of pin ranges and attempt to pin
677 * each one, stopping after we've pinned maxBytesToPin bytes. (We may truncate the last
678 * pinned range to fit.) In this way, by choosing to emit certain PIN_RANGE pairs
679 * before others, file generators can express pins in priority order, making most
680 * effective use of the pinned-page quota.
681 *
682 * N.B. Each PIN_RANGE is clamped to the actual bounds of the file; all inputs have a
683 * meaningful interpretation. Also, a range locking a single byte of a page locks the
684 * whole page. Any truncated PIN_RANGE at EOF is ignored. Overlapping pinned entries
685 * are legal, but each pin of a byte counts toward the pin quota regardless of whether
686 * that byte has already been pinned, so the generator of PINLIST_META ought to ensure
687 * that ranges are non-overlapping.
688 *
689 * @param fileToPin Path to file to pin
690 * @param maxBytesToPin Maximum number of bytes to pin
691 * @param attemptPinIntrospection If true, try to open file as a
692 * zip in order to extract the
693 * @return Pinned memory resource owner thing or null on error
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700694 */
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700695 private static PinnedFile pinFile(String fileToPin,
696 int maxBytesToPin,
697 boolean attemptPinIntrospection) {
698 ZipFile fileAsZip = null;
699 InputStream pinRangeStream = null;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700700 try {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700701 if (attemptPinIntrospection) {
702 fileAsZip = maybeOpenZip(fileToPin);
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700703 }
704
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700705 if (fileAsZip != null) {
706 pinRangeStream = maybeOpenPinMetaInZip(fileAsZip, fileToPin);
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700707 }
708
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700709 Slog.d(TAG, "pinRangeStream: " + pinRangeStream);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700710
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700711 PinRangeSource pinRangeSource = (pinRangeStream != null)
712 ? new PinRangeSourceStream(pinRangeStream)
713 : new PinRangeSourceStatic(0, Integer.MAX_VALUE /* will be clipped */);
714 return pinFileRanges(fileToPin, maxBytesToPin, pinRangeSource);
715 } finally {
716 safeClose(pinRangeStream);
717 safeClose(fileAsZip); // Also closes any streams we've opened
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700718 }
719 }
720
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700721 /**
722 * Attempt to open a file as a zip file. On any sort of corruption, log, swallow the
723 * error, and return null.
724 */
725 private static ZipFile maybeOpenZip(String fileName) {
726 ZipFile zip = null;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700727 try {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700728 zip = new ZipFile(fileName);
729 } catch (IOException ex) {
730 Slog.w(TAG,
731 String.format(
732 "could not open \"%s\" as zip: pinning as blob",
733 fileName),
734 ex);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700735 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700736 return zip;
737 }
738
739 /**
740 * Open a pin metadata file in the zip if one is present.
741 *
742 * @param zipFile Zip file to search
743 * @return Open input stream or null on any error
744 */
745 private static InputStream maybeOpenPinMetaInZip(ZipFile zipFile, String fileName) {
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700746 if (!PROP_PIN_PINLIST) {
747 if (DEBUG) {
748 Slog.i(TAG, "Pin - skip pinlist.meta in " + fileName);
749 }
750 return null;
751 }
752
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700753 ZipEntry pinMetaEntry = zipFile.getEntry(PIN_META_FILENAME);
754 InputStream pinMetaStream = null;
755 if (pinMetaEntry != null) {
756 try {
757 pinMetaStream = zipFile.getInputStream(pinMetaEntry);
758 } catch (IOException ex) {
759 Slog.w(TAG,
760 String.format("error reading pin metadata \"%s\": pinning as blob",
761 fileName),
762 ex);
763 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700764 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700765 return pinMetaStream;
766 }
767
768 private static abstract class PinRangeSource {
769 /** Retrive a range to pin.
770 *
771 * @param outPinRange Receives the pin region
772 * @return True if we filled in outPinRange or false if we're out of pin entries
773 */
774 abstract boolean read(PinRange outPinRange);
775 }
776
777 private static final class PinRangeSourceStatic extends PinRangeSource {
778 private final int mPinStart;
779 private final int mPinLength;
780 private boolean mDone = false;
781
782 PinRangeSourceStatic(int pinStart, int pinLength) {
783 mPinStart = pinStart;
784 mPinLength = pinLength;
785 }
786
787 @Override
788 boolean read(PinRange outPinRange) {
789 outPinRange.start = mPinStart;
790 outPinRange.length = mPinLength;
791 boolean done = mDone;
792 mDone = true;
793 return !done;
794 }
795 }
796
797 private static final class PinRangeSourceStream extends PinRangeSource {
798 private final DataInputStream mStream;
799 private boolean mDone = false;
800
801 PinRangeSourceStream(InputStream stream) {
802 mStream = new DataInputStream(stream);
803 }
804
805 @Override
806 boolean read(PinRange outPinRange) {
807 if (!mDone) {
808 try {
809 outPinRange.start = mStream.readInt();
810 outPinRange.length = mStream.readInt();
811 } catch (IOException ex) {
812 mDone = true;
813 }
814 }
815 return !mDone;
816 }
817 }
818
819 /**
820 * Helper for pinFile.
821 *
822 * @param fileToPin Name of file to pin
823 * @param maxBytesToPin Maximum number of bytes to pin
824 * @param pinRangeSource Read PIN_RANGE entries from this stream to tell us what bytes
825 * to pin.
826 * @return PinnedFile or null on error
827 */
828 private static PinnedFile pinFileRanges(
829 String fileToPin,
830 int maxBytesToPin,
831 PinRangeSource pinRangeSource)
832 {
833 FileDescriptor fd = new FileDescriptor();
834 long address = -1;
835 int mapSize = 0;
836
837 try {
838 int openFlags = (OsConstants.O_RDONLY |
839 OsConstants.O_CLOEXEC |
840 OsConstants.O_NOFOLLOW);
841 fd = Os.open(fileToPin, openFlags, 0);
842 mapSize = (int) Math.min(Os.fstat(fd).st_size, Integer.MAX_VALUE);
843 address = Os.mmap(0, mapSize,
844 OsConstants.PROT_READ,
845 OsConstants.MAP_SHARED,
846 fd, /*offset=*/0);
847
848 PinRange pinRange = new PinRange();
849 int bytesPinned = 0;
850
851 // We pin at page granularity, so make sure the limit is page-aligned
852 if (maxBytesToPin % PAGE_SIZE != 0) {
853 maxBytesToPin -= maxBytesToPin % PAGE_SIZE;
854 }
855
856 while (bytesPinned < maxBytesToPin && pinRangeSource.read(pinRange)) {
857 int pinStart = pinRange.start;
858 int pinLength = pinRange.length;
859 pinStart = clamp(0, pinStart, mapSize);
860 pinLength = clamp(0, pinLength, mapSize - pinStart);
861 pinLength = Math.min(maxBytesToPin - bytesPinned, pinLength);
862
863 // mlock doesn't require the region to be page-aligned, but we snap the
864 // lock region to page boundaries anyway so that we don't under-count
865 // locking a single byte of a page as a charge of one byte even though the
866 // OS will retain the whole page. Thanks to this adjustment, we slightly
867 // over-count the pin charge of back-to-back pins touching the same page,
868 // but better that than undercounting. Besides: nothing stops pin metafile
869 // creators from making the actual regions page-aligned.
870 pinLength += pinStart % PAGE_SIZE;
871 pinStart -= pinStart % PAGE_SIZE;
872 if (pinLength % PAGE_SIZE != 0) {
873 pinLength += PAGE_SIZE - pinLength % PAGE_SIZE;
874 }
875 pinLength = clamp(0, pinLength, maxBytesToPin - bytesPinned);
876
877 if (pinLength > 0) {
878 if (DEBUG) {
879 Slog.d(TAG,
880 String.format(
881 "pinning at %s %s bytes of %s",
882 pinStart, pinLength, fileToPin));
883 }
884 Os.mlock(address + pinStart, pinLength);
885 }
886 bytesPinned += pinLength;
887 }
888
889 PinnedFile pinnedFile = new PinnedFile(address, mapSize, fileToPin, bytesPinned);
890 address = -1; // Ownership transferred
891 return pinnedFile;
892 } catch (ErrnoException ex) {
893 Slog.e(TAG, "Could not pin file " + fileToPin, ex);
894 return null;
895 } finally {
896 safeClose(fd);
897 if (address >= 0) {
898 safeMunmap(address, mapSize);
899 }
900 }
901 }
902
903 private static int clamp(int min, int value, int max) {
904 return Math.max(min, Math.min(value, max));
905 }
906
907 private static void safeMunmap(long address, long mapSize) {
908 try {
909 Os.munmap(address, mapSize);
910 } catch (ErrnoException ex) {
911 Slog.w(TAG, "ignoring error in unmap", ex);
912 }
913 }
914
915 /**
916 * Close FD, swallowing irrelevant errors.
917 */
918 private static void safeClose(@Nullable FileDescriptor fd) {
919 if (fd != null && fd.valid()) {
920 try {
921 Os.close(fd);
922 } catch (ErrnoException ex) {
923 // Swallow the exception: non-EBADF errors in close(2)
924 // indicate deferred paging write errors, which we
925 // don't care about here. The underlying file
926 // descriptor is always closed.
927 if (ex.errno == OsConstants.EBADF) {
928 throw new AssertionError(ex);
929 }
930 }
931 }
932 }
933
934 /**
935 * Close closeable thing, swallowing errors.
936 */
937 private static void safeClose(@Nullable Closeable thing) {
938 if (thing != null) {
939 try {
940 thing.close();
941 } catch (IOException ex) {
942 Slog.w(TAG, "ignoring error closing resource: " + thing, ex);
943 }
944 }
945 }
946
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700947 private final class BinderService extends Binder {
948 @Override
949 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600950 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200951 synchronized (PinnerService.this) {
952 long totalSize = 0;
953 for (PinnedFile pinnedFile : mPinnedFiles) {
954 pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
955 totalSize += pinnedFile.bytesPinned;
956 }
957 pw.println();
958 for (int key : mPinnedApps.keySet()) {
959 PinnedApp app = mPinnedApps.get(key);
960 pw.print(getNameForKey(key));
961 pw.print(" uid="); pw.print(app.uid);
962 pw.print(" active="); pw.print(app.active);
963 pw.println();
964 for (PinnedFile pf : mPinnedApps.get(key).mFiles) {
965 pw.print(" "); pw.format("%s %s\n", pf.fileName, pf.bytesPinned);
966 totalSize += pf.bytesPinned;
967 }
968 }
969 pw.format("Total size: %s\n", totalSize);
970 pw.println();
971 if (!mPendingRepin.isEmpty()) {
972 pw.print("Pending repin: ");
973 for (int key : mPendingRepin.values()) {
974 pw.print(getNameForKey(key)); pw.print(' ');
975 }
976 pw.println();
977 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700978 }
979 }
980 }
981
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700982 private static final class PinnedFile implements AutoCloseable {
983 private long mAddress;
984 final int mapSize;
985 final String fileName;
986 final int bytesPinned;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700987
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700988 PinnedFile(long address, int mapSize, String fileName, int bytesPinned) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700989 mAddress = address;
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700990 this.mapSize = mapSize;
991 this.fileName = fileName;
992 this.bytesPinned = bytesPinned;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700993 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700994
995 @Override
996 public void close() {
997 if (mAddress >= 0) {
998 safeMunmap(mAddress, mapSize);
999 mAddress = -1;
1000 }
1001 }
1002
1003 @Override
1004 public void finalize() {
1005 close();
1006 }
1007 }
1008
1009 final static class PinRange {
1010 int start;
1011 int length;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -07001012 }
Philip Cuadraa95cea02016-07-06 16:00:32 -07001013
Jorim Jaggi7119800f2018-07-09 17:57:10 +02001014 /**
1015 * Represents an app that was pinned.
1016 */
1017 private final class PinnedApp {
1018
1019 /**
1020 * The uid of the package being pinned. This stays constant while the package stays
1021 * installed.
1022 */
1023 final int uid;
1024
1025 /** Whether it is currently active, i.e. there is a running process from that package. */
1026 boolean active;
1027
1028 /** List of pinned files. */
1029 final ArrayList<PinnedFile> mFiles = new ArrayList<>();
1030
1031 private PinnedApp(ApplicationInfo appInfo) {
1032 uid = appInfo.uid;
1033 active = mAmInternal.isUidActive(uid);
1034 }
1035 }
1036
Philip Cuadraa95cea02016-07-06 16:00:32 -07001037 final class PinnerHandler extends Handler {
Philip Cuadraa95cea02016-07-06 16:00:32 -07001038 static final int PIN_ONSTART_MSG = 4001;
1039
1040 public PinnerHandler(Looper looper) {
1041 super(looper, null, true);
1042 }
1043
1044 @Override
1045 public void handleMessage(Message msg) {
1046 switch (msg.what) {
Philip Cuadraa95cea02016-07-06 16:00:32 -07001047 case PIN_ONSTART_MSG:
1048 {
1049 handlePinOnStart();
1050 }
1051 break;
1052
1053 default:
1054 super.handleMessage(msg);
1055 }
1056 }
1057 }
1058
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -07001059}