blob: 135f6f3ea1259a025d2c6c48f47827afa5e00fef [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;
275 if (bootImage.endsWith("apex.art")) {
276 // Use the files listed for that specific boot image
277 filesToPin = mContext.getResources().getStringArray(
278 com.android.internal.R.array.config_apexBootImagePinnerServiceFiles);
279 } else {
280 // Files to pin come from the overlay and can be specified per-device config
281 filesToPin = mContext.getResources().getStringArray(
282 com.android.internal.R.array.config_defaultPinnerServiceFiles);
283 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700284 // Continue trying to pin each file even if we fail to pin some of them
285 for (String fileToPin : filesToPin) {
286 PinnedFile pf = pinFile(fileToPin,
287 Integer.MAX_VALUE,
288 /*attemptPinIntrospection=*/false);
289 if (pf == null) {
290 Slog.e(TAG, "Failed to pin file = " + fileToPin);
291 continue;
292 }
293
294 synchronized (this) {
295 mPinnedFiles.add(pf);
Philip Cuadraa95cea02016-07-06 16:00:32 -0700296 }
297 }
298 }
299
Jorim Jaggi0c849962018-07-15 14:24:38 +0200300 /**
301 * Registers a listener to repin the home app when user setup is complete, as the home intent
302 * initially resolves to setup wizard, but once setup is complete, it will resolve to the
303 * regular home app.
304 */
305 private void registerUserSetupCompleteListener() {
306 Uri userSetupCompleteUri = Settings.Secure.getUriFor(
307 Settings.Secure.USER_SETUP_COMPLETE);
308 mContext.getContentResolver().registerContentObserver(userSetupCompleteUri,
309 false, new ContentObserver(null) {
310 @Override
311 public void onChange(boolean selfChange, Uri uri) {
312 if (userSetupCompleteUri.equals(uri)) {
313 sendPinAppMessage(KEY_HOME, ActivityManager.getCurrentUser(),
314 true /* force */);
315 }
316 }
317 }, UserHandle.USER_ALL);
318 }
319
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200320 private void registerUidListener() {
321 try {
322 mAm.registerUidObserver(new IUidObserver.Stub() {
323 @Override
324 public void onUidGone(int uid, boolean disabled) throws RemoteException {
325 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
326 PinnerService::handleUidGone, PinnerService.this, uid));
327 }
328
329 @Override
330 public void onUidActive(int uid) throws RemoteException {
331 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
332 PinnerService::handleUidActive, PinnerService.this, uid));
333 }
334
335 @Override
336 public void onUidIdle(int uid, boolean disabled) throws RemoteException {
337 }
338
339 @Override
Hui Yu26969322019-08-21 14:56:35 -0700340 public void onUidStateChanged(int uid, int procState, long procStateSeq,
341 int capability) throws RemoteException {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200342 }
343
344 @Override
345 public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
346 }
347 }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, "system");
348 } catch (RemoteException e) {
349 Slog.e(TAG, "Failed to register uid observer", e);
350 }
351 }
352
353 private void handleUidGone(int uid) {
354 updateActiveState(uid, false /* active */);
355 int key;
356 synchronized (this) {
357
358 // In case we have a pending repin, repin now. See mPendingRepin for more information.
359 key = mPendingRepin.getOrDefault(uid, -1);
360 if (key == -1) {
361 return;
362 }
363 mPendingRepin.remove(uid);
364 }
365 pinApp(key, ActivityManager.getCurrentUser(), false /* force */);
366 }
367
368 private void handleUidActive(int uid) {
369 updateActiveState(uid, true /* active */);
370 }
371
372 private void updateActiveState(int uid, boolean active) {
373 synchronized (this) {
374 for (int i = mPinnedApps.size() - 1; i >= 0; i--) {
375 PinnedApp app = mPinnedApps.valueAt(i);
376 if (app.uid == uid) {
377 app.active = active;
378 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700379 }
380 }
381 }
382
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200383 private void unpinApp(@AppKey int key) {
384 ArrayList<PinnedFile> pinnedAppFiles;
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700385 synchronized (this) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200386 PinnedApp app = mPinnedApps.get(key);
387 if (app == null) {
388 return;
389 }
390 mPinnedApps.remove(key);
391 pinnedAppFiles = new ArrayList<>(app.mFiles);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700392 }
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200393 for (PinnedFile pinnedFile : pinnedAppFiles) {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700394 pinnedFile.close();
395 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700396 }
397
398 private boolean isResolverActivity(ActivityInfo info) {
399 return ResolverActivity.class.getName().equals(info.name);
400 }
401
402 private ApplicationInfo getCameraInfo(int userHandle) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700403 Intent cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
Carmen Jackson879fb682018-07-20 16:43:09 -0700404 ApplicationInfo info = getApplicationInfoForIntent(cameraIntent, userHandle,
405 false /* defaultToSystemApp */);
406
407 // If the STILL_IMAGE_CAMERA intent doesn't resolve, try the _SECURE intent.
408 // We don't use _SECURE first because it will never get set on a device
409 // without File-based Encryption. But if the user has only set the intent
410 // before unlocking their device, we may still be able to identify their
411 // preference using this intent.
412 if (info == null) {
413 cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
414 info = getApplicationInfoForIntent(cameraIntent, userHandle,
415 false /* defaultToSystemApp */);
416 }
417
418 // If the _SECURE intent doesn't resolve, try the original intent but request
419 // the system app for camera if there was more than one result.
420 if (info == null) {
421 cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
422 info = getApplicationInfoForIntent(cameraIntent, userHandle,
423 true /* defaultToSystemApp */);
424 }
425 return info;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200426 }
427
428 private ApplicationInfo getHomeInfo(int userHandle) {
Wale Ogunwale214f3482018-10-04 11:00:47 -0700429 Intent intent = mAtmInternal.getHomeIntent();
Carmen Jackson879fb682018-07-20 16:43:09 -0700430 return getApplicationInfoForIntent(intent, userHandle, false);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200431 }
432
Valentin Iftime6a652e12019-07-24 16:51:38 +0200433 private ApplicationInfo getAssistantInfo(int userHandle) {
434 if (mSearchManager != null) {
435 Intent intent = mSearchManager.getAssistIntent(false);
436 return getApplicationInfoForIntent(intent, userHandle, true);
437 }
438 return null;
439 }
440
Carmen Jackson879fb682018-07-20 16:43:09 -0700441 private ApplicationInfo getApplicationInfoForIntent(Intent intent, int userHandle,
442 boolean defaultToSystemApp) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200443 if (intent == null) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700444 return null;
445 }
Carmen Jackson879fb682018-07-20 16:43:09 -0700446
447 ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivityAsUser(intent,
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200448 MATCH_FLAGS, userHandle);
Carmen Jackson879fb682018-07-20 16:43:09 -0700449
450 // If this intent can resolve to only one app, choose that one.
451 // Otherwise, if we've requested to default to the system app, return it;
452 // if we have not requested that default, return null if there's more than one option.
453 // If there's more than one system app, return null since we don't know which to pick.
454 if (resolveInfo == null) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700455 return null;
456 }
Carmen Jackson879fb682018-07-20 16:43:09 -0700457
458 if (!isResolverActivity(resolveInfo.activityInfo)) {
459 return resolveInfo.activityInfo.applicationInfo;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200460 }
Carmen Jackson879fb682018-07-20 16:43:09 -0700461
462 if (defaultToSystemApp) {
463 List<ResolveInfo> infoList = mContext.getPackageManager()
464 .queryIntentActivitiesAsUser(intent, MATCH_FLAGS, userHandle);
465 ApplicationInfo systemAppInfo = null;
466 for (ResolveInfo info : infoList) {
467 if ((info.activityInfo.applicationInfo.flags
468 & ApplicationInfo.FLAG_SYSTEM) != 0) {
469 if (systemAppInfo == null) {
470 systemAppInfo = info.activityInfo.applicationInfo;
471 } else {
472 // If there's more than one system app, return null due to ambiguity.
473 return null;
474 }
475 }
476 }
477 return systemAppInfo;
478 }
479
480 return null;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200481 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700482
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200483 private void sendPinAppsMessage(int userHandle) {
484 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApps, this,
485 userHandle));
486 }
487
488 private void pinApps(int userHandle) {
489 for (int i = mPinKeys.size() - 1; i >= 0; i--) {
490 int key = mPinKeys.valueAt(i);
491 pinApp(key, userHandle, true /* force */);
492 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700493 }
494
Carmen Jacksonf107a232017-05-16 10:37:26 -0700495 /**
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200496 * @see #pinApp(int, int, boolean)
Carmen Jacksonf107a232017-05-16 10:37:26 -0700497 */
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200498 private void sendPinAppMessage(int key, int userHandle, boolean force) {
499 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApp, this,
500 key, userHandle, force));
501 }
502
503 /**
504 * Pins an app of a specific type {@code key}.
505 *
506 * @param force If false, this will not repin the app if it's currently active. See
507 * {@link #mPendingRepin}.
508 */
509 private void pinApp(int key, int userHandle, boolean force) {
510 int uid = getUidForKey(key);
511
512 // In case the app is currently active, don't repin until next process restart. See
513 // mPendingRepin for more information.
514 if (!force && uid != -1) {
515 synchronized (this) {
516 mPendingRepin.put(uid, key);
517 }
518 return;
519 }
520 unpinApp(key);
521 ApplicationInfo info = getInfoForKey(key, userHandle);
522 if (info != null) {
523 pinApp(key, info);
524 }
525 }
526
527 /**
528 * Checks whether the pinned package with {@code key} is active or not.
529
530 * @return The uid of the pinned app, or {@code -1} otherwise.
531 */
532 private int getUidForKey(@AppKey int key) {
533 synchronized (this) {
534 PinnedApp existing = mPinnedApps.get(key);
535 return existing != null && existing.active
536 ? existing.uid
537 : -1;
538 }
539 }
540
541 /**
542 * Retrieves the current application info for the given app type.
543 *
544 * @param key The app type to retrieve the info for.
545 * @param userHandle The user id of the current user.
546 */
547 private @Nullable ApplicationInfo getInfoForKey(@AppKey int key, int userHandle) {
548 switch (key) {
549 case KEY_CAMERA:
550 return getCameraInfo(userHandle);
551 case KEY_HOME:
552 return getHomeInfo(userHandle);
Valentin Iftime6a652e12019-07-24 16:51:38 +0200553 case KEY_ASSISTANT:
554 return getAssistantInfo(userHandle);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200555 default:
556 return null;
557 }
558 }
559
560 /**
561 * @return The app type name for {@code key}.
562 */
563 private String getNameForKey(@AppKey int key) {
564 switch (key) {
565 case KEY_CAMERA:
566 return "Camera";
567 case KEY_HOME:
568 return "Home";
Valentin Iftime6a652e12019-07-24 16:51:38 +0200569 case KEY_ASSISTANT:
570 return "Assistant";
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200571 default:
572 return null;
573 }
574 }
575
576 /**
577 * @return The maximum amount of bytes to be pinned for an app of type {@code key}.
578 */
579 private int getSizeLimitForKey(@AppKey int key) {
580 switch (key) {
581 case KEY_CAMERA:
582 return MAX_CAMERA_PIN_SIZE;
583 case KEY_HOME:
584 return MAX_HOME_PIN_SIZE;
Valentin Iftime6a652e12019-07-24 16:51:38 +0200585 case KEY_ASSISTANT:
586 return MAX_ASSISTANT_PIN_SIZE;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200587 default:
588 return 0;
589 }
590 }
591
592 /**
593 * Pins an application.
594 *
595 * @param key The key of the app to pin.
596 * @param appInfo The corresponding app info.
597 */
598 private void pinApp(@AppKey int key, @Nullable ApplicationInfo appInfo) {
599 if (appInfo == null) {
600 return;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700601 }
602
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200603 PinnedApp pinnedApp = new PinnedApp(appInfo);
604 synchronized (this) {
605 mPinnedApps.put(key, pinnedApp);
606 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700607
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200608 // pin APK
609 int pinSizeLimit = getSizeLimitForKey(key);
610 String apk = appInfo.sourceDir;
611 PinnedFile pf = pinFile(apk, pinSizeLimit, /*attemptPinIntrospection=*/true);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700612 if (pf == null) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200613 Slog.e(TAG, "Failed to pin " + apk);
614 return;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700615 }
616 if (DEBUG) {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700617 Slog.i(TAG, "Pinned " + pf.fileName);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700618 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700619 synchronized (this) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200620 pinnedApp.mFiles.add(pf);
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700621 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700622
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700623 // determine the ABI from either ApplicationInfo or Build
Valentin Iftimed22452d2019-07-12 15:15:54 +0200624 String abi = appInfo.primaryCpuAbi != null ? appInfo.primaryCpuAbi :
625 Build.SUPPORTED_ABIS[0];
626 String arch = VMRuntime.getInstructionSet(abi);
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700627 // get the path to the odex or oat file
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200628 String baseCodePath = appInfo.getBaseCodePath();
Calin Juravle128721a2017-05-15 20:20:50 -0700629 String[] files = null;
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700630 try {
Calin Juravle128721a2017-05-15 20:20:50 -0700631 files = DexFile.getDexFileOutputPaths(baseCodePath, arch);
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700632 } catch (IOException ioe) {}
Calin Juravle128721a2017-05-15 20:20:50 -0700633 if (files == null) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200634 return;
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700635 }
636
637 //not pinning the oat/odex is not a fatal error
Calin Juravle128721a2017-05-15 20:20:50 -0700638 for (String file : files) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200639 pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false);
Calin Juravle128721a2017-05-15 20:20:50 -0700640 if (pf != null) {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700641 synchronized (this) {
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700642 if (PROP_PIN_ODEX) {
643 pinnedApp.mFiles.add(pf);
644 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700645 }
Calin Juravle128721a2017-05-15 20:20:50 -0700646 if (DEBUG) {
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700647 if (PROP_PIN_ODEX) {
648 Slog.i(TAG, "Pinned " + pf.fileName);
649 } else {
650 Slog.i(TAG, "Pinned [skip] " + pf.fileName);
651 }
Calin Juravle128721a2017-05-15 20:20:50 -0700652 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700653 }
654 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700655 }
656
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700657 /** mlock length bytes of fileToPin in memory
658 *
659 * If attemptPinIntrospection is true, then treat the file to pin as a zip file and
660 * look for a "pinlist.meta" file in the archive root directory. The structure of this
661 * file is a PINLIST_META as described below:
662 *
663 * <pre>
664 * PINLIST_META: PIN_RANGE*
665 * PIN_RANGE: PIN_START PIN_LENGTH
666 * PIN_START: big endian i32: offset in bytes of pin region from file start
667 * PIN_LENGTH: big endian i32: length of pin region in bytes
668 * </pre>
669 *
670 * (We use big endian because that's what DataInputStream is hardcoded to use.)
671 *
672 * If attemptPinIntrospection is false, then we use a single implicit PIN_RANGE of (0,
673 * maxBytesToPin); that is, we attempt to pin the first maxBytesToPin bytes of the file.
674 *
675 * After we open a file, we march through the list of pin ranges and attempt to pin
676 * each one, stopping after we've pinned maxBytesToPin bytes. (We may truncate the last
677 * pinned range to fit.) In this way, by choosing to emit certain PIN_RANGE pairs
678 * before others, file generators can express pins in priority order, making most
679 * effective use of the pinned-page quota.
680 *
681 * N.B. Each PIN_RANGE is clamped to the actual bounds of the file; all inputs have a
682 * meaningful interpretation. Also, a range locking a single byte of a page locks the
683 * whole page. Any truncated PIN_RANGE at EOF is ignored. Overlapping pinned entries
684 * are legal, but each pin of a byte counts toward the pin quota regardless of whether
685 * that byte has already been pinned, so the generator of PINLIST_META ought to ensure
686 * that ranges are non-overlapping.
687 *
688 * @param fileToPin Path to file to pin
689 * @param maxBytesToPin Maximum number of bytes to pin
690 * @param attemptPinIntrospection If true, try to open file as a
691 * zip in order to extract the
692 * @return Pinned memory resource owner thing or null on error
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700693 */
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700694 private static PinnedFile pinFile(String fileToPin,
695 int maxBytesToPin,
696 boolean attemptPinIntrospection) {
697 ZipFile fileAsZip = null;
698 InputStream pinRangeStream = null;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700699 try {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700700 if (attemptPinIntrospection) {
701 fileAsZip = maybeOpenZip(fileToPin);
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700702 }
703
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700704 if (fileAsZip != null) {
705 pinRangeStream = maybeOpenPinMetaInZip(fileAsZip, fileToPin);
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700706 }
707
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700708 Slog.d(TAG, "pinRangeStream: " + pinRangeStream);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700709
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700710 PinRangeSource pinRangeSource = (pinRangeStream != null)
711 ? new PinRangeSourceStream(pinRangeStream)
712 : new PinRangeSourceStatic(0, Integer.MAX_VALUE /* will be clipped */);
713 return pinFileRanges(fileToPin, maxBytesToPin, pinRangeSource);
714 } finally {
715 safeClose(pinRangeStream);
716 safeClose(fileAsZip); // Also closes any streams we've opened
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700717 }
718 }
719
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700720 /**
721 * Attempt to open a file as a zip file. On any sort of corruption, log, swallow the
722 * error, and return null.
723 */
724 private static ZipFile maybeOpenZip(String fileName) {
725 ZipFile zip = null;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700726 try {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700727 zip = new ZipFile(fileName);
728 } catch (IOException ex) {
729 Slog.w(TAG,
730 String.format(
731 "could not open \"%s\" as zip: pinning as blob",
732 fileName),
733 ex);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700734 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700735 return zip;
736 }
737
738 /**
739 * Open a pin metadata file in the zip if one is present.
740 *
741 * @param zipFile Zip file to search
742 * @return Open input stream or null on any error
743 */
744 private static InputStream maybeOpenPinMetaInZip(ZipFile zipFile, String fileName) {
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700745 if (!PROP_PIN_PINLIST) {
746 if (DEBUG) {
747 Slog.i(TAG, "Pin - skip pinlist.meta in " + fileName);
748 }
749 return null;
750 }
751
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700752 ZipEntry pinMetaEntry = zipFile.getEntry(PIN_META_FILENAME);
753 InputStream pinMetaStream = null;
754 if (pinMetaEntry != null) {
755 try {
756 pinMetaStream = zipFile.getInputStream(pinMetaEntry);
757 } catch (IOException ex) {
758 Slog.w(TAG,
759 String.format("error reading pin metadata \"%s\": pinning as blob",
760 fileName),
761 ex);
762 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700763 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700764 return pinMetaStream;
765 }
766
767 private static abstract class PinRangeSource {
768 /** Retrive a range to pin.
769 *
770 * @param outPinRange Receives the pin region
771 * @return True if we filled in outPinRange or false if we're out of pin entries
772 */
773 abstract boolean read(PinRange outPinRange);
774 }
775
776 private static final class PinRangeSourceStatic extends PinRangeSource {
777 private final int mPinStart;
778 private final int mPinLength;
779 private boolean mDone = false;
780
781 PinRangeSourceStatic(int pinStart, int pinLength) {
782 mPinStart = pinStart;
783 mPinLength = pinLength;
784 }
785
786 @Override
787 boolean read(PinRange outPinRange) {
788 outPinRange.start = mPinStart;
789 outPinRange.length = mPinLength;
790 boolean done = mDone;
791 mDone = true;
792 return !done;
793 }
794 }
795
796 private static final class PinRangeSourceStream extends PinRangeSource {
797 private final DataInputStream mStream;
798 private boolean mDone = false;
799
800 PinRangeSourceStream(InputStream stream) {
801 mStream = new DataInputStream(stream);
802 }
803
804 @Override
805 boolean read(PinRange outPinRange) {
806 if (!mDone) {
807 try {
808 outPinRange.start = mStream.readInt();
809 outPinRange.length = mStream.readInt();
810 } catch (IOException ex) {
811 mDone = true;
812 }
813 }
814 return !mDone;
815 }
816 }
817
818 /**
819 * Helper for pinFile.
820 *
821 * @param fileToPin Name of file to pin
822 * @param maxBytesToPin Maximum number of bytes to pin
823 * @param pinRangeSource Read PIN_RANGE entries from this stream to tell us what bytes
824 * to pin.
825 * @return PinnedFile or null on error
826 */
827 private static PinnedFile pinFileRanges(
828 String fileToPin,
829 int maxBytesToPin,
830 PinRangeSource pinRangeSource)
831 {
832 FileDescriptor fd = new FileDescriptor();
833 long address = -1;
834 int mapSize = 0;
835
836 try {
837 int openFlags = (OsConstants.O_RDONLY |
838 OsConstants.O_CLOEXEC |
839 OsConstants.O_NOFOLLOW);
840 fd = Os.open(fileToPin, openFlags, 0);
841 mapSize = (int) Math.min(Os.fstat(fd).st_size, Integer.MAX_VALUE);
842 address = Os.mmap(0, mapSize,
843 OsConstants.PROT_READ,
844 OsConstants.MAP_SHARED,
845 fd, /*offset=*/0);
846
847 PinRange pinRange = new PinRange();
848 int bytesPinned = 0;
849
850 // We pin at page granularity, so make sure the limit is page-aligned
851 if (maxBytesToPin % PAGE_SIZE != 0) {
852 maxBytesToPin -= maxBytesToPin % PAGE_SIZE;
853 }
854
855 while (bytesPinned < maxBytesToPin && pinRangeSource.read(pinRange)) {
856 int pinStart = pinRange.start;
857 int pinLength = pinRange.length;
858 pinStart = clamp(0, pinStart, mapSize);
859 pinLength = clamp(0, pinLength, mapSize - pinStart);
860 pinLength = Math.min(maxBytesToPin - bytesPinned, pinLength);
861
862 // mlock doesn't require the region to be page-aligned, but we snap the
863 // lock region to page boundaries anyway so that we don't under-count
864 // locking a single byte of a page as a charge of one byte even though the
865 // OS will retain the whole page. Thanks to this adjustment, we slightly
866 // over-count the pin charge of back-to-back pins touching the same page,
867 // but better that than undercounting. Besides: nothing stops pin metafile
868 // creators from making the actual regions page-aligned.
869 pinLength += pinStart % PAGE_SIZE;
870 pinStart -= pinStart % PAGE_SIZE;
871 if (pinLength % PAGE_SIZE != 0) {
872 pinLength += PAGE_SIZE - pinLength % PAGE_SIZE;
873 }
874 pinLength = clamp(0, pinLength, maxBytesToPin - bytesPinned);
875
876 if (pinLength > 0) {
877 if (DEBUG) {
878 Slog.d(TAG,
879 String.format(
880 "pinning at %s %s bytes of %s",
881 pinStart, pinLength, fileToPin));
882 }
883 Os.mlock(address + pinStart, pinLength);
884 }
885 bytesPinned += pinLength;
886 }
887
888 PinnedFile pinnedFile = new PinnedFile(address, mapSize, fileToPin, bytesPinned);
889 address = -1; // Ownership transferred
890 return pinnedFile;
891 } catch (ErrnoException ex) {
892 Slog.e(TAG, "Could not pin file " + fileToPin, ex);
893 return null;
894 } finally {
895 safeClose(fd);
896 if (address >= 0) {
897 safeMunmap(address, mapSize);
898 }
899 }
900 }
901
902 private static int clamp(int min, int value, int max) {
903 return Math.max(min, Math.min(value, max));
904 }
905
906 private static void safeMunmap(long address, long mapSize) {
907 try {
908 Os.munmap(address, mapSize);
909 } catch (ErrnoException ex) {
910 Slog.w(TAG, "ignoring error in unmap", ex);
911 }
912 }
913
914 /**
915 * Close FD, swallowing irrelevant errors.
916 */
917 private static void safeClose(@Nullable FileDescriptor fd) {
918 if (fd != null && fd.valid()) {
919 try {
920 Os.close(fd);
921 } catch (ErrnoException ex) {
922 // Swallow the exception: non-EBADF errors in close(2)
923 // indicate deferred paging write errors, which we
924 // don't care about here. The underlying file
925 // descriptor is always closed.
926 if (ex.errno == OsConstants.EBADF) {
927 throw new AssertionError(ex);
928 }
929 }
930 }
931 }
932
933 /**
934 * Close closeable thing, swallowing errors.
935 */
936 private static void safeClose(@Nullable Closeable thing) {
937 if (thing != null) {
938 try {
939 thing.close();
940 } catch (IOException ex) {
941 Slog.w(TAG, "ignoring error closing resource: " + thing, ex);
942 }
943 }
944 }
945
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700946 private final class BinderService extends Binder {
947 @Override
948 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600949 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200950 synchronized (PinnerService.this) {
951 long totalSize = 0;
952 for (PinnedFile pinnedFile : mPinnedFiles) {
953 pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
954 totalSize += pinnedFile.bytesPinned;
955 }
956 pw.println();
957 for (int key : mPinnedApps.keySet()) {
958 PinnedApp app = mPinnedApps.get(key);
959 pw.print(getNameForKey(key));
960 pw.print(" uid="); pw.print(app.uid);
961 pw.print(" active="); pw.print(app.active);
962 pw.println();
963 for (PinnedFile pf : mPinnedApps.get(key).mFiles) {
964 pw.print(" "); pw.format("%s %s\n", pf.fileName, pf.bytesPinned);
965 totalSize += pf.bytesPinned;
966 }
967 }
968 pw.format("Total size: %s\n", totalSize);
969 pw.println();
970 if (!mPendingRepin.isEmpty()) {
971 pw.print("Pending repin: ");
972 for (int key : mPendingRepin.values()) {
973 pw.print(getNameForKey(key)); pw.print(' ');
974 }
975 pw.println();
976 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700977 }
978 }
979 }
980
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700981 private static final class PinnedFile implements AutoCloseable {
982 private long mAddress;
983 final int mapSize;
984 final String fileName;
985 final int bytesPinned;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700986
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700987 PinnedFile(long address, int mapSize, String fileName, int bytesPinned) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700988 mAddress = address;
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700989 this.mapSize = mapSize;
990 this.fileName = fileName;
991 this.bytesPinned = bytesPinned;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700992 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700993
994 @Override
995 public void close() {
996 if (mAddress >= 0) {
997 safeMunmap(mAddress, mapSize);
998 mAddress = -1;
999 }
1000 }
1001
1002 @Override
1003 public void finalize() {
1004 close();
1005 }
1006 }
1007
1008 final static class PinRange {
1009 int start;
1010 int length;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -07001011 }
Philip Cuadraa95cea02016-07-06 16:00:32 -07001012
Jorim Jaggi7119800f2018-07-09 17:57:10 +02001013 /**
1014 * Represents an app that was pinned.
1015 */
1016 private final class PinnedApp {
1017
1018 /**
1019 * The uid of the package being pinned. This stays constant while the package stays
1020 * installed.
1021 */
1022 final int uid;
1023
1024 /** Whether it is currently active, i.e. there is a running process from that package. */
1025 boolean active;
1026
1027 /** List of pinned files. */
1028 final ArrayList<PinnedFile> mFiles = new ArrayList<>();
1029
1030 private PinnedApp(ApplicationInfo appInfo) {
1031 uid = appInfo.uid;
1032 active = mAmInternal.isUidActive(uid);
1033 }
1034 }
1035
Philip Cuadraa95cea02016-07-06 16:00:32 -07001036 final class PinnerHandler extends Handler {
Philip Cuadraa95cea02016-07-06 16:00:32 -07001037 static final int PIN_ONSTART_MSG = 4001;
1038
1039 public PinnerHandler(Looper looper) {
1040 super(looper, null, true);
1041 }
1042
1043 @Override
1044 public void handleMessage(Message msg) {
1045 switch (msg.what) {
Philip Cuadraa95cea02016-07-06 16:00:32 -07001046 case PIN_ONSTART_MSG:
1047 {
1048 handlePinOnStart();
1049 }
1050 break;
1051
1052 default:
1053 super.handleMessage(msg);
1054 }
1055 }
1056 }
1057
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -07001058}