blob: 16c84c63959b9ae1c2c23b3dc22bc6c68bee45aa [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;
24
Jorim Jaggi7119800f2018-07-09 17:57:10 +020025import android.app.ActivityManager;
26import android.app.ActivityManagerInternal;
27import android.app.IActivityManager;
28import android.app.IUidObserver;
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;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -070048import android.provider.MediaStore;
Jorim Jaggi0c849962018-07-15 14:24:38 +020049import android.provider.Settings;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070050import android.system.ErrnoException;
51import android.system.Os;
52import android.system.OsConstants;
Jorim Jaggi7119800f2018-07-09 17:57:10 +020053import android.util.ArrayMap;
Calin Juravle31ce3a82017-05-22 17:49:01 -070054import android.util.ArraySet;
Jeff Sharkey847bd852016-08-03 17:20:03 -060055import android.util.Slog;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070056
Jorim Jaggi7119800f2018-07-09 17:57:10 +020057import com.android.internal.annotations.GuardedBy;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -070058import com.android.internal.app.ResolverActivity;
Philip Cuadraa95cea02016-07-06 16:00:32 -070059import com.android.internal.os.BackgroundThread;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060060import com.android.internal.util.DumpUtils;
Jorim Jaggi7119800f2018-07-09 17:57:10 +020061import com.android.internal.util.function.pooled.PooledLambda;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -070062
Wale Ogunwale214f3482018-10-04 11:00:47 -070063import com.android.server.wm.ActivityTaskManagerInternal;
Philip Cuadrad9bd8842016-07-12 17:29:38 -070064import dalvik.system.DexFile;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -070065import dalvik.system.VMRuntime;
66
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070067import java.io.FileDescriptor;
Daniel Colascione9779b5e2018-03-21 19:13:57 -070068import java.io.Closeable;
69import java.io.InputStream;
70import java.io.DataInputStream;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070071import java.io.IOException;
72import java.io.PrintWriter;
Jorim Jaggi7119800f2018-07-09 17:57:10 +020073import java.lang.annotation.Retention;
74import java.lang.annotation.RetentionPolicy;
Carmen Jackson879fb682018-07-20 16:43:09 -070075import java.util.List;
Jeff Sharkey847bd852016-08-03 17:20:03 -060076import java.util.ArrayList;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070077
Daniel Colascione9779b5e2018-03-21 19:13:57 -070078import java.util.zip.ZipFile;
Daniel Colascione9779b5e2018-03-21 19:13:57 -070079import java.util.zip.ZipEntry;
80
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;
99
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700100 // Pin the camera application.
101 private static boolean PROP_PIN_CAMERA = SystemProperties.getBoolean(
102 "pinner.pin_camera", true);
103 // Pin using pinlist.meta when pinning apps.
104 private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean(
105 "pinner.use_pinlist", true);
106 // Pin the whole odex/vdex/etc file when pinning apps.
107 private static boolean PROP_PIN_ODEX = SystemProperties.getBoolean(
108 "pinner.whole_odex", true);
109
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200110 private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app.
111 private static final int MAX_HOME_PIN_SIZE = 6 * (1 << 20); // 6MB max for home app.
112
113 @IntDef({KEY_CAMERA, KEY_HOME})
114 @Retention(RetentionPolicy.SOURCE)
115 public @interface AppKey {}
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700116
117 private final Context mContext;
Wale Ogunwale214f3482018-10-04 11:00:47 -0700118 private final ActivityTaskManagerInternal mAtmInternal;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200119 private final ActivityManagerInternal mAmInternal;
120 private final IActivityManager mAm;
Carmen Jackson0ff99462018-08-09 14:51:39 -0700121 private final UserManager mUserManager;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700122
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200123 /** The list of the statically pinned files. */
124 @GuardedBy("this")
125 private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<>();
126
127 /** The list of the pinned apps. This is a map from {@link AppKey} to a pinned app. */
128 @GuardedBy("this")
129 private final ArrayMap<Integer, PinnedApp> mPinnedApps = new ArrayMap<>();
130
131 /**
132 * The list of the pinned apps that need to be repinned as soon as the all processes of a given
133 * uid are no longer active. Note that with background dex opt, the new dex/vdex files are only
134 * loaded into the processes once it restarts. So in case background dex opt recompiled these
135 * files, we still need to keep the old ones pinned until the processes restart.
136 * <p>
137 * This is a map from uid to {@link AppKey}
138 */
139 @GuardedBy("this")
140 private final ArrayMap<Integer, Integer> mPendingRepin = new ArrayMap<>();
141
142 /**
143 * A set of {@link AppKey} that are configured to be pinned.
144 */
145 private final ArraySet<Integer> mPinKeys = new ArraySet<>();
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700146
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700147 private BinderService mBinderService;
Philip Cuadraa95cea02016-07-06 16:00:32 -0700148 private PinnerHandler mPinnerHandler = null;
149
Carmen Jacksonf107a232017-05-16 10:37:26 -0700150 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
151 @Override
152 public void onReceive(Context context, Intent intent) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200153 // If an app has updated, update pinned files accordingly.
154 if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) {
Carmen Jacksonf107a232017-05-16 10:37:26 -0700155 Uri packageUri = intent.getData();
Calin Juravle31ce3a82017-05-22 17:49:01 -0700156 String packageName = packageUri.getSchemeSpecificPart();
157 ArraySet<String> updatedPackages = new ArraySet<>();
158 updatedPackages.add(packageName);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200159 update(updatedPackages, true /* force */);
Carmen Jacksonf107a232017-05-16 10:37:26 -0700160 }
161 }
162 };
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700163
164 public PinnerService(Context context) {
165 super(context);
166
167 mContext = context;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200168 boolean shouldPinCamera = context.getResources().getBoolean(
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700169 com.android.internal.R.bool.config_pinnerCameraApp);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200170 boolean shouldPinHome = context.getResources().getBoolean(
171 com.android.internal.R.bool.config_pinnerHomeApp);
172 if (shouldPinCamera) {
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700173 if (PROP_PIN_CAMERA) {
174 mPinKeys.add(KEY_CAMERA);
175 } else if (DEBUG) {
176 Slog.i(TAG, "Pinner - skip pinning camera app");
177 }
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200178 }
179 if (shouldPinHome) {
180 mPinKeys.add(KEY_HOME);
181 }
Philip Cuadraa95cea02016-07-06 16:00:32 -0700182 mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());
Carmen Jacksonf107a232017-05-16 10:37:26 -0700183
Wale Ogunwale214f3482018-10-04 11:00:47 -0700184 mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200185 mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
186 mAm = ActivityManager.getService();
187
Carmen Jackson0ff99462018-08-09 14:51:39 -0700188 mUserManager = mContext.getSystemService(UserManager.class);
189
Carmen Jacksonf107a232017-05-16 10:37:26 -0700190 IntentFilter filter = new IntentFilter();
191 filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
192 filter.addDataScheme("package");
193 mContext.registerReceiver(mBroadcastReceiver, filter);
Jorim Jaggi0c849962018-07-15 14:24:38 +0200194
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200195 registerUidListener();
Jorim Jaggi0c849962018-07-15 14:24:38 +0200196 registerUserSetupCompleteListener();
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700197 }
198
199 @Override
200 public void onStart() {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700201 if (DEBUG) {
202 Slog.i(TAG, "Starting PinnerService");
203 }
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700204 mBinderService = new BinderService();
205 publishBinderService("pinner", mBinderService);
Carmen Jacksonf107a232017-05-16 10:37:26 -0700206 publishLocalService(PinnerService.class, this);
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700207
Jeff Sharkey847bd852016-08-03 17:20:03 -0600208 mPinnerHandler.obtainMessage(PinnerHandler.PIN_ONSTART_MSG).sendToTarget();
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200209 sendPinAppsMessage(UserHandle.USER_SYSTEM);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700210 }
211
212 /**
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200213 * Repin apps on user switch.
214 * <p>
215 * If more than one user is using the device each user may set a different preference for the
216 * individual apps. Make sure that user's preference is pinned into memory.
Philip Cuadraa95cea02016-07-06 16:00:32 -0700217 */
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700218 @Override
219 public void onSwitchUser(int userHandle) {
Carmen Jackson0ff99462018-08-09 14:51:39 -0700220 if (!mUserManager.isManagedProfile(userHandle)) {
221 sendPinAppsMessage(userHandle);
222 }
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200223 }
224
225 @Override
226 public void onUnlockUser(int userHandle) {
Carmen Jackson0ff99462018-08-09 14:51:39 -0700227 if (!mUserManager.isManagedProfile(userHandle)) {
228 sendPinAppsMessage(userHandle);
229 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700230 }
231
Philip Cuadraa95cea02016-07-06 16:00:32 -0700232 /**
Carmen Jacksonf107a232017-05-16 10:37:26 -0700233 * Update the currently pinned files.
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200234 * Specifically, this only updates pinning for the apps that need to be pinned.
Carmen Jacksonf107a232017-05-16 10:37:26 -0700235 * The other files pinned in onStart will not need to be updated.
236 */
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200237 public void update(ArraySet<String> updatedPackages, boolean force) {
238 int currentUser = ActivityManager.getCurrentUser();
239 for (int i = mPinKeys.size() - 1; i >= 0; i--) {
240 int key = mPinKeys.valueAt(i);
241 ApplicationInfo info = getInfoForKey(key, currentUser);
242 if (info != null && updatedPackages.contains(info.packageName)) {
243 Slog.i(TAG, "Updating pinned files for " + info.packageName + " force=" + force);
244 sendPinAppMessage(key, currentUser, force);
245 }
Calin Juravle31ce3a82017-05-22 17:49:01 -0700246 }
Carmen Jacksonf107a232017-05-16 10:37:26 -0700247 }
248
249 /**
Philip Cuadraa95cea02016-07-06 16:00:32 -0700250 * Handler for on start pinning message
251 */
252 private void handlePinOnStart() {
Nicolas Geoffray8b5976e2019-02-20 15:41:03 +0000253 final String bootImage = SystemProperties.get("dalvik.vm.boot-image", "");
254 String[] filesToPin = null;
255 if (bootImage.endsWith("apex.art")) {
256 // Use the files listed for that specific boot image
257 filesToPin = mContext.getResources().getStringArray(
258 com.android.internal.R.array.config_apexBootImagePinnerServiceFiles);
259 } else {
260 // Files to pin come from the overlay and can be specified per-device config
261 filesToPin = mContext.getResources().getStringArray(
262 com.android.internal.R.array.config_defaultPinnerServiceFiles);
263 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700264 // Continue trying to pin each file even if we fail to pin some of them
265 for (String fileToPin : filesToPin) {
266 PinnedFile pf = pinFile(fileToPin,
267 Integer.MAX_VALUE,
268 /*attemptPinIntrospection=*/false);
269 if (pf == null) {
270 Slog.e(TAG, "Failed to pin file = " + fileToPin);
271 continue;
272 }
273
274 synchronized (this) {
275 mPinnedFiles.add(pf);
Philip Cuadraa95cea02016-07-06 16:00:32 -0700276 }
277 }
278 }
279
Jorim Jaggi0c849962018-07-15 14:24:38 +0200280 /**
281 * Registers a listener to repin the home app when user setup is complete, as the home intent
282 * initially resolves to setup wizard, but once setup is complete, it will resolve to the
283 * regular home app.
284 */
285 private void registerUserSetupCompleteListener() {
286 Uri userSetupCompleteUri = Settings.Secure.getUriFor(
287 Settings.Secure.USER_SETUP_COMPLETE);
288 mContext.getContentResolver().registerContentObserver(userSetupCompleteUri,
289 false, new ContentObserver(null) {
290 @Override
291 public void onChange(boolean selfChange, Uri uri) {
292 if (userSetupCompleteUri.equals(uri)) {
293 sendPinAppMessage(KEY_HOME, ActivityManager.getCurrentUser(),
294 true /* force */);
295 }
296 }
297 }, UserHandle.USER_ALL);
298 }
299
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200300 private void registerUidListener() {
301 try {
302 mAm.registerUidObserver(new IUidObserver.Stub() {
303 @Override
304 public void onUidGone(int uid, boolean disabled) throws RemoteException {
305 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
306 PinnerService::handleUidGone, PinnerService.this, uid));
307 }
308
309 @Override
310 public void onUidActive(int uid) throws RemoteException {
311 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
312 PinnerService::handleUidActive, PinnerService.this, uid));
313 }
314
315 @Override
316 public void onUidIdle(int uid, boolean disabled) throws RemoteException {
317 }
318
319 @Override
320 public void onUidStateChanged(int uid, int procState, long procStateSeq)
321 throws RemoteException {
322 }
323
324 @Override
325 public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
326 }
327 }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, "system");
328 } catch (RemoteException e) {
329 Slog.e(TAG, "Failed to register uid observer", e);
330 }
331 }
332
333 private void handleUidGone(int uid) {
334 updateActiveState(uid, false /* active */);
335 int key;
336 synchronized (this) {
337
338 // In case we have a pending repin, repin now. See mPendingRepin for more information.
339 key = mPendingRepin.getOrDefault(uid, -1);
340 if (key == -1) {
341 return;
342 }
343 mPendingRepin.remove(uid);
344 }
345 pinApp(key, ActivityManager.getCurrentUser(), false /* force */);
346 }
347
348 private void handleUidActive(int uid) {
349 updateActiveState(uid, true /* active */);
350 }
351
352 private void updateActiveState(int uid, boolean active) {
353 synchronized (this) {
354 for (int i = mPinnedApps.size() - 1; i >= 0; i--) {
355 PinnedApp app = mPinnedApps.valueAt(i);
356 if (app.uid == uid) {
357 app.active = active;
358 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700359 }
360 }
361 }
362
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200363 private void unpinApp(@AppKey int key) {
364 ArrayList<PinnedFile> pinnedAppFiles;
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700365 synchronized (this) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200366 PinnedApp app = mPinnedApps.get(key);
367 if (app == null) {
368 return;
369 }
370 mPinnedApps.remove(key);
371 pinnedAppFiles = new ArrayList<>(app.mFiles);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700372 }
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200373 for (PinnedFile pinnedFile : pinnedAppFiles) {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700374 pinnedFile.close();
375 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700376 }
377
378 private boolean isResolverActivity(ActivityInfo info) {
379 return ResolverActivity.class.getName().equals(info.name);
380 }
381
382 private ApplicationInfo getCameraInfo(int userHandle) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700383 Intent cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
Carmen Jackson879fb682018-07-20 16:43:09 -0700384 ApplicationInfo info = getApplicationInfoForIntent(cameraIntent, userHandle,
385 false /* defaultToSystemApp */);
386
387 // If the STILL_IMAGE_CAMERA intent doesn't resolve, try the _SECURE intent.
388 // We don't use _SECURE first because it will never get set on a device
389 // without File-based Encryption. But if the user has only set the intent
390 // before unlocking their device, we may still be able to identify their
391 // preference using this intent.
392 if (info == null) {
393 cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
394 info = getApplicationInfoForIntent(cameraIntent, userHandle,
395 false /* defaultToSystemApp */);
396 }
397
398 // If the _SECURE intent doesn't resolve, try the original intent but request
399 // the system app for camera if there was more than one result.
400 if (info == null) {
401 cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
402 info = getApplicationInfoForIntent(cameraIntent, userHandle,
403 true /* defaultToSystemApp */);
404 }
405 return info;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200406 }
407
408 private ApplicationInfo getHomeInfo(int userHandle) {
Wale Ogunwale214f3482018-10-04 11:00:47 -0700409 Intent intent = mAtmInternal.getHomeIntent();
Carmen Jackson879fb682018-07-20 16:43:09 -0700410 return getApplicationInfoForIntent(intent, userHandle, false);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200411 }
412
Carmen Jackson879fb682018-07-20 16:43:09 -0700413 private ApplicationInfo getApplicationInfoForIntent(Intent intent, int userHandle,
414 boolean defaultToSystemApp) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200415 if (intent == null) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700416 return null;
417 }
Carmen Jackson879fb682018-07-20 16:43:09 -0700418
419 ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivityAsUser(intent,
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200420 MATCH_FLAGS, userHandle);
Carmen Jackson879fb682018-07-20 16:43:09 -0700421
422 // If this intent can resolve to only one app, choose that one.
423 // Otherwise, if we've requested to default to the system app, return it;
424 // if we have not requested that default, return null if there's more than one option.
425 // If there's more than one system app, return null since we don't know which to pick.
426 if (resolveInfo == null) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700427 return null;
428 }
Carmen Jackson879fb682018-07-20 16:43:09 -0700429
430 if (!isResolverActivity(resolveInfo.activityInfo)) {
431 return resolveInfo.activityInfo.applicationInfo;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200432 }
Carmen Jackson879fb682018-07-20 16:43:09 -0700433
434 if (defaultToSystemApp) {
435 List<ResolveInfo> infoList = mContext.getPackageManager()
436 .queryIntentActivitiesAsUser(intent, MATCH_FLAGS, userHandle);
437 ApplicationInfo systemAppInfo = null;
438 for (ResolveInfo info : infoList) {
439 if ((info.activityInfo.applicationInfo.flags
440 & ApplicationInfo.FLAG_SYSTEM) != 0) {
441 if (systemAppInfo == null) {
442 systemAppInfo = info.activityInfo.applicationInfo;
443 } else {
444 // If there's more than one system app, return null due to ambiguity.
445 return null;
446 }
447 }
448 }
449 return systemAppInfo;
450 }
451
452 return null;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200453 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700454
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200455 private void sendPinAppsMessage(int userHandle) {
456 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApps, this,
457 userHandle));
458 }
459
460 private void pinApps(int userHandle) {
461 for (int i = mPinKeys.size() - 1; i >= 0; i--) {
462 int key = mPinKeys.valueAt(i);
463 pinApp(key, userHandle, true /* force */);
464 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700465 }
466
Carmen Jacksonf107a232017-05-16 10:37:26 -0700467 /**
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200468 * @see #pinApp(int, int, boolean)
Carmen Jacksonf107a232017-05-16 10:37:26 -0700469 */
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200470 private void sendPinAppMessage(int key, int userHandle, boolean force) {
471 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApp, this,
472 key, userHandle, force));
473 }
474
475 /**
476 * Pins an app of a specific type {@code key}.
477 *
478 * @param force If false, this will not repin the app if it's currently active. See
479 * {@link #mPendingRepin}.
480 */
481 private void pinApp(int key, int userHandle, boolean force) {
482 int uid = getUidForKey(key);
483
484 // In case the app is currently active, don't repin until next process restart. See
485 // mPendingRepin for more information.
486 if (!force && uid != -1) {
487 synchronized (this) {
488 mPendingRepin.put(uid, key);
489 }
490 return;
491 }
492 unpinApp(key);
493 ApplicationInfo info = getInfoForKey(key, userHandle);
494 if (info != null) {
495 pinApp(key, info);
496 }
497 }
498
499 /**
500 * Checks whether the pinned package with {@code key} is active or not.
501
502 * @return The uid of the pinned app, or {@code -1} otherwise.
503 */
504 private int getUidForKey(@AppKey int key) {
505 synchronized (this) {
506 PinnedApp existing = mPinnedApps.get(key);
507 return existing != null && existing.active
508 ? existing.uid
509 : -1;
510 }
511 }
512
513 /**
514 * Retrieves the current application info for the given app type.
515 *
516 * @param key The app type to retrieve the info for.
517 * @param userHandle The user id of the current user.
518 */
519 private @Nullable ApplicationInfo getInfoForKey(@AppKey int key, int userHandle) {
520 switch (key) {
521 case KEY_CAMERA:
522 return getCameraInfo(userHandle);
523 case KEY_HOME:
524 return getHomeInfo(userHandle);
525 default:
526 return null;
527 }
528 }
529
530 /**
531 * @return The app type name for {@code key}.
532 */
533 private String getNameForKey(@AppKey int key) {
534 switch (key) {
535 case KEY_CAMERA:
536 return "Camera";
537 case KEY_HOME:
538 return "Home";
539 default:
540 return null;
541 }
542 }
543
544 /**
545 * @return The maximum amount of bytes to be pinned for an app of type {@code key}.
546 */
547 private int getSizeLimitForKey(@AppKey int key) {
548 switch (key) {
549 case KEY_CAMERA:
550 return MAX_CAMERA_PIN_SIZE;
551 case KEY_HOME:
552 return MAX_HOME_PIN_SIZE;
553 default:
554 return 0;
555 }
556 }
557
558 /**
559 * Pins an application.
560 *
561 * @param key The key of the app to pin.
562 * @param appInfo The corresponding app info.
563 */
564 private void pinApp(@AppKey int key, @Nullable ApplicationInfo appInfo) {
565 if (appInfo == null) {
566 return;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700567 }
568
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200569 PinnedApp pinnedApp = new PinnedApp(appInfo);
570 synchronized (this) {
571 mPinnedApps.put(key, pinnedApp);
572 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700573
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200574 // pin APK
575 int pinSizeLimit = getSizeLimitForKey(key);
576 String apk = appInfo.sourceDir;
577 PinnedFile pf = pinFile(apk, pinSizeLimit, /*attemptPinIntrospection=*/true);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700578 if (pf == null) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200579 Slog.e(TAG, "Failed to pin " + apk);
580 return;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700581 }
582 if (DEBUG) {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700583 Slog.i(TAG, "Pinned " + pf.fileName);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700584 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700585 synchronized (this) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200586 pinnedApp.mFiles.add(pf);
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700587 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700588
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700589 // determine the ABI from either ApplicationInfo or Build
590 String arch = "arm";
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200591 if (appInfo.primaryCpuAbi != null) {
592 if (VMRuntime.is64BitAbi(appInfo.primaryCpuAbi)) {
John Eckerdal60b07cd2016-11-03 14:04:47 +0100593 arch = arch + "64";
594 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700595 } else {
596 if (VMRuntime.is64BitAbi(Build.SUPPORTED_ABIS[0])) {
597 arch = arch + "64";
598 }
599 }
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700600
601 // get the path to the odex or oat file
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200602 String baseCodePath = appInfo.getBaseCodePath();
Calin Juravle128721a2017-05-15 20:20:50 -0700603 String[] files = null;
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700604 try {
Calin Juravle128721a2017-05-15 20:20:50 -0700605 files = DexFile.getDexFileOutputPaths(baseCodePath, arch);
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700606 } catch (IOException ioe) {}
Calin Juravle128721a2017-05-15 20:20:50 -0700607 if (files == null) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200608 return;
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700609 }
610
611 //not pinning the oat/odex is not a fatal error
Calin Juravle128721a2017-05-15 20:20:50 -0700612 for (String file : files) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200613 pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false);
Calin Juravle128721a2017-05-15 20:20:50 -0700614 if (pf != null) {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700615 synchronized (this) {
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700616 if (PROP_PIN_ODEX) {
617 pinnedApp.mFiles.add(pf);
618 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700619 }
Calin Juravle128721a2017-05-15 20:20:50 -0700620 if (DEBUG) {
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700621 if (PROP_PIN_ODEX) {
622 Slog.i(TAG, "Pinned " + pf.fileName);
623 } else {
624 Slog.i(TAG, "Pinned [skip] " + pf.fileName);
625 }
Calin Juravle128721a2017-05-15 20:20:50 -0700626 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700627 }
628 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700629 }
630
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700631 /** mlock length bytes of fileToPin in memory
632 *
633 * If attemptPinIntrospection is true, then treat the file to pin as a zip file and
634 * look for a "pinlist.meta" file in the archive root directory. The structure of this
635 * file is a PINLIST_META as described below:
636 *
637 * <pre>
638 * PINLIST_META: PIN_RANGE*
639 * PIN_RANGE: PIN_START PIN_LENGTH
640 * PIN_START: big endian i32: offset in bytes of pin region from file start
641 * PIN_LENGTH: big endian i32: length of pin region in bytes
642 * </pre>
643 *
644 * (We use big endian because that's what DataInputStream is hardcoded to use.)
645 *
646 * If attemptPinIntrospection is false, then we use a single implicit PIN_RANGE of (0,
647 * maxBytesToPin); that is, we attempt to pin the first maxBytesToPin bytes of the file.
648 *
649 * After we open a file, we march through the list of pin ranges and attempt to pin
650 * each one, stopping after we've pinned maxBytesToPin bytes. (We may truncate the last
651 * pinned range to fit.) In this way, by choosing to emit certain PIN_RANGE pairs
652 * before others, file generators can express pins in priority order, making most
653 * effective use of the pinned-page quota.
654 *
655 * N.B. Each PIN_RANGE is clamped to the actual bounds of the file; all inputs have a
656 * meaningful interpretation. Also, a range locking a single byte of a page locks the
657 * whole page. Any truncated PIN_RANGE at EOF is ignored. Overlapping pinned entries
658 * are legal, but each pin of a byte counts toward the pin quota regardless of whether
659 * that byte has already been pinned, so the generator of PINLIST_META ought to ensure
660 * that ranges are non-overlapping.
661 *
662 * @param fileToPin Path to file to pin
663 * @param maxBytesToPin Maximum number of bytes to pin
664 * @param attemptPinIntrospection If true, try to open file as a
665 * zip in order to extract the
666 * @return Pinned memory resource owner thing or null on error
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700667 */
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700668 private static PinnedFile pinFile(String fileToPin,
669 int maxBytesToPin,
670 boolean attemptPinIntrospection) {
671 ZipFile fileAsZip = null;
672 InputStream pinRangeStream = null;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700673 try {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700674 if (attemptPinIntrospection) {
675 fileAsZip = maybeOpenZip(fileToPin);
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700676 }
677
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700678 if (fileAsZip != null) {
679 pinRangeStream = maybeOpenPinMetaInZip(fileAsZip, fileToPin);
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700680 }
681
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700682 Slog.d(TAG, "pinRangeStream: " + pinRangeStream);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700683
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700684 PinRangeSource pinRangeSource = (pinRangeStream != null)
685 ? new PinRangeSourceStream(pinRangeStream)
686 : new PinRangeSourceStatic(0, Integer.MAX_VALUE /* will be clipped */);
687 return pinFileRanges(fileToPin, maxBytesToPin, pinRangeSource);
688 } finally {
689 safeClose(pinRangeStream);
690 safeClose(fileAsZip); // Also closes any streams we've opened
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700691 }
692 }
693
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700694 /**
695 * Attempt to open a file as a zip file. On any sort of corruption, log, swallow the
696 * error, and return null.
697 */
698 private static ZipFile maybeOpenZip(String fileName) {
699 ZipFile zip = null;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700700 try {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700701 zip = new ZipFile(fileName);
702 } catch (IOException ex) {
703 Slog.w(TAG,
704 String.format(
705 "could not open \"%s\" as zip: pinning as blob",
706 fileName),
707 ex);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700708 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700709 return zip;
710 }
711
712 /**
713 * Open a pin metadata file in the zip if one is present.
714 *
715 * @param zipFile Zip file to search
716 * @return Open input stream or null on any error
717 */
718 private static InputStream maybeOpenPinMetaInZip(ZipFile zipFile, String fileName) {
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700719 if (!PROP_PIN_PINLIST) {
720 if (DEBUG) {
721 Slog.i(TAG, "Pin - skip pinlist.meta in " + fileName);
722 }
723 return null;
724 }
725
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700726 ZipEntry pinMetaEntry = zipFile.getEntry(PIN_META_FILENAME);
727 InputStream pinMetaStream = null;
728 if (pinMetaEntry != null) {
729 try {
730 pinMetaStream = zipFile.getInputStream(pinMetaEntry);
731 } catch (IOException ex) {
732 Slog.w(TAG,
733 String.format("error reading pin metadata \"%s\": pinning as blob",
734 fileName),
735 ex);
736 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700737 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700738 return pinMetaStream;
739 }
740
741 private static abstract class PinRangeSource {
742 /** Retrive a range to pin.
743 *
744 * @param outPinRange Receives the pin region
745 * @return True if we filled in outPinRange or false if we're out of pin entries
746 */
747 abstract boolean read(PinRange outPinRange);
748 }
749
750 private static final class PinRangeSourceStatic extends PinRangeSource {
751 private final int mPinStart;
752 private final int mPinLength;
753 private boolean mDone = false;
754
755 PinRangeSourceStatic(int pinStart, int pinLength) {
756 mPinStart = pinStart;
757 mPinLength = pinLength;
758 }
759
760 @Override
761 boolean read(PinRange outPinRange) {
762 outPinRange.start = mPinStart;
763 outPinRange.length = mPinLength;
764 boolean done = mDone;
765 mDone = true;
766 return !done;
767 }
768 }
769
770 private static final class PinRangeSourceStream extends PinRangeSource {
771 private final DataInputStream mStream;
772 private boolean mDone = false;
773
774 PinRangeSourceStream(InputStream stream) {
775 mStream = new DataInputStream(stream);
776 }
777
778 @Override
779 boolean read(PinRange outPinRange) {
780 if (!mDone) {
781 try {
782 outPinRange.start = mStream.readInt();
783 outPinRange.length = mStream.readInt();
784 } catch (IOException ex) {
785 mDone = true;
786 }
787 }
788 return !mDone;
789 }
790 }
791
792 /**
793 * Helper for pinFile.
794 *
795 * @param fileToPin Name of file to pin
796 * @param maxBytesToPin Maximum number of bytes to pin
797 * @param pinRangeSource Read PIN_RANGE entries from this stream to tell us what bytes
798 * to pin.
799 * @return PinnedFile or null on error
800 */
801 private static PinnedFile pinFileRanges(
802 String fileToPin,
803 int maxBytesToPin,
804 PinRangeSource pinRangeSource)
805 {
806 FileDescriptor fd = new FileDescriptor();
807 long address = -1;
808 int mapSize = 0;
809
810 try {
811 int openFlags = (OsConstants.O_RDONLY |
812 OsConstants.O_CLOEXEC |
813 OsConstants.O_NOFOLLOW);
814 fd = Os.open(fileToPin, openFlags, 0);
815 mapSize = (int) Math.min(Os.fstat(fd).st_size, Integer.MAX_VALUE);
816 address = Os.mmap(0, mapSize,
817 OsConstants.PROT_READ,
818 OsConstants.MAP_SHARED,
819 fd, /*offset=*/0);
820
821 PinRange pinRange = new PinRange();
822 int bytesPinned = 0;
823
824 // We pin at page granularity, so make sure the limit is page-aligned
825 if (maxBytesToPin % PAGE_SIZE != 0) {
826 maxBytesToPin -= maxBytesToPin % PAGE_SIZE;
827 }
828
829 while (bytesPinned < maxBytesToPin && pinRangeSource.read(pinRange)) {
830 int pinStart = pinRange.start;
831 int pinLength = pinRange.length;
832 pinStart = clamp(0, pinStart, mapSize);
833 pinLength = clamp(0, pinLength, mapSize - pinStart);
834 pinLength = Math.min(maxBytesToPin - bytesPinned, pinLength);
835
836 // mlock doesn't require the region to be page-aligned, but we snap the
837 // lock region to page boundaries anyway so that we don't under-count
838 // locking a single byte of a page as a charge of one byte even though the
839 // OS will retain the whole page. Thanks to this adjustment, we slightly
840 // over-count the pin charge of back-to-back pins touching the same page,
841 // but better that than undercounting. Besides: nothing stops pin metafile
842 // creators from making the actual regions page-aligned.
843 pinLength += pinStart % PAGE_SIZE;
844 pinStart -= pinStart % PAGE_SIZE;
845 if (pinLength % PAGE_SIZE != 0) {
846 pinLength += PAGE_SIZE - pinLength % PAGE_SIZE;
847 }
848 pinLength = clamp(0, pinLength, maxBytesToPin - bytesPinned);
849
850 if (pinLength > 0) {
851 if (DEBUG) {
852 Slog.d(TAG,
853 String.format(
854 "pinning at %s %s bytes of %s",
855 pinStart, pinLength, fileToPin));
856 }
857 Os.mlock(address + pinStart, pinLength);
858 }
859 bytesPinned += pinLength;
860 }
861
862 PinnedFile pinnedFile = new PinnedFile(address, mapSize, fileToPin, bytesPinned);
863 address = -1; // Ownership transferred
864 return pinnedFile;
865 } catch (ErrnoException ex) {
866 Slog.e(TAG, "Could not pin file " + fileToPin, ex);
867 return null;
868 } finally {
869 safeClose(fd);
870 if (address >= 0) {
871 safeMunmap(address, mapSize);
872 }
873 }
874 }
875
876 private static int clamp(int min, int value, int max) {
877 return Math.max(min, Math.min(value, max));
878 }
879
880 private static void safeMunmap(long address, long mapSize) {
881 try {
882 Os.munmap(address, mapSize);
883 } catch (ErrnoException ex) {
884 Slog.w(TAG, "ignoring error in unmap", ex);
885 }
886 }
887
888 /**
889 * Close FD, swallowing irrelevant errors.
890 */
891 private static void safeClose(@Nullable FileDescriptor fd) {
892 if (fd != null && fd.valid()) {
893 try {
894 Os.close(fd);
895 } catch (ErrnoException ex) {
896 // Swallow the exception: non-EBADF errors in close(2)
897 // indicate deferred paging write errors, which we
898 // don't care about here. The underlying file
899 // descriptor is always closed.
900 if (ex.errno == OsConstants.EBADF) {
901 throw new AssertionError(ex);
902 }
903 }
904 }
905 }
906
907 /**
908 * Close closeable thing, swallowing errors.
909 */
910 private static void safeClose(@Nullable Closeable thing) {
911 if (thing != null) {
912 try {
913 thing.close();
914 } catch (IOException ex) {
915 Slog.w(TAG, "ignoring error closing resource: " + thing, ex);
916 }
917 }
918 }
919
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700920 private final class BinderService extends Binder {
921 @Override
922 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600923 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200924 synchronized (PinnerService.this) {
925 long totalSize = 0;
926 for (PinnedFile pinnedFile : mPinnedFiles) {
927 pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
928 totalSize += pinnedFile.bytesPinned;
929 }
930 pw.println();
931 for (int key : mPinnedApps.keySet()) {
932 PinnedApp app = mPinnedApps.get(key);
933 pw.print(getNameForKey(key));
934 pw.print(" uid="); pw.print(app.uid);
935 pw.print(" active="); pw.print(app.active);
936 pw.println();
937 for (PinnedFile pf : mPinnedApps.get(key).mFiles) {
938 pw.print(" "); pw.format("%s %s\n", pf.fileName, pf.bytesPinned);
939 totalSize += pf.bytesPinned;
940 }
941 }
942 pw.format("Total size: %s\n", totalSize);
943 pw.println();
944 if (!mPendingRepin.isEmpty()) {
945 pw.print("Pending repin: ");
946 for (int key : mPendingRepin.values()) {
947 pw.print(getNameForKey(key)); pw.print(' ');
948 }
949 pw.println();
950 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700951 }
952 }
953 }
954
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700955 private static final class PinnedFile implements AutoCloseable {
956 private long mAddress;
957 final int mapSize;
958 final String fileName;
959 final int bytesPinned;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700960
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700961 PinnedFile(long address, int mapSize, String fileName, int bytesPinned) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700962 mAddress = address;
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700963 this.mapSize = mapSize;
964 this.fileName = fileName;
965 this.bytesPinned = bytesPinned;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700966 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700967
968 @Override
969 public void close() {
970 if (mAddress >= 0) {
971 safeMunmap(mAddress, mapSize);
972 mAddress = -1;
973 }
974 }
975
976 @Override
977 public void finalize() {
978 close();
979 }
980 }
981
982 final static class PinRange {
983 int start;
984 int length;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700985 }
Philip Cuadraa95cea02016-07-06 16:00:32 -0700986
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200987 /**
988 * Represents an app that was pinned.
989 */
990 private final class PinnedApp {
991
992 /**
993 * The uid of the package being pinned. This stays constant while the package stays
994 * installed.
995 */
996 final int uid;
997
998 /** Whether it is currently active, i.e. there is a running process from that package. */
999 boolean active;
1000
1001 /** List of pinned files. */
1002 final ArrayList<PinnedFile> mFiles = new ArrayList<>();
1003
1004 private PinnedApp(ApplicationInfo appInfo) {
1005 uid = appInfo.uid;
1006 active = mAmInternal.isUidActive(uid);
1007 }
1008 }
1009
Philip Cuadraa95cea02016-07-06 16:00:32 -07001010 final class PinnerHandler extends Handler {
Philip Cuadraa95cea02016-07-06 16:00:32 -07001011 static final int PIN_ONSTART_MSG = 4001;
1012
1013 public PinnerHandler(Looper looper) {
1014 super(looper, null, true);
1015 }
1016
1017 @Override
1018 public void handleMessage(Message msg) {
1019 switch (msg.what) {
Philip Cuadraa95cea02016-07-06 16:00:32 -07001020 case PIN_ONSTART_MSG:
1021 {
1022 handlePinOnStart();
1023 }
1024 break;
1025
1026 default:
1027 super.handleMessage(msg);
1028 }
1029 }
1030 }
1031
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -07001032}