blob: d5f79568367562323d19c9ac3f942ed0d9004273 [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;
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;
Wale Ogunwale214f3482018-10-04 11:00:47 -070062import com.android.server.wm.ActivityTaskManagerInternal;
Valentin Iftimed22452d2019-07-12 15:15:54 +020063
Philip Cuadrad9bd8842016-07-12 17:29:38 -070064import dalvik.system.DexFile;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -070065import dalvik.system.VMRuntime;
66
Daniel Colascione9779b5e2018-03-21 19:13:57 -070067import java.io.Closeable;
Daniel Colascione9779b5e2018-03-21 19:13:57 -070068import java.io.DataInputStream;
Valentin Iftimed22452d2019-07-12 15:15:54 +020069import java.io.FileDescriptor;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070070import java.io.IOException;
Valentin Iftimed22452d2019-07-12 15:15:54 +020071import java.io.InputStream;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070072import java.io.PrintWriter;
Jorim Jaggi7119800f2018-07-09 17:57:10 +020073import java.lang.annotation.Retention;
74import java.lang.annotation.RetentionPolicy;
Jeff Sharkey847bd852016-08-03 17:20:03 -060075import java.util.ArrayList;
Valentin Iftimed22452d2019-07-12 15:15:54 +020076import java.util.List;
Daniel Colascione9779b5e2018-03-21 19:13:57 -070077import java.util.zip.ZipEntry;
Valentin Iftimed22452d2019-07-12 15:15:54 +020078import java.util.zip.ZipFile;
Daniel Colascione9779b5e2018-03-21 19:13:57 -070079
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070080/**
81 * <p>PinnerService pins important files for key processes in memory.</p>
82 * <p>Files to pin are specified in the config_defaultPinnerServiceFiles
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -070083 * overlay.</p>
84 * <p>Pin the default camera application if specified in config_pinnerCameraApp.</p>
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -070085 */
86public final class PinnerService extends SystemService {
87 private static final boolean DEBUG = false;
88 private static final String TAG = "PinnerService";
Jorim Jaggi7119800f2018-07-09 17:57:10 +020089
Daniel Colascione9779b5e2018-03-21 19:13:57 -070090 private static final String PIN_META_FILENAME = "pinlist.meta";
91 private static final int PAGE_SIZE = (int) Os.sysconf(OsConstants._SC_PAGESIZE);
Jorim Jaggi7119800f2018-07-09 17:57:10 +020092 private static final int MATCH_FLAGS = PackageManager.MATCH_DEFAULT_ONLY
93 | PackageManager.MATCH_DIRECT_BOOT_AWARE
94 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
95
96 private static final int KEY_CAMERA = 0;
97 private static final int KEY_HOME = 1;
Valentin Iftime6a652e12019-07-24 16:51:38 +020098 private static final int KEY_ASSISTANT = 2;
Jorim Jaggi7119800f2018-07-09 17:57:10 +020099
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.
Valentin Iftime6a652e12019-07-24 16:51:38 +0200112 private static final int MAX_ASSISTANT_PIN_SIZE = 60 * (1 << 20); // 60MB max for assistant app.
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200113
Valentin Iftime6a652e12019-07-24 16:51:38 +0200114 @IntDef({KEY_CAMERA, KEY_HOME, KEY_ASSISTANT})
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200115 @Retention(RetentionPolicy.SOURCE)
116 public @interface AppKey {}
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700117
118 private final Context mContext;
Wale Ogunwale214f3482018-10-04 11:00:47 -0700119 private final ActivityTaskManagerInternal mAtmInternal;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200120 private final ActivityManagerInternal mAmInternal;
121 private final IActivityManager mAm;
Carmen Jackson0ff99462018-08-09 14:51:39 -0700122 private final UserManager mUserManager;
Valentin Iftime6a652e12019-07-24 16:51:38 +0200123 private SearchManager mSearchManager;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700124
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200125 /** The list of the statically pinned files. */
126 @GuardedBy("this")
127 private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<>();
128
129 /** The list of the pinned apps. This is a map from {@link AppKey} to a pinned app. */
130 @GuardedBy("this")
131 private final ArrayMap<Integer, PinnedApp> mPinnedApps = new ArrayMap<>();
132
133 /**
134 * The list of the pinned apps that need to be repinned as soon as the all processes of a given
135 * uid are no longer active. Note that with background dex opt, the new dex/vdex files are only
136 * loaded into the processes once it restarts. So in case background dex opt recompiled these
137 * files, we still need to keep the old ones pinned until the processes restart.
138 * <p>
139 * This is a map from uid to {@link AppKey}
140 */
141 @GuardedBy("this")
142 private final ArrayMap<Integer, Integer> mPendingRepin = new ArrayMap<>();
143
144 /**
145 * A set of {@link AppKey} that are configured to be pinned.
146 */
147 private final ArraySet<Integer> mPinKeys = new ArraySet<>();
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700148
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700149 private BinderService mBinderService;
Philip Cuadraa95cea02016-07-06 16:00:32 -0700150 private PinnerHandler mPinnerHandler = null;
151
Carmen Jacksonf107a232017-05-16 10:37:26 -0700152 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
153 @Override
154 public void onReceive(Context context, Intent intent) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200155 // If an app has updated, update pinned files accordingly.
156 if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) {
Carmen Jacksonf107a232017-05-16 10:37:26 -0700157 Uri packageUri = intent.getData();
Calin Juravle31ce3a82017-05-22 17:49:01 -0700158 String packageName = packageUri.getSchemeSpecificPart();
159 ArraySet<String> updatedPackages = new ArraySet<>();
160 updatedPackages.add(packageName);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200161 update(updatedPackages, true /* force */);
Carmen Jacksonf107a232017-05-16 10:37:26 -0700162 }
163 }
164 };
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700165
166 public PinnerService(Context context) {
167 super(context);
168
169 mContext = context;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200170 boolean shouldPinCamera = context.getResources().getBoolean(
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700171 com.android.internal.R.bool.config_pinnerCameraApp);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200172 boolean shouldPinHome = context.getResources().getBoolean(
173 com.android.internal.R.bool.config_pinnerHomeApp);
Valentin Iftime6a652e12019-07-24 16:51:38 +0200174 boolean shouldPinAssistant = context.getResources().getBoolean(
175 com.android.internal.R.bool.config_pinnerAssistantApp);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200176 if (shouldPinCamera) {
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700177 if (PROP_PIN_CAMERA) {
178 mPinKeys.add(KEY_CAMERA);
179 } else if (DEBUG) {
180 Slog.i(TAG, "Pinner - skip pinning camera app");
181 }
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200182 }
183 if (shouldPinHome) {
184 mPinKeys.add(KEY_HOME);
185 }
Valentin Iftime6a652e12019-07-24 16:51:38 +0200186 if (shouldPinAssistant) {
187 mPinKeys.add(KEY_ASSISTANT);
188 }
Philip Cuadraa95cea02016-07-06 16:00:32 -0700189 mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());
Carmen Jacksonf107a232017-05-16 10:37:26 -0700190
Wale Ogunwale214f3482018-10-04 11:00:47 -0700191 mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200192 mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
193 mAm = ActivityManager.getService();
194
Carmen Jackson0ff99462018-08-09 14:51:39 -0700195 mUserManager = mContext.getSystemService(UserManager.class);
196
Carmen Jacksonf107a232017-05-16 10:37:26 -0700197 IntentFilter filter = new IntentFilter();
198 filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
199 filter.addDataScheme("package");
200 mContext.registerReceiver(mBroadcastReceiver, filter);
Jorim Jaggi0c849962018-07-15 14:24:38 +0200201
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200202 registerUidListener();
Jorim Jaggi0c849962018-07-15 14:24:38 +0200203 registerUserSetupCompleteListener();
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700204 }
205
206 @Override
207 public void onStart() {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700208 if (DEBUG) {
209 Slog.i(TAG, "Starting PinnerService");
210 }
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700211 mBinderService = new BinderService();
212 publishBinderService("pinner", mBinderService);
Carmen Jacksonf107a232017-05-16 10:37:26 -0700213 publishLocalService(PinnerService.class, this);
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700214
Jeff Sharkey847bd852016-08-03 17:20:03 -0600215 mPinnerHandler.obtainMessage(PinnerHandler.PIN_ONSTART_MSG).sendToTarget();
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200216 sendPinAppsMessage(UserHandle.USER_SYSTEM);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700217 }
218
Valentin Iftime6a652e12019-07-24 16:51:38 +0200219 @Override
220 public void onBootPhase(int phase) {
221 // SearchManagerService is started after PinnerService, wait for PHASE_SYSTEM_SERVICES_READY
222 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
223 mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
224 sendPinAppsMessage(UserHandle.USER_SYSTEM);
225 }
226 }
227
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700228 /**
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200229 * Repin apps on user switch.
230 * <p>
231 * If more than one user is using the device each user may set a different preference for the
232 * individual apps. Make sure that user's preference is pinned into memory.
Philip Cuadraa95cea02016-07-06 16:00:32 -0700233 */
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700234 @Override
235 public void onSwitchUser(int userHandle) {
Carmen Jackson0ff99462018-08-09 14:51:39 -0700236 if (!mUserManager.isManagedProfile(userHandle)) {
237 sendPinAppsMessage(userHandle);
238 }
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200239 }
240
241 @Override
242 public void onUnlockUser(int userHandle) {
Carmen Jackson0ff99462018-08-09 14:51:39 -0700243 if (!mUserManager.isManagedProfile(userHandle)) {
244 sendPinAppsMessage(userHandle);
245 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700246 }
247
Philip Cuadraa95cea02016-07-06 16:00:32 -0700248 /**
Carmen Jacksonf107a232017-05-16 10:37:26 -0700249 * Update the currently pinned files.
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200250 * Specifically, this only updates pinning for the apps that need to be pinned.
Carmen Jacksonf107a232017-05-16 10:37:26 -0700251 * The other files pinned in onStart will not need to be updated.
252 */
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200253 public void update(ArraySet<String> updatedPackages, boolean force) {
254 int currentUser = ActivityManager.getCurrentUser();
255 for (int i = mPinKeys.size() - 1; i >= 0; i--) {
256 int key = mPinKeys.valueAt(i);
257 ApplicationInfo info = getInfoForKey(key, currentUser);
258 if (info != null && updatedPackages.contains(info.packageName)) {
259 Slog.i(TAG, "Updating pinned files for " + info.packageName + " force=" + force);
260 sendPinAppMessage(key, currentUser, force);
261 }
Calin Juravle31ce3a82017-05-22 17:49:01 -0700262 }
Carmen Jacksonf107a232017-05-16 10:37:26 -0700263 }
264
265 /**
Philip Cuadraa95cea02016-07-06 16:00:32 -0700266 * Handler for on start pinning message
267 */
268 private void handlePinOnStart() {
Nicolas Geoffray8b5976e2019-02-20 15:41:03 +0000269 final String bootImage = SystemProperties.get("dalvik.vm.boot-image", "");
270 String[] filesToPin = null;
271 if (bootImage.endsWith("apex.art")) {
272 // Use the files listed for that specific boot image
273 filesToPin = mContext.getResources().getStringArray(
274 com.android.internal.R.array.config_apexBootImagePinnerServiceFiles);
275 } else {
276 // Files to pin come from the overlay and can be specified per-device config
277 filesToPin = mContext.getResources().getStringArray(
278 com.android.internal.R.array.config_defaultPinnerServiceFiles);
279 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700280 // Continue trying to pin each file even if we fail to pin some of them
281 for (String fileToPin : filesToPin) {
282 PinnedFile pf = pinFile(fileToPin,
283 Integer.MAX_VALUE,
284 /*attemptPinIntrospection=*/false);
285 if (pf == null) {
286 Slog.e(TAG, "Failed to pin file = " + fileToPin);
287 continue;
288 }
289
290 synchronized (this) {
291 mPinnedFiles.add(pf);
Philip Cuadraa95cea02016-07-06 16:00:32 -0700292 }
293 }
294 }
295
Jorim Jaggi0c849962018-07-15 14:24:38 +0200296 /**
297 * Registers a listener to repin the home app when user setup is complete, as the home intent
298 * initially resolves to setup wizard, but once setup is complete, it will resolve to the
299 * regular home app.
300 */
301 private void registerUserSetupCompleteListener() {
302 Uri userSetupCompleteUri = Settings.Secure.getUriFor(
303 Settings.Secure.USER_SETUP_COMPLETE);
304 mContext.getContentResolver().registerContentObserver(userSetupCompleteUri,
305 false, new ContentObserver(null) {
306 @Override
307 public void onChange(boolean selfChange, Uri uri) {
308 if (userSetupCompleteUri.equals(uri)) {
309 sendPinAppMessage(KEY_HOME, ActivityManager.getCurrentUser(),
310 true /* force */);
311 }
312 }
313 }, UserHandle.USER_ALL);
314 }
315
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200316 private void registerUidListener() {
317 try {
318 mAm.registerUidObserver(new IUidObserver.Stub() {
319 @Override
320 public void onUidGone(int uid, boolean disabled) throws RemoteException {
321 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
322 PinnerService::handleUidGone, PinnerService.this, uid));
323 }
324
325 @Override
326 public void onUidActive(int uid) throws RemoteException {
327 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
328 PinnerService::handleUidActive, PinnerService.this, uid));
329 }
330
331 @Override
332 public void onUidIdle(int uid, boolean disabled) throws RemoteException {
333 }
334
335 @Override
Hui Yu26969322019-08-21 14:56:35 -0700336 public void onUidStateChanged(int uid, int procState, long procStateSeq,
337 int capability) throws RemoteException {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200338 }
339
340 @Override
341 public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
342 }
343 }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, "system");
344 } catch (RemoteException e) {
345 Slog.e(TAG, "Failed to register uid observer", e);
346 }
347 }
348
349 private void handleUidGone(int uid) {
350 updateActiveState(uid, false /* active */);
351 int key;
352 synchronized (this) {
353
354 // In case we have a pending repin, repin now. See mPendingRepin for more information.
355 key = mPendingRepin.getOrDefault(uid, -1);
356 if (key == -1) {
357 return;
358 }
359 mPendingRepin.remove(uid);
360 }
361 pinApp(key, ActivityManager.getCurrentUser(), false /* force */);
362 }
363
364 private void handleUidActive(int uid) {
365 updateActiveState(uid, true /* active */);
366 }
367
368 private void updateActiveState(int uid, boolean active) {
369 synchronized (this) {
370 for (int i = mPinnedApps.size() - 1; i >= 0; i--) {
371 PinnedApp app = mPinnedApps.valueAt(i);
372 if (app.uid == uid) {
373 app.active = active;
374 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700375 }
376 }
377 }
378
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200379 private void unpinApp(@AppKey int key) {
380 ArrayList<PinnedFile> pinnedAppFiles;
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700381 synchronized (this) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200382 PinnedApp app = mPinnedApps.get(key);
383 if (app == null) {
384 return;
385 }
386 mPinnedApps.remove(key);
387 pinnedAppFiles = new ArrayList<>(app.mFiles);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700388 }
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200389 for (PinnedFile pinnedFile : pinnedAppFiles) {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700390 pinnedFile.close();
391 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700392 }
393
394 private boolean isResolverActivity(ActivityInfo info) {
395 return ResolverActivity.class.getName().equals(info.name);
396 }
397
398 private ApplicationInfo getCameraInfo(int userHandle) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700399 Intent cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
Carmen Jackson879fb682018-07-20 16:43:09 -0700400 ApplicationInfo info = getApplicationInfoForIntent(cameraIntent, userHandle,
401 false /* defaultToSystemApp */);
402
403 // If the STILL_IMAGE_CAMERA intent doesn't resolve, try the _SECURE intent.
404 // We don't use _SECURE first because it will never get set on a device
405 // without File-based Encryption. But if the user has only set the intent
406 // before unlocking their device, we may still be able to identify their
407 // preference using this intent.
408 if (info == null) {
409 cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
410 info = getApplicationInfoForIntent(cameraIntent, userHandle,
411 false /* defaultToSystemApp */);
412 }
413
414 // If the _SECURE intent doesn't resolve, try the original intent but request
415 // the system app for camera if there was more than one result.
416 if (info == null) {
417 cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
418 info = getApplicationInfoForIntent(cameraIntent, userHandle,
419 true /* defaultToSystemApp */);
420 }
421 return info;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200422 }
423
424 private ApplicationInfo getHomeInfo(int userHandle) {
Wale Ogunwale214f3482018-10-04 11:00:47 -0700425 Intent intent = mAtmInternal.getHomeIntent();
Carmen Jackson879fb682018-07-20 16:43:09 -0700426 return getApplicationInfoForIntent(intent, userHandle, false);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200427 }
428
Valentin Iftime6a652e12019-07-24 16:51:38 +0200429 private ApplicationInfo getAssistantInfo(int userHandle) {
430 if (mSearchManager != null) {
431 Intent intent = mSearchManager.getAssistIntent(false);
432 return getApplicationInfoForIntent(intent, userHandle, true);
433 }
434 return null;
435 }
436
Carmen Jackson879fb682018-07-20 16:43:09 -0700437 private ApplicationInfo getApplicationInfoForIntent(Intent intent, int userHandle,
438 boolean defaultToSystemApp) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200439 if (intent == null) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700440 return null;
441 }
Carmen Jackson879fb682018-07-20 16:43:09 -0700442
443 ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivityAsUser(intent,
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200444 MATCH_FLAGS, userHandle);
Carmen Jackson879fb682018-07-20 16:43:09 -0700445
446 // If this intent can resolve to only one app, choose that one.
447 // Otherwise, if we've requested to default to the system app, return it;
448 // if we have not requested that default, return null if there's more than one option.
449 // If there's more than one system app, return null since we don't know which to pick.
450 if (resolveInfo == null) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700451 return null;
452 }
Carmen Jackson879fb682018-07-20 16:43:09 -0700453
454 if (!isResolverActivity(resolveInfo.activityInfo)) {
455 return resolveInfo.activityInfo.applicationInfo;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200456 }
Carmen Jackson879fb682018-07-20 16:43:09 -0700457
458 if (defaultToSystemApp) {
459 List<ResolveInfo> infoList = mContext.getPackageManager()
460 .queryIntentActivitiesAsUser(intent, MATCH_FLAGS, userHandle);
461 ApplicationInfo systemAppInfo = null;
462 for (ResolveInfo info : infoList) {
463 if ((info.activityInfo.applicationInfo.flags
464 & ApplicationInfo.FLAG_SYSTEM) != 0) {
465 if (systemAppInfo == null) {
466 systemAppInfo = info.activityInfo.applicationInfo;
467 } else {
468 // If there's more than one system app, return null due to ambiguity.
469 return null;
470 }
471 }
472 }
473 return systemAppInfo;
474 }
475
476 return null;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200477 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700478
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200479 private void sendPinAppsMessage(int userHandle) {
480 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApps, this,
481 userHandle));
482 }
483
484 private void pinApps(int userHandle) {
485 for (int i = mPinKeys.size() - 1; i >= 0; i--) {
486 int key = mPinKeys.valueAt(i);
487 pinApp(key, userHandle, true /* force */);
488 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700489 }
490
Carmen Jacksonf107a232017-05-16 10:37:26 -0700491 /**
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200492 * @see #pinApp(int, int, boolean)
Carmen Jacksonf107a232017-05-16 10:37:26 -0700493 */
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200494 private void sendPinAppMessage(int key, int userHandle, boolean force) {
495 mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApp, this,
496 key, userHandle, force));
497 }
498
499 /**
500 * Pins an app of a specific type {@code key}.
501 *
502 * @param force If false, this will not repin the app if it's currently active. See
503 * {@link #mPendingRepin}.
504 */
505 private void pinApp(int key, int userHandle, boolean force) {
506 int uid = getUidForKey(key);
507
508 // In case the app is currently active, don't repin until next process restart. See
509 // mPendingRepin for more information.
510 if (!force && uid != -1) {
511 synchronized (this) {
512 mPendingRepin.put(uid, key);
513 }
514 return;
515 }
516 unpinApp(key);
517 ApplicationInfo info = getInfoForKey(key, userHandle);
518 if (info != null) {
519 pinApp(key, info);
520 }
521 }
522
523 /**
524 * Checks whether the pinned package with {@code key} is active or not.
525
526 * @return The uid of the pinned app, or {@code -1} otherwise.
527 */
528 private int getUidForKey(@AppKey int key) {
529 synchronized (this) {
530 PinnedApp existing = mPinnedApps.get(key);
531 return existing != null && existing.active
532 ? existing.uid
533 : -1;
534 }
535 }
536
537 /**
538 * Retrieves the current application info for the given app type.
539 *
540 * @param key The app type to retrieve the info for.
541 * @param userHandle The user id of the current user.
542 */
543 private @Nullable ApplicationInfo getInfoForKey(@AppKey int key, int userHandle) {
544 switch (key) {
545 case KEY_CAMERA:
546 return getCameraInfo(userHandle);
547 case KEY_HOME:
548 return getHomeInfo(userHandle);
Valentin Iftime6a652e12019-07-24 16:51:38 +0200549 case KEY_ASSISTANT:
550 return getAssistantInfo(userHandle);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200551 default:
552 return null;
553 }
554 }
555
556 /**
557 * @return The app type name for {@code key}.
558 */
559 private String getNameForKey(@AppKey int key) {
560 switch (key) {
561 case KEY_CAMERA:
562 return "Camera";
563 case KEY_HOME:
564 return "Home";
Valentin Iftime6a652e12019-07-24 16:51:38 +0200565 case KEY_ASSISTANT:
566 return "Assistant";
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200567 default:
568 return null;
569 }
570 }
571
572 /**
573 * @return The maximum amount of bytes to be pinned for an app of type {@code key}.
574 */
575 private int getSizeLimitForKey(@AppKey int key) {
576 switch (key) {
577 case KEY_CAMERA:
578 return MAX_CAMERA_PIN_SIZE;
579 case KEY_HOME:
580 return MAX_HOME_PIN_SIZE;
Valentin Iftime6a652e12019-07-24 16:51:38 +0200581 case KEY_ASSISTANT:
582 return MAX_ASSISTANT_PIN_SIZE;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200583 default:
584 return 0;
585 }
586 }
587
588 /**
589 * Pins an application.
590 *
591 * @param key The key of the app to pin.
592 * @param appInfo The corresponding app info.
593 */
594 private void pinApp(@AppKey int key, @Nullable ApplicationInfo appInfo) {
595 if (appInfo == null) {
596 return;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700597 }
598
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200599 PinnedApp pinnedApp = new PinnedApp(appInfo);
600 synchronized (this) {
601 mPinnedApps.put(key, pinnedApp);
602 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700603
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200604 // pin APK
605 int pinSizeLimit = getSizeLimitForKey(key);
606 String apk = appInfo.sourceDir;
607 PinnedFile pf = pinFile(apk, pinSizeLimit, /*attemptPinIntrospection=*/true);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700608 if (pf == null) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200609 Slog.e(TAG, "Failed to pin " + apk);
610 return;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700611 }
612 if (DEBUG) {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700613 Slog.i(TAG, "Pinned " + pf.fileName);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700614 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700615 synchronized (this) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200616 pinnedApp.mFiles.add(pf);
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700617 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700618
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700619 // determine the ABI from either ApplicationInfo or Build
Valentin Iftimed22452d2019-07-12 15:15:54 +0200620 String abi = appInfo.primaryCpuAbi != null ? appInfo.primaryCpuAbi :
621 Build.SUPPORTED_ABIS[0];
622 String arch = VMRuntime.getInstructionSet(abi);
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700623 // get the path to the odex or oat file
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200624 String baseCodePath = appInfo.getBaseCodePath();
Calin Juravle128721a2017-05-15 20:20:50 -0700625 String[] files = null;
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700626 try {
Calin Juravle128721a2017-05-15 20:20:50 -0700627 files = DexFile.getDexFileOutputPaths(baseCodePath, arch);
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700628 } catch (IOException ioe) {}
Calin Juravle128721a2017-05-15 20:20:50 -0700629 if (files == null) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200630 return;
Philip Cuadrad9bd8842016-07-12 17:29:38 -0700631 }
632
633 //not pinning the oat/odex is not a fatal error
Calin Juravle128721a2017-05-15 20:20:50 -0700634 for (String file : files) {
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200635 pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false);
Calin Juravle128721a2017-05-15 20:20:50 -0700636 if (pf != null) {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700637 synchronized (this) {
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700638 if (PROP_PIN_ODEX) {
639 pinnedApp.mFiles.add(pf);
640 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700641 }
Calin Juravle128721a2017-05-15 20:20:50 -0700642 if (DEBUG) {
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700643 if (PROP_PIN_ODEX) {
644 Slog.i(TAG, "Pinned " + pf.fileName);
645 } else {
646 Slog.i(TAG, "Pinned [skip] " + pf.fileName);
647 }
Calin Juravle128721a2017-05-15 20:20:50 -0700648 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700649 }
650 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700651 }
652
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700653 /** mlock length bytes of fileToPin in memory
654 *
655 * If attemptPinIntrospection is true, then treat the file to pin as a zip file and
656 * look for a "pinlist.meta" file in the archive root directory. The structure of this
657 * file is a PINLIST_META as described below:
658 *
659 * <pre>
660 * PINLIST_META: PIN_RANGE*
661 * PIN_RANGE: PIN_START PIN_LENGTH
662 * PIN_START: big endian i32: offset in bytes of pin region from file start
663 * PIN_LENGTH: big endian i32: length of pin region in bytes
664 * </pre>
665 *
666 * (We use big endian because that's what DataInputStream is hardcoded to use.)
667 *
668 * If attemptPinIntrospection is false, then we use a single implicit PIN_RANGE of (0,
669 * maxBytesToPin); that is, we attempt to pin the first maxBytesToPin bytes of the file.
670 *
671 * After we open a file, we march through the list of pin ranges and attempt to pin
672 * each one, stopping after we've pinned maxBytesToPin bytes. (We may truncate the last
673 * pinned range to fit.) In this way, by choosing to emit certain PIN_RANGE pairs
674 * before others, file generators can express pins in priority order, making most
675 * effective use of the pinned-page quota.
676 *
677 * N.B. Each PIN_RANGE is clamped to the actual bounds of the file; all inputs have a
678 * meaningful interpretation. Also, a range locking a single byte of a page locks the
679 * whole page. Any truncated PIN_RANGE at EOF is ignored. Overlapping pinned entries
680 * are legal, but each pin of a byte counts toward the pin quota regardless of whether
681 * that byte has already been pinned, so the generator of PINLIST_META ought to ensure
682 * that ranges are non-overlapping.
683 *
684 * @param fileToPin Path to file to pin
685 * @param maxBytesToPin Maximum number of bytes to pin
686 * @param attemptPinIntrospection If true, try to open file as a
687 * zip in order to extract the
688 * @return Pinned memory resource owner thing or null on error
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700689 */
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700690 private static PinnedFile pinFile(String fileToPin,
691 int maxBytesToPin,
692 boolean attemptPinIntrospection) {
693 ZipFile fileAsZip = null;
694 InputStream pinRangeStream = null;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700695 try {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700696 if (attemptPinIntrospection) {
697 fileAsZip = maybeOpenZip(fileToPin);
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700698 }
699
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700700 if (fileAsZip != null) {
701 pinRangeStream = maybeOpenPinMetaInZip(fileAsZip, fileToPin);
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700702 }
703
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700704 Slog.d(TAG, "pinRangeStream: " + pinRangeStream);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700705
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700706 PinRangeSource pinRangeSource = (pinRangeStream != null)
707 ? new PinRangeSourceStream(pinRangeStream)
708 : new PinRangeSourceStatic(0, Integer.MAX_VALUE /* will be clipped */);
709 return pinFileRanges(fileToPin, maxBytesToPin, pinRangeSource);
710 } finally {
711 safeClose(pinRangeStream);
712 safeClose(fileAsZip); // Also closes any streams we've opened
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700713 }
714 }
715
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700716 /**
717 * Attempt to open a file as a zip file. On any sort of corruption, log, swallow the
718 * error, and return null.
719 */
720 private static ZipFile maybeOpenZip(String fileName) {
721 ZipFile zip = null;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700722 try {
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700723 zip = new ZipFile(fileName);
724 } catch (IOException ex) {
725 Slog.w(TAG,
726 String.format(
727 "could not open \"%s\" as zip: pinning as blob",
728 fileName),
729 ex);
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700730 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700731 return zip;
732 }
733
734 /**
735 * Open a pin metadata file in the zip if one is present.
736 *
737 * @param zipFile Zip file to search
738 * @return Open input stream or null on any error
739 */
740 private static InputStream maybeOpenPinMetaInZip(ZipFile zipFile, String fileName) {
Igor Murashkina6d3cd12019-05-13 16:40:39 -0700741 if (!PROP_PIN_PINLIST) {
742 if (DEBUG) {
743 Slog.i(TAG, "Pin - skip pinlist.meta in " + fileName);
744 }
745 return null;
746 }
747
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700748 ZipEntry pinMetaEntry = zipFile.getEntry(PIN_META_FILENAME);
749 InputStream pinMetaStream = null;
750 if (pinMetaEntry != null) {
751 try {
752 pinMetaStream = zipFile.getInputStream(pinMetaEntry);
753 } catch (IOException ex) {
754 Slog.w(TAG,
755 String.format("error reading pin metadata \"%s\": pinning as blob",
756 fileName),
757 ex);
758 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700759 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700760 return pinMetaStream;
761 }
762
763 private static abstract class PinRangeSource {
764 /** Retrive a range to pin.
765 *
766 * @param outPinRange Receives the pin region
767 * @return True if we filled in outPinRange or false if we're out of pin entries
768 */
769 abstract boolean read(PinRange outPinRange);
770 }
771
772 private static final class PinRangeSourceStatic extends PinRangeSource {
773 private final int mPinStart;
774 private final int mPinLength;
775 private boolean mDone = false;
776
777 PinRangeSourceStatic(int pinStart, int pinLength) {
778 mPinStart = pinStart;
779 mPinLength = pinLength;
780 }
781
782 @Override
783 boolean read(PinRange outPinRange) {
784 outPinRange.start = mPinStart;
785 outPinRange.length = mPinLength;
786 boolean done = mDone;
787 mDone = true;
788 return !done;
789 }
790 }
791
792 private static final class PinRangeSourceStream extends PinRangeSource {
793 private final DataInputStream mStream;
794 private boolean mDone = false;
795
796 PinRangeSourceStream(InputStream stream) {
797 mStream = new DataInputStream(stream);
798 }
799
800 @Override
801 boolean read(PinRange outPinRange) {
802 if (!mDone) {
803 try {
804 outPinRange.start = mStream.readInt();
805 outPinRange.length = mStream.readInt();
806 } catch (IOException ex) {
807 mDone = true;
808 }
809 }
810 return !mDone;
811 }
812 }
813
814 /**
815 * Helper for pinFile.
816 *
817 * @param fileToPin Name of file to pin
818 * @param maxBytesToPin Maximum number of bytes to pin
819 * @param pinRangeSource Read PIN_RANGE entries from this stream to tell us what bytes
820 * to pin.
821 * @return PinnedFile or null on error
822 */
823 private static PinnedFile pinFileRanges(
824 String fileToPin,
825 int maxBytesToPin,
826 PinRangeSource pinRangeSource)
827 {
828 FileDescriptor fd = new FileDescriptor();
829 long address = -1;
830 int mapSize = 0;
831
832 try {
833 int openFlags = (OsConstants.O_RDONLY |
834 OsConstants.O_CLOEXEC |
835 OsConstants.O_NOFOLLOW);
836 fd = Os.open(fileToPin, openFlags, 0);
837 mapSize = (int) Math.min(Os.fstat(fd).st_size, Integer.MAX_VALUE);
838 address = Os.mmap(0, mapSize,
839 OsConstants.PROT_READ,
840 OsConstants.MAP_SHARED,
841 fd, /*offset=*/0);
842
843 PinRange pinRange = new PinRange();
844 int bytesPinned = 0;
845
846 // We pin at page granularity, so make sure the limit is page-aligned
847 if (maxBytesToPin % PAGE_SIZE != 0) {
848 maxBytesToPin -= maxBytesToPin % PAGE_SIZE;
849 }
850
851 while (bytesPinned < maxBytesToPin && pinRangeSource.read(pinRange)) {
852 int pinStart = pinRange.start;
853 int pinLength = pinRange.length;
854 pinStart = clamp(0, pinStart, mapSize);
855 pinLength = clamp(0, pinLength, mapSize - pinStart);
856 pinLength = Math.min(maxBytesToPin - bytesPinned, pinLength);
857
858 // mlock doesn't require the region to be page-aligned, but we snap the
859 // lock region to page boundaries anyway so that we don't under-count
860 // locking a single byte of a page as a charge of one byte even though the
861 // OS will retain the whole page. Thanks to this adjustment, we slightly
862 // over-count the pin charge of back-to-back pins touching the same page,
863 // but better that than undercounting. Besides: nothing stops pin metafile
864 // creators from making the actual regions page-aligned.
865 pinLength += pinStart % PAGE_SIZE;
866 pinStart -= pinStart % PAGE_SIZE;
867 if (pinLength % PAGE_SIZE != 0) {
868 pinLength += PAGE_SIZE - pinLength % PAGE_SIZE;
869 }
870 pinLength = clamp(0, pinLength, maxBytesToPin - bytesPinned);
871
872 if (pinLength > 0) {
873 if (DEBUG) {
874 Slog.d(TAG,
875 String.format(
876 "pinning at %s %s bytes of %s",
877 pinStart, pinLength, fileToPin));
878 }
879 Os.mlock(address + pinStart, pinLength);
880 }
881 bytesPinned += pinLength;
882 }
883
884 PinnedFile pinnedFile = new PinnedFile(address, mapSize, fileToPin, bytesPinned);
885 address = -1; // Ownership transferred
886 return pinnedFile;
887 } catch (ErrnoException ex) {
888 Slog.e(TAG, "Could not pin file " + fileToPin, ex);
889 return null;
890 } finally {
891 safeClose(fd);
892 if (address >= 0) {
893 safeMunmap(address, mapSize);
894 }
895 }
896 }
897
898 private static int clamp(int min, int value, int max) {
899 return Math.max(min, Math.min(value, max));
900 }
901
902 private static void safeMunmap(long address, long mapSize) {
903 try {
904 Os.munmap(address, mapSize);
905 } catch (ErrnoException ex) {
906 Slog.w(TAG, "ignoring error in unmap", ex);
907 }
908 }
909
910 /**
911 * Close FD, swallowing irrelevant errors.
912 */
913 private static void safeClose(@Nullable FileDescriptor fd) {
914 if (fd != null && fd.valid()) {
915 try {
916 Os.close(fd);
917 } catch (ErrnoException ex) {
918 // Swallow the exception: non-EBADF errors in close(2)
919 // indicate deferred paging write errors, which we
920 // don't care about here. The underlying file
921 // descriptor is always closed.
922 if (ex.errno == OsConstants.EBADF) {
923 throw new AssertionError(ex);
924 }
925 }
926 }
927 }
928
929 /**
930 * Close closeable thing, swallowing errors.
931 */
932 private static void safeClose(@Nullable Closeable thing) {
933 if (thing != null) {
934 try {
935 thing.close();
936 } catch (IOException ex) {
937 Slog.w(TAG, "ignoring error closing resource: " + thing, ex);
938 }
939 }
940 }
941
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700942 private final class BinderService extends Binder {
943 @Override
944 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600945 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200946 synchronized (PinnerService.this) {
947 long totalSize = 0;
948 for (PinnedFile pinnedFile : mPinnedFiles) {
949 pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
950 totalSize += pinnedFile.bytesPinned;
951 }
952 pw.println();
953 for (int key : mPinnedApps.keySet()) {
954 PinnedApp app = mPinnedApps.get(key);
955 pw.print(getNameForKey(key));
956 pw.print(" uid="); pw.print(app.uid);
957 pw.print(" active="); pw.print(app.active);
958 pw.println();
959 for (PinnedFile pf : mPinnedApps.get(key).mFiles) {
960 pw.print(" "); pw.format("%s %s\n", pf.fileName, pf.bytesPinned);
961 totalSize += pf.bytesPinned;
962 }
963 }
964 pw.format("Total size: %s\n", totalSize);
965 pw.println();
966 if (!mPendingRepin.isEmpty()) {
967 pw.print("Pending repin: ");
968 for (int key : mPendingRepin.values()) {
969 pw.print(getNameForKey(key)); pw.print(' ');
970 }
971 pw.println();
972 }
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700973 }
974 }
975 }
976
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700977 private static final class PinnedFile implements AutoCloseable {
978 private long mAddress;
979 final int mapSize;
980 final String fileName;
981 final int bytesPinned;
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700982
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700983 PinnedFile(long address, int mapSize, String fileName, int bytesPinned) {
Philip Cuadra7cb2f8b2016-06-15 16:23:43 -0700984 mAddress = address;
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700985 this.mapSize = mapSize;
986 this.fileName = fileName;
987 this.bytesPinned = bytesPinned;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -0700988 }
Daniel Colascione9779b5e2018-03-21 19:13:57 -0700989
990 @Override
991 public void close() {
992 if (mAddress >= 0) {
993 safeMunmap(mAddress, mapSize);
994 mAddress = -1;
995 }
996 }
997
998 @Override
999 public void finalize() {
1000 close();
1001 }
1002 }
1003
1004 final static class PinRange {
1005 int start;
1006 int length;
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -07001007 }
Philip Cuadraa95cea02016-07-06 16:00:32 -07001008
Jorim Jaggi7119800f2018-07-09 17:57:10 +02001009 /**
1010 * Represents an app that was pinned.
1011 */
1012 private final class PinnedApp {
1013
1014 /**
1015 * The uid of the package being pinned. This stays constant while the package stays
1016 * installed.
1017 */
1018 final int uid;
1019
1020 /** Whether it is currently active, i.e. there is a running process from that package. */
1021 boolean active;
1022
1023 /** List of pinned files. */
1024 final ArrayList<PinnedFile> mFiles = new ArrayList<>();
1025
1026 private PinnedApp(ApplicationInfo appInfo) {
1027 uid = appInfo.uid;
1028 active = mAmInternal.isUidActive(uid);
1029 }
1030 }
1031
Philip Cuadraa95cea02016-07-06 16:00:32 -07001032 final class PinnerHandler extends Handler {
Philip Cuadraa95cea02016-07-06 16:00:32 -07001033 static final int PIN_ONSTART_MSG = 4001;
1034
1035 public PinnerHandler(Looper looper) {
1036 super(looper, null, true);
1037 }
1038
1039 @Override
1040 public void handleMessage(Message msg) {
1041 switch (msg.what) {
Philip Cuadraa95cea02016-07-06 16:00:32 -07001042 case PIN_ONSTART_MSG:
1043 {
1044 handlePinOnStart();
1045 }
1046 break;
1047
1048 default:
1049 super.handleMessage(msg);
1050 }
1051 }
1052 }
1053
Philip Cuadra7bd0fdd2016-04-28 15:26:49 -07001054}