blob: 99e1388dcd52e1fb8552ae0b580718dbdc76c062 [file] [log] [blame]
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.pm;
import static android.Manifest.permission.DELETE_PACKAGES;
import static android.Manifest.permission.INSTALL_PACKAGES;
import static android.Manifest.permission.MANAGE_DEVICE_ADMINS;
import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.CATEGORY_HOME;
import static android.content.Intent.EXTRA_LONG_VERSION_CODE;
import static android.content.Intent.EXTRA_PACKAGE_NAME;
import static android.content.Intent.EXTRA_VERSION_CODE;
import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER;
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
import static android.content.pm.PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID;
import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
import static android.content.pm.PackageManager.INSTALL_INTERNAL;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.MATCH_APEX;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
import static android.content.pm.PackageManager.MATCH_KNOWN_PACKAGES;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.MOVE_FAILED_3RD_PARTY_NOT_ALLOWED_ON_INTERNAL;
import static android.content.pm.PackageManager.MOVE_FAILED_DEVICE_ADMIN;
import static android.content.pm.PackageManager.MOVE_FAILED_DOESNT_EXIST;
import static android.content.pm.PackageManager.MOVE_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.MOVE_FAILED_LOCKED_USER;
import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING;
import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageManager.RESTRICTION_NONE;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.incremental.IncrementalManager.isIncrementalPath;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.internal.util.ArrayUtils.emptyIfNull;
import static com.android.internal.util.ArrayUtils.filter;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME;
import static com.android.server.pm.ComponentResolver.RESOLVE_PRIORITY_SORTER;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
import static com.android.server.pm.PackageManagerServiceUtils.decompressFile;
import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
import static com.android.server.pm.PackageManagerServiceUtils.dumpCriticalInfo;
import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles;
import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive;
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import android.Manifest;
import android.annotation.AppIdInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.ApplicationPackageManager;
import android.app.BroadcastOptions;
import android.app.IActivityManager;
import android.app.ResourcesManager;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
import android.app.backup.IBackupManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.ChangedPackages;
import android.content.pm.ComponentInfo;
import android.content.pm.DataLoaderType;
import android.content.pm.FallbackCategoryProvider;
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
import android.content.pm.IPackageChangeObserver;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageManagerNative;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstallSourceInfo;
import android.content.pm.InstantAppInfo;
import android.content.pm.InstantAppRequest;
import android.content.pm.InstantAppResolveInfo.InstantAppDigest;
import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageChangeEvent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
import android.content.pm.PackageManager.ModuleInfoFlags;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser.ParseFlags;
import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.PackagePartitions;
import android.content.pm.PackagePartitions.SystemPartition;
import android.content.pm.PackageStats;
import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ProcessInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.SELinuxUtil;
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.pm.SigningInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.dex.IArtManager;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.content.pm.parsing.component.ParsedPermission;
import android.content.pm.parsing.component.ParsedProcess;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.res.Resources;
import android.content.rollback.IRollbackManager;
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.PatternMatcher;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SELinux;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalStorage;
import android.os.storage.DiskInfo;
import android.os.storage.IStorageManager;
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
import android.permission.IPermissionManager;
import android.provider.ContactsContract;
import android.provider.DeviceConfig;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.security.KeyStore;
import android.security.SystemKeyStore;
import android.service.pm.PackageServiceDumpProto;
import android.stats.storage.StorageEnums;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Base64;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.ExceptionUtils;
import android.util.IntArray;
import android.util.Log;
import android.util.LogPrinter;
import android.util.LongSparseArray;
import android.util.LongSparseLongArray;
import android.util.MathUtils;
import android.util.PackageUtils;
import android.util.Pair;
import android.util.PrintStreamPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TimingsTraceLog;
import android.util.Xml;
import android.util.apk.ApkSignatureVerifier;
import android.util.jar.StrictJarFile;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.SomeArgs;
import com.android.internal.os.Zygote;
import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.AttributeCache;
import com.android.server.DeviceIdleInternal;
import com.android.server.EventLogTags;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.PackageWatchdog;
import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.Watchdog;
import com.android.server.compat.PlatformCompat;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.pm.Settings.VersionInfo;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.dex.ViewCompiler;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.BasePermission;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.permission.PermissionsState;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.security.VerityUtils;
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.ActivityTaskManagerInternal;
import dalvik.system.CloseGuard;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
import libcore.util.HexEncoding;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.security.DigestException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* Keep track of all those APKs everywhere.
* <p>
* Internally there are two important locks:
* <ul>
* <li>{@link #mLock} is used to guard all in-memory parsed package details
* and other related state. It is a fine-grained lock that should only be held
* momentarily, as it's one of the most contended locks in the system.
* <li>{@link #mInstallLock} is used to guard all {@code installd} access, whose
* operations typically involve heavy lifting of application data on disk. Since
* {@code installd} is single-threaded, and it's operations can often be slow,
* this lock should never be acquired while already holding {@link #mLock}.
* Conversely, it's safe to acquire {@link #mLock} momentarily while already
* holding {@link #mInstallLock}.
* </ul>
* Many internal methods rely on the caller to hold the appropriate locks, and
* this contract is expressed through method name suffixes:
* <ul>
* <li>fooLI(): the caller must hold {@link #mInstallLock}
* <li>fooLIF(): the caller must hold {@link #mInstallLock} and the package
* being modified must be frozen
* <li>fooLPr(): the caller must hold {@link #mLock} for reading
* <li>fooLPw(): the caller must hold {@link #mLock} for writing
* </ul>
* <p>
* Because this class is very central to the platform's security; please run all
* CTS and unit tests whenever making modifications:
*
* <pre>
* $ runtest -c android.content.pm.PackageManagerTests frameworks-core
* $ cts-tradefed run commandAndExit cts -m CtsAppSecurityHostTestCases
* </pre>
*/
public class PackageManagerService extends IPackageManager.Stub
implements PackageSender {
static final String TAG = "PackageManager";
public static final boolean DEBUG_SETTINGS = false;
static final boolean DEBUG_PREFERRED = false;
static final boolean DEBUG_UPGRADE = false;
static final boolean DEBUG_DOMAIN_VERIFICATION = false;
private static final boolean DEBUG_BACKUP = false;
public static final boolean DEBUG_INSTALL = false;
public static final boolean DEBUG_REMOVE = false;
private static final boolean DEBUG_BROADCASTS = false;
private static final boolean DEBUG_PACKAGE_INFO = false;
private static final boolean DEBUG_INTENT_MATCHING = false;
public static final boolean DEBUG_PACKAGE_SCANNING = false;
private static final boolean DEBUG_VERIFY = false;
public static final boolean DEBUG_PERMISSIONS = false;
private static final boolean DEBUG_SHARED_LIBRARIES = false;
public static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE;
// Debug output for dexopting. This is shared between PackageManagerService, OtaDexoptService
// and PackageDexOptimizer. All these classes have their own flag to allow switching a single
// user, but by default initialize to this.
public static final boolean DEBUG_DEXOPT = false;
static final boolean DEBUG_ABI_SELECTION = false;
private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
private static final boolean DEBUG_APP_DATA = false;
/** REMOVE. According to Svet, this was only used to reset permissions during development. */
static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false;
private static final boolean HIDE_EPHEMERAL_APIS = false;
private static final boolean ENABLE_FREE_CACHE_V2 =
SystemProperties.getBoolean("fw.free_cache_v2", true);
private static final String PRECOMPILE_LAYOUTS = "pm.precompile_layouts";
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
private static final int NFC_UID = Process.NFC_UID;
private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID;
private static final int SHELL_UID = Process.SHELL_UID;
private static final int SE_UID = Process.SE_UID;
private static final int NETWORKSTACK_UID = Process.NETWORK_STACK_UID;
static final int SCAN_NO_DEX = 1 << 0;
static final int SCAN_UPDATE_SIGNATURE = 1 << 1;
static final int SCAN_NEW_INSTALL = 1 << 2;
static final int SCAN_UPDATE_TIME = 1 << 3;
static final int SCAN_BOOTING = 1 << 4;
static final int SCAN_REQUIRE_KNOWN = 1 << 7;
static final int SCAN_MOVE = 1 << 8;
static final int SCAN_INITIAL = 1 << 9;
static final int SCAN_DONT_KILL_APP = 1 << 10;
static final int SCAN_IGNORE_FROZEN = 1 << 11;
static final int SCAN_FIRST_BOOT_OR_UPGRADE = 1 << 12;
static final int SCAN_AS_INSTANT_APP = 1 << 13;
static final int SCAN_AS_FULL_APP = 1 << 14;
static final int SCAN_AS_VIRTUAL_PRELOAD = 1 << 15;
static final int SCAN_AS_SYSTEM = 1 << 16;
static final int SCAN_AS_PRIVILEGED = 1 << 17;
static final int SCAN_AS_OEM = 1 << 18;
static final int SCAN_AS_VENDOR = 1 << 19;
static final int SCAN_AS_PRODUCT = 1 << 20;
static final int SCAN_AS_SYSTEM_EXT = 1 << 21;
static final int SCAN_AS_ODM = 1 << 22;
static final int SCAN_AS_APK_IN_APEX = 1 << 23;
@IntDef(flag = true, prefix = { "SCAN_" }, value = {
SCAN_NO_DEX,
SCAN_UPDATE_SIGNATURE,
SCAN_NEW_INSTALL,
SCAN_UPDATE_TIME,
SCAN_BOOTING,
SCAN_REQUIRE_KNOWN,
SCAN_MOVE,
SCAN_INITIAL,
SCAN_DONT_KILL_APP,
SCAN_IGNORE_FROZEN,
SCAN_FIRST_BOOT_OR_UPGRADE,
SCAN_AS_INSTANT_APP,
SCAN_AS_FULL_APP,
SCAN_AS_VIRTUAL_PRELOAD,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ScanFlags {}
private static final String STATIC_SHARED_LIB_DELIMITER = "_";
/** Extension of the compressed packages */
public final static String COMPRESSED_EXTENSION = ".gz";
/** Suffix of stub packages on the system partition */
public final static String STUB_SUFFIX = "-Stub";
private static final int[] EMPTY_INT_ARRAY = new int[0];
private static final int TYPE_UNKNOWN = 0;
private static final int TYPE_ACTIVITY = 1;
private static final int TYPE_RECEIVER = 2;
private static final int TYPE_SERVICE = 3;
private static final int TYPE_PROVIDER = 4;
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_UNKNOWN,
TYPE_ACTIVITY,
TYPE_RECEIVER,
TYPE_SERVICE,
TYPE_PROVIDER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ComponentType {}
/**
* Timeout (in milliseconds) after which the watchdog should declare that
* our handler thread is wedged. The usual default for such things is one
* minute but we sometimes do very lengthy I/O operations on this thread,
* such as installing multi-gigabyte applications, so ours needs to be longer.
*/
static final long WATCHDOG_TIMEOUT = 1000*60*10; // ten minutes
/**
* Wall-clock timeout (in milliseconds) after which we *require* that an fstrim
* be run on this device. We use the value in the Settings.Global.MANDATORY_FSTRIM_INTERVAL
* settings entry if available, otherwise we use the hardcoded default. If it's been
* more than this long since the last fstrim, we force one during the boot sequence.
*
* This backstops other fstrim scheduling: if the device is alive at midnight+idle,
* one gets run at the next available charging+idle time. This final mandatory
* no-fstrim check kicks in only of the other scheduling criteria is never met.
*/
private static final long DEFAULT_MANDATORY_FSTRIM_INTERVAL = 3 * DateUtils.DAY_IN_MILLIS;
/**
* Whether verification is enabled by default.
*/
private static final boolean DEFAULT_VERIFY_ENABLE = true;
/**
* Whether integrity verification is enabled by default.
*/
private static final boolean DEFAULT_INTEGRITY_VERIFY_ENABLE = true;
/**
* The default maximum time to wait for the verification agent to return in
* milliseconds.
*/
private static final long DEFAULT_VERIFICATION_TIMEOUT = 10 * 1000;
/**
* The default maximum time to wait for the integrity verification to return in
* milliseconds.
*/
private static final long DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT = 30 * 1000;
/**
* Timeout duration in milliseconds for enabling package rollback. If we fail to enable
* rollback within that period, the install will proceed without rollback enabled.
*
* <p>If flag value is negative, the default value will be assigned.
*
* Flag type: {@code long}
* Namespace: NAMESPACE_ROLLBACK
*/
private static final String PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS = "enable_rollback_timeout";
/**
* The default duration to wait for rollback to be enabled in
* milliseconds.
*/
private static final long DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS = 10 * 1000;
/**
* The default response for package verification timeout.
*
* This can be either PackageManager.VERIFICATION_ALLOW or
* PackageManager.VERIFICATION_REJECT.
*/
private static final int DEFAULT_VERIFICATION_RESPONSE = PackageManager.VERIFICATION_ALLOW;
/**
* Adding an installer package name to a package that does not have one set requires the
* INSTALL_PACKAGES permission.
*
* If the caller targets R, this will throw a SecurityException. Otherwise the request will
* fail silently. In both cases, and regardless of whether this change is enabled, the
* installer package will remain unchanged.
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
private static final long THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE =
150857253;
public static final String PLATFORM_PACKAGE_NAME = "android";
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
private static final String PACKAGE_SCHEME = "package";
/** Canonical intent used to identify what counts as a "web browser" app */
private static final Intent sBrowserIntent;
static {
sBrowserIntent = new Intent();
sBrowserIntent.setAction(Intent.ACTION_VIEW);
sBrowserIntent.addCategory(Intent.CATEGORY_BROWSABLE);
sBrowserIntent.setData(Uri.parse("http:"));
sBrowserIntent.addFlags(Intent.FLAG_IGNORE_EPHEMERAL);
}
// Compilation reasons.
public static final int REASON_UNKNOWN = -1;
public static final int REASON_FIRST_BOOT = 0;
public static final int REASON_BOOT = 1;
public static final int REASON_INSTALL = 2;
public static final int REASON_BACKGROUND_DEXOPT = 3;
public static final int REASON_AB_OTA = 4;
public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 5;
public static final int REASON_SHARED = 6;
public static final int REASON_LAST = REASON_SHARED;
/**
* The initial enabled state of the cache before other checks are done.
*/
private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true;
/**
* Whether to skip all other checks and force the cache to be enabled.
*
* Setting this to true will cause the cache to be named "debug" to avoid eviction from
* build fingerprint changes.
*/
private static final boolean FORCE_PACKAGE_PARSED_CACHE_ENABLED = false;
/**
* Permissions required in order to receive instant application lifecycle broadcasts.
*/
private static final String[] INSTANT_APP_BROADCAST_PERMISSION =
new String[] { android.Manifest.permission.ACCESS_INSTANT_APPS };
private static final String RANDOM_DIR_PREFIX = "~~";
final ServiceThread mHandlerThread;
final Handler mHandler;
private final ProcessLoggingHandler mProcessLoggingHandler;
final int mSdkVersion = Build.VERSION.SDK_INT;
final Context mContext;
final boolean mFactoryTest;
final boolean mOnlyCore;
final DisplayMetrics mMetrics;
final int mDefParseFlags;
final String[] mSeparateProcesses;
final boolean mIsUpgrade;
final boolean mIsPreNUpgrade;
final boolean mIsPreNMR1Upgrade;
final boolean mIsPreQUpgrade;
@GuardedBy("mLock")
private boolean mDexOptDialogShown;
// Used for privilege escalation. MUST NOT BE CALLED WITH mPackages
// LOCK HELD. Can be called with mInstallLock held.
@GuardedBy("mInstallLock")
final Installer mInstaller;
/** Directory where installed applications are stored */
private static final File sAppInstallDir =
new File(Environment.getDataDirectory(), "app");
/** Directory where installed application's 32-bit native libraries are copied. */
@VisibleForTesting
static final File sAppLib32InstallDir =
new File(Environment.getDataDirectory(), "app-lib");
// ----------------------------------------------------------------
// Lock for state used when installing and doing other long running
// operations. Methods that must be called with this lock held have
// the suffix "LI".
final Object mInstallLock;
// ----------------------------------------------------------------
// Lock for global state used when modifying package state or settings.
// Methods that must be called with this lock held have
// the suffix "Locked". Some methods may use the legacy suffix "LP"
final Object mLock;
// Keys are String (package name), values are Package.
@GuardedBy("mLock")
final ArrayMap<String, AndroidPackage> mPackages = new ArrayMap<>();
// Keys are isolated uids and values are the uid of the application
// that created the isolated proccess.
@GuardedBy("mLock")
final SparseIntArray mIsolatedOwners = new SparseIntArray();
/**
* Tracks new system packages [received in an OTA] that we expect to
* find updated user-installed versions. Keys are package name, values
* are package location.
*/
final private ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
/**
* Tracks existing packages prior to receiving an OTA. Keys are package name.
* Only non-null during an OTA, and even then it is nulled again once systemReady().
*/
private @Nullable ArraySet<String> mExistingPackages = null;
/**
* Whether or not system app permissions should be promoted from install to runtime.
*/
boolean mPromoteSystemApps;
private final PackageManagerInternal mPmInternal;
@GuardedBy("mLock")
final Settings mSettings;
/**
* Set of package names that are currently "frozen", which means active
* surgery is being done on the code/data for that package. The platform
* will refuse to launch frozen packages to avoid race conditions.
*
* @see PackageFreezer
*/
@GuardedBy("mLock")
final ArraySet<String> mFrozenPackages = new ArraySet<>();
final ProtectedPackages mProtectedPackages;
@GuardedBy("mLoadedVolumes")
final ArraySet<String> mLoadedVolumes = new ArraySet<>();
boolean mFirstBoot;
PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy;
@GuardedBy("mAvailableFeatures")
final ArrayMap<String, FeatureInfo> mAvailableFeatures;
private final InstantAppRegistry mInstantAppRegistry;
@GuardedBy("mLock")
int mChangedPackagesSequenceNumber;
/**
* List of changed [installed, removed or updated] packages.
* mapping from user id -> sequence number -> package name
*/
@GuardedBy("mLock")
final SparseArray<SparseArray<String>> mChangedPackages = new SparseArray<>();
/**
* The sequence number of the last change to a package.
* mapping from user id -> package name -> sequence number
*/
@GuardedBy("mLock")
final SparseArray<Map<String, Integer>> mChangedPackagesSequenceNumbers = new SparseArray<>();
@GuardedBy("mLock")
final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>();
@GuardedBy("mLock")
private final SparseIntArray mDefaultPermissionsGrantedUsers = new SparseIntArray();
private final ModuleInfoProvider mModuleInfoProvider;
private final ApexManager mApexManager;
private final Injector mInjector;
/**
* The list of all system partitions that may contain packages in ascending order of
* specificity (the more generic, the earlier in the list a partition appears).
*/
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static final List<ScanPartition> SYSTEM_PARTITIONS = Collections.unmodifiableList(
PackagePartitions.getOrderedPartitions(ScanPartition::new));
private final List<ScanPartition> mDirsToScanAsSystem;
private final OverlayConfig mOverlayConfig;
@GuardedBy("itself")
final private ArrayList<IPackageChangeObserver> mPackageChangeObservers =
new ArrayList<>();
/**
* Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
*
* NOTE: All getters should return the same instance for every call.
*/
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Injector {
@VisibleForTesting(visibility = Visibility.PRIVATE)
interface Producer<T> {
/** Produce an instance of type {@link T} */
T produce(Injector injector, PackageManagerService packageManager);
}
static class LocalServicesProducer<T> implements Producer<T> {
private final Class<T> mProducingClass;
LocalServicesProducer(Class<T> clazz) {
this.mProducingClass = clazz;
}
public T produce(Injector injector, PackageManagerService packageManager) {
return LocalServices.getService(mProducingClass);
}
}
static class SystemServiceProducer<T> implements Producer<T> {
private final Class<T> mProducingClass;
SystemServiceProducer(Class<T> clazz) {
this.mProducingClass = clazz;
}
public T produce(Injector injector, PackageManagerService packageManager) {
return packageManager.mContext.getSystemService(mProducingClass);
}
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
static class Singleton<T> {
private final Producer<T> mProducer;
private volatile T mInstance = null;
Singleton(Producer<T> producer) {
this.mProducer = producer;
}
T get(Injector injector, PackageManagerService packageManagerService) {
if (mInstance == null) {
mInstance = mProducer.produce(injector, packageManagerService);
}
return mInstance;
}
}
private PackageManagerService mPackageManager;
private final PackageAbiHelper mAbiHelper;
private final Context mContext;
private final Object mLock;
private final Installer mInstaller;
private final Object mInstallLock;
// ----- producers -----
private final Singleton<ComponentResolver> mComponentResolverProducer;
private final Singleton<PermissionManagerServiceInternal> mPermissionManagerProducer;
private final Singleton<UserManagerService> mUserManagerProducer;
private final Singleton<Settings> mSettingsProducer;
private final Singleton<ActivityTaskManagerInternal> mActivityTaskManagerProducer;
private final Singleton<ActivityManagerInternal> mActivityManagerInternalProducer;
private final Singleton<DeviceIdleInternal> mLocalDeviceIdleController;
private final Singleton<StorageManagerInternal> mStorageManagerInternalProducer;
private final Singleton<NetworkPolicyManagerInternal> mNetworkPolicyManagerProducer;
private final Singleton<PermissionPolicyInternal> mPermissionPolicyProducer;
private final Singleton<DeviceStorageMonitorInternal> mDeviceStorageMonitorProducer;
private final Singleton<DisplayManager> mDisplayManagerProducer;
private final Singleton<StorageManager> mStorageManagerProducer;
private final Singleton<AppOpsManager> mAppOpsManagerProducer;
private final Singleton<AppsFilter> mAppsFilterProducer;
private final Singleton<PlatformCompat> mPlatformCompatProducer;
Injector(Context context, Object lock, Installer installer,
Object installLock, PackageAbiHelper abiHelper,
Producer<ComponentResolver> componentResolverProducer,
Producer<PermissionManagerServiceInternal> permissionManagerProducer,
Producer<UserManagerService> userManagerProducer,
Producer<Settings> settingsProducer,
Producer<ActivityTaskManagerInternal> activityTaskManagerProducer,
Producer<ActivityManagerInternal> activityManagerInternalProducer,
Producer<DeviceIdleInternal> deviceIdleControllerProducer,
Producer<StorageManagerInternal> storageManagerInternalProducer,
Producer<NetworkPolicyManagerInternal> networkPolicyManagerProducer,
Producer<PermissionPolicyInternal> permissionPolicyProvider,
Producer<DeviceStorageMonitorInternal> deviceStorageMonitorProducer,
Producer<DisplayManager> displayManagerProducer,
Producer<StorageManager> storageManagerProducer,
Producer<AppOpsManager> appOpsManagerProducer,
Producer<AppsFilter> appsFilterProducer,
Producer<PlatformCompat> platformCompatProducer) {
mContext = context;
mLock = lock;
mInstaller = installer;
mAbiHelper = abiHelper;
mInstallLock = installLock;
mComponentResolverProducer = new Singleton<>(componentResolverProducer);
mPermissionManagerProducer = new Singleton<>(permissionManagerProducer);
mUserManagerProducer = new Singleton<>(userManagerProducer);
mSettingsProducer = new Singleton<>(settingsProducer);
mActivityTaskManagerProducer = new Singleton<>(activityTaskManagerProducer);
mActivityManagerInternalProducer = new Singleton<>(activityManagerInternalProducer);
mLocalDeviceIdleController = new Singleton<>(deviceIdleControllerProducer);
mStorageManagerInternalProducer = new Singleton<>(storageManagerInternalProducer);
mNetworkPolicyManagerProducer = new Singleton<>(networkPolicyManagerProducer);
mPermissionPolicyProducer = new Singleton<>(permissionPolicyProvider);
mDeviceStorageMonitorProducer = new Singleton<>(deviceStorageMonitorProducer);
mDisplayManagerProducer = new Singleton<>(displayManagerProducer);
mStorageManagerProducer = new Singleton<>(storageManagerProducer);
mAppOpsManagerProducer = new Singleton<>(appOpsManagerProducer);
mAppsFilterProducer = new Singleton<>(appsFilterProducer);
mPlatformCompatProducer = new Singleton<>(platformCompatProducer);
}
/**
* Bootstraps this injector with the {@link PackageManagerService instance to which it
* belongs.
*/
public void bootstrap(PackageManagerService pm) {
this.mPackageManager = pm;
}
public UserManagerInternal getUserManagerInternal() {
return getUserManagerService().getInternalForInjectorOnly();
}
public PackageAbiHelper getAbiHelper() {
return mAbiHelper;
}
public Object getInstallLock() {
return mInstallLock;
}
public UserManagerService getUserManagerService() {
return mUserManagerProducer.get(this, mPackageManager);
}
public Object getLock() {
return mLock;
}
public Installer getInstaller() {
return mInstaller;
}
public ComponentResolver getComponentResolver() {
return mComponentResolverProducer.get(this, mPackageManager);
}
public PermissionManagerServiceInternal getPermissionManagerServiceInternal() {
return mPermissionManagerProducer.get(this, mPackageManager);
}
public Context getContext() {
return mContext;
}
public Settings getSettings() {
return mSettingsProducer.get(this, mPackageManager);
}
public ActivityTaskManagerInternal getActivityTaskManagerInternal() {
return mActivityTaskManagerProducer.get(this, mPackageManager);
}
public ActivityManagerInternal getActivityManagerInternal() {
return mActivityManagerInternalProducer.get(this, mPackageManager);
}
public DeviceIdleInternal getLocalDeviceIdleController() {
return mLocalDeviceIdleController.get(this, mPackageManager);
}
public StorageManagerInternal getStorageManagerInternal() {
return mStorageManagerInternalProducer.get(this, mPackageManager);
}
public NetworkPolicyManagerInternal getNetworkPolicyManagerInternal() {
return mNetworkPolicyManagerProducer.get(this, mPackageManager);
}
public PermissionPolicyInternal getPermissionPolicyInternal() {
return mPermissionPolicyProducer.get(this, mPackageManager);
}
public DeviceStorageMonitorInternal getDeviceStorageMonitorInternal() {
return mDeviceStorageMonitorProducer.get(this, mPackageManager);
}
public DisplayManager getDisplayManager() {
return mDisplayManagerProducer.get(this, mPackageManager);
}
public StorageManager getStorageManager() {
return mStorageManagerProducer.get(this, mPackageManager);
}
public AppOpsManager getAppOpsManager() {
return mAppOpsManagerProducer.get(this, mPackageManager);
}
public AppsFilter getAppsFilter() {
return mAppsFilterProducer.get(this, mPackageManager);
}
public PlatformCompat getCompatibility() {
return mPlatformCompatProducer.get(this, mPackageManager);
}
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class TestParams {
public ApexManager apexManager;
public @Nullable String appPredictionServicePackage;
public ArtManagerService artManagerService;
public @Nullable String configuratorPackage;
public int defParseFlags;
public DexManager dexManager;
public List<ScanPartition> dirsToScanAsSystem;
public @Nullable String documenterPackage;
public boolean factoryTest;
public ArrayMap<String, FeatureInfo> availableFeatures;
public Handler handler;
public ServiceThread handlerThread;
public @Nullable String incidentReportApproverPackage;
public IncrementalManager incrementalManager;
public PackageInstallerService installerService;
public InstantAppRegistry instantAppRegistry;
public InstantAppResolverConnection instantAppResolverConnection;
public ComponentName instantAppResolverSettingsComponent;
public @Nullable IntentFilterVerifier<ParsedIntentInfo> intentFilterVerifier;
public @Nullable ComponentName intentFilterVerifierComponent;
public boolean isPreNmr1Upgrade;
public boolean isPreNupgrade;
public boolean isPreQupgrade;
public boolean isUpgrade;
public DisplayMetrics Metrics;
public ModuleInfoProvider moduleInfoProvider;
public MoveCallbacks moveCallbacks;
public boolean onlyCore;
public OverlayConfig overlayConfig;
public PackageDexOptimizer packageDexOptimizer;
public PackageParser2.Callback packageParserCallback;
public IPermissionManager permissionManagerService;
public PendingPackageBroadcasts pendingPackageBroadcasts;
public PackageManagerInternal pmInternal;
public ProcessLoggingHandler processLoggingHandler;
public ProtectedPackages protectedPackages;
public @NonNull String requiredInstallerPackage;
public @NonNull String requiredPermissionControllerPackage;
public @NonNull String requiredUninstallerPackage;
public @Nullable String requiredVerifierPackage;
public String[] separateProcesses;
public @NonNull String servicesExtensionPackageName;
public @Nullable String setupWizardPackage;
public @NonNull String sharedSystemSharedLibraryPackageName;
public @Nullable String storageManagerPackage;
public @Nullable String defaultTextClassifierPackage;
public @Nullable String systemTextClassifierPackage;
public ViewCompiler viewCompiler;
public @Nullable String wellbeingPackage;
public @Nullable String retailDemoPackage;
public ComponentName resolveComponentName;
public ArrayMap<String, AndroidPackage> packages;
}
private final AppsFilter mAppsFilter;
final PackageParser2.Callback mPackageParserCallback;
// Currently known shared libraries.
final ArrayMap<String, LongSparseArray<SharedLibraryInfo>> mSharedLibraries = new ArrayMap<>();
final ArrayMap<String, LongSparseArray<SharedLibraryInfo>> mStaticLibsByDeclaringPackage =
new ArrayMap<>();
// Mapping from instrumentation class names to info about them.
final ArrayMap<ComponentName, ParsedInstrumentation> mInstrumentation =
new ArrayMap<>();
// Packages whose data we have transfered into another package, thus
// should no longer exist.
final ArraySet<String> mTransferredPackages = new ArraySet<>();
// Broadcast actions that are only available to the system.
@GuardedBy("mProtectedBroadcasts")
final ArraySet<String> mProtectedBroadcasts = new ArraySet<>();
/** List of packages waiting for verification. */
final SparseArray<PackageVerificationState> mPendingVerification = new SparseArray<>();
/** List of packages waiting for rollback to be enabled. */
final SparseArray<InstallParams> mPendingEnableRollback = new SparseArray<>();
final PackageInstallerService mInstallerService;
final ArtManagerService mArtManagerService;
private final PackageDexOptimizer mPackageDexOptimizer;
// DexManager handles the usage of dex files (e.g. secondary files, whether or not a package
// is used by other apps).
private final DexManager mDexManager;
private final ViewCompiler mViewCompiler;
private AtomicInteger mNextMoveId = new AtomicInteger();
private final MoveCallbacks mMoveCallbacks;
// Cache of users who need badging.
private final SparseBooleanArray mUserNeedsBadging = new SparseBooleanArray();
/** Token for keys in mPendingVerification. */
private int mPendingVerificationToken = 0;
/** Token for keys in mPendingEnableRollback. */
private int mPendingEnableRollbackToken = 0;
volatile boolean mSystemReady;
volatile boolean mSafeMode;
volatile boolean mHasSystemUidErrors;
private volatile SparseBooleanArray mWebInstantAppsDisabled = new SparseBooleanArray();
ApplicationInfo mAndroidApplication;
final ActivityInfo mResolveActivity = new ActivityInfo();
final ResolveInfo mResolveInfo = new ResolveInfo();
ComponentName mResolveComponentName;
AndroidPackage mPlatformPackage;
ComponentName mCustomResolverComponentName;
boolean mResolverReplaced = false;
private final @Nullable ComponentName mIntentFilterVerifierComponent;
private final @Nullable IntentFilterVerifier<ParsedIntentInfo> mIntentFilterVerifier;
private int mIntentFilterVerificationToken = 0;
/** The service connection to the ephemeral resolver */
final InstantAppResolverConnection mInstantAppResolverConnection;
/** Component used to show resolver settings for Instant Apps */
final ComponentName mInstantAppResolverSettingsComponent;
/** Activity used to install instant applications */
ActivityInfo mInstantAppInstallerActivity;
final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();
private final Map<String, Pair<PackageInstalledInfo, IPackageInstallObserver2>>
mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>());
final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
= new SparseArray<>();
// Internal interface for permission manager
private final PermissionManagerServiceInternal mPermissionManager;
// Public interface for permission manager
private final IPermissionManager mPermissionManagerService;
private final ComponentResolver mComponentResolver;
// List of packages names to keep cached, even if they are uninstalled for all users
private List<String> mKeepUninstalledPackages;
private File mCacheDir;
private Future<?> mPrepareAppDataFuture;
private final IncrementalManager mIncrementalManager;
private static class IFVerificationParams {
String packageName;
boolean hasDomainUrls;
List<ParsedActivity> activities;
boolean replacing;
int userId;
int verifierUid;
public IFVerificationParams(String packageName, boolean hasDomainUrls,
List<ParsedActivity> activities, boolean _replacing,
int _userId, int _verifierUid) {
this.packageName = packageName;
this.hasDomainUrls = hasDomainUrls;
this.activities = activities;
replacing = _replacing;
userId = _userId;
verifierUid = _verifierUid;
}
}
private interface IntentFilterVerifier<T extends IntentFilter> {
boolean addOneIntentFilterVerification(int verifierId, int userId, int verificationId,
T filter, String packageName);
void startVerifications(int userId);
void receiveVerificationResponse(int verificationId);
}
private class IntentVerifierProxy implements IntentFilterVerifier<ParsedIntentInfo> {
private Context mContext;
private ComponentName mIntentFilterVerifierComponent;
private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<>();
public IntentVerifierProxy(Context context, ComponentName verifierComponent) {
mContext = context;
mIntentFilterVerifierComponent = verifierComponent;
}
private String getDefaultScheme() {
return IntentFilter.SCHEME_HTTPS;
}
@Override
public void startVerifications(int userId) {
// Launch verifications requests
int count = mCurrentIntentFilterVerifications.size();
for (int n=0; n<count; n++) {
int verificationId = mCurrentIntentFilterVerifications.get(n);
final IntentFilterVerificationState ivs =
mIntentFilterVerificationStates.get(verificationId);
String packageName = ivs.getPackageName();
ArrayList<ParsedIntentInfo> filters = ivs.getFilters();
final int filterCount = filters.size();
ArraySet<String> domainsSet = new ArraySet<>();
for (int m=0; m<filterCount; m++) {
ParsedIntentInfo filter = filters.get(m);
domainsSet.addAll(filter.getHostsList());
}
synchronized (mLock) {
if (mSettings.createIntentFilterVerificationIfNeededLPw(
packageName, domainsSet) != null) {
scheduleWriteSettingsLocked();
}
}
sendVerificationRequest(verificationId, ivs);
}
mCurrentIntentFilterVerifications.clear();
}
private void sendVerificationRequest(int verificationId, IntentFilterVerificationState ivs) {
Intent verificationIntent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
verificationIntent.putExtra(
PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID,
verificationId);
verificationIntent.putExtra(
PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME,
getDefaultScheme());
verificationIntent.putExtra(
PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS,
ivs.getHostsString());
verificationIntent.putExtra(
PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME,
ivs.getPackageName());
verificationIntent.setComponent(mIntentFilterVerifierComponent);
verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
final long whitelistTimeout = getVerificationTimeout();
final BroadcastOptions options = BroadcastOptions.makeBasic();
options.setTemporaryAppWhitelistDuration(whitelistTimeout);
DeviceIdleInternal idleController =
mInjector.getLocalDeviceIdleController();
idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
mIntentFilterVerifierComponent.getPackageName(), whitelistTimeout,
UserHandle.USER_SYSTEM, true, "intent filter verifier");
mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM,
null, options.toBundle());
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"Sending IntentFilter verification broadcast");
}
public void receiveVerificationResponse(int verificationId) {
IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId);
final boolean verified = ivs.isVerified();
ArrayList<ParsedIntentInfo> filters = ivs.getFilters();
final int count = filters.size();
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.i(TAG, "Received verification response " + verificationId
+ " for " + count + " filters, verified=" + verified);
}
for (int n=0; n<count; n++) {
ParsedIntentInfo filter = filters.get(n);
filter.setVerified(verified);
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "IntentFilter " + filter.toString()
+ " verified with result:" + verified + " and hosts:"
+ ivs.getHostsString());
}
mIntentFilterVerificationStates.remove(verificationId);
final String packageName = ivs.getPackageName();
IntentFilterVerificationInfo ivi;
synchronized (mLock) {
ivi = mSettings.getIntentFilterVerificationLPr(packageName);
}
if (ivi == null) {
Slog.w(TAG, "IntentFilterVerificationInfo not found for verificationId:"
+ verificationId + " packageName:" + packageName);
return;
}
synchronized (mLock) {
if (verified) {
ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS);
} else {
ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK);
}
scheduleWriteSettingsLocked();
final int userId = ivs.getUserId();
if (userId != UserHandle.USER_ALL) {
final int userStatus =
mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
int updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
boolean needUpdate = false;
// In a success case, we promote from undefined or ASK to ALWAYS. This
// supports a flow where the app fails validation but then ships an updated
// APK that passes, and therefore deserves to be in ALWAYS.
//
// If validation failed, the undefined state winds up in the basic ASK behavior,
// but apps that previously passed and became ALWAYS are *demoted* out of
// that state, since they would not deserve the ALWAYS behavior in case of a
// clean install.
switch (userStatus) {
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
if (!verified) {
// Don't demote if sysconfig says 'always'
SystemConfig systemConfig = SystemConfig.getInstance();
ArraySet<String> packages = systemConfig.getLinkedApps();
if (!packages.contains(packageName)) {
// updatedStatus is already UNDEFINED
needUpdate = true;
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG, "Formerly validated but now failing; demoting");
}
} else {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG, "Updating bundled package " + packageName
+ " failed autoVerify, but sysconfig supersedes");
}
// leave needUpdate == false here intentionally
}
}
break;
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
// Stay in 'undefined' on verification failure
if (verified) {
updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
}
needUpdate = true;
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG, "Applying update; old=" + userStatus
+ " new=" + updatedStatus);
}
break;
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
// Keep in 'ask' on failure
if (verified) {
updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
needUpdate = true;
}
break;
default:
// Nothing to do
}
if (needUpdate) {
mSettings.updateIntentFilterVerificationStatusLPw(
packageName, updatedStatus, userId);
scheduleWritePackageRestrictionsLocked(userId);
}
} else {
Slog.i(TAG, "autoVerify ignored when installing for all users");
}
}
}
@Override
public boolean addOneIntentFilterVerification(int verifierUid, int userId, int verificationId,
ParsedIntentInfo filter, String packageName) {
if (!hasValidDomains(filter)) {
return false;
}
IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId);
if (ivs == null) {
ivs = createDomainVerificationState(verifierUid, userId, verificationId,
packageName);
}
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG, "Adding verification filter for " + packageName + ": " + filter);
}
ivs.addFilter(filter);
return true;
}
private IntentFilterVerificationState createDomainVerificationState(int verifierUid,
int userId, int verificationId, String packageName) {
IntentFilterVerificationState ivs = new IntentFilterVerificationState(
verifierUid, userId, packageName);
ivs.setPendingState();
synchronized (mLock) {
mIntentFilterVerificationStates.append(verificationId, ivs);
mCurrentIntentFilterVerifications.add(verificationId);
}
return ivs;
}
}
private static boolean hasValidDomains(ParsedIntentInfo filter) {
return filter.hasCategory(Intent.CATEGORY_BROWSABLE)
&& (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
filter.hasDataScheme(IntentFilter.SCHEME_HTTPS));
}
// Set of pending broadcasts for aggregating enable/disable of components.
@VisibleForTesting(visibility = Visibility.PACKAGE)
public static class PendingPackageBroadcasts {
// for each user id, a map of <package name -> components within that package>
final SparseArray<ArrayMap<String, ArrayList<String>>> mUidMap;
public PendingPackageBroadcasts() {
mUidMap = new SparseArray<>(2);
}
public ArrayList<String> get(int userId, String packageName) {
ArrayMap<String, ArrayList<String>> packages = getOrAllocate(userId);
return packages.get(packageName);
}
public void put(int userId, String packageName, ArrayList<String> components) {
ArrayMap<String, ArrayList<String>> packages = getOrAllocate(userId);
packages.put(packageName, components);
}
public void remove(int userId, String packageName) {
ArrayMap<String, ArrayList<String>> packages = mUidMap.get(userId);
if (packages != null) {
packages.remove(packageName);
}
}
public void remove(int userId) {
mUidMap.remove(userId);
}
public int userIdCount() {
return mUidMap.size();
}
public int userIdAt(int n) {
return mUidMap.keyAt(n);
}
public ArrayMap<String, ArrayList<String>> packagesForUserId(int userId) {
return mUidMap.get(userId);
}
public int size() {
// total number of pending broadcast entries across all userIds
int num = 0;
for (int i = 0; i< mUidMap.size(); i++) {
num += mUidMap.valueAt(i).size();
}
return num;
}
public void clear() {
mUidMap.clear();
}
private ArrayMap<String, ArrayList<String>> getOrAllocate(int userId) {
ArrayMap<String, ArrayList<String>> map = mUidMap.get(userId);
if (map == null) {
map = new ArrayMap<>();
mUidMap.put(userId, map);
}
return map;
}
}
final PendingPackageBroadcasts mPendingBroadcasts;
static final int SEND_PENDING_BROADCAST = 1;
static final int INIT_COPY = 5;
static final int POST_INSTALL = 9;
static final int WRITE_SETTINGS = 13;
static final int WRITE_PACKAGE_RESTRICTIONS = 14;
static final int PACKAGE_VERIFIED = 15;
static final int CHECK_PENDING_VERIFICATION = 16;
static final int START_INTENT_FILTER_VERIFICATIONS = 17;
static final int INTENT_FILTER_VERIFIED = 18;
static final int WRITE_PACKAGE_LIST = 19;
static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20;
static final int ENABLE_ROLLBACK_STATUS = 21;
static final int ENABLE_ROLLBACK_TIMEOUT = 22;
static final int DEFERRED_NO_KILL_POST_DELETE = 23;
static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24;
static final int INTEGRITY_VERIFICATION_COMPLETE = 25;
static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26;
static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds
private static final long BROADCAST_DELAY_DURING_STARTUP = 10 * 1000L; // 10 seconds (in millis)
private static final long BROADCAST_DELAY = 1 * 1000L; // 1 second (in millis)
// When the service constructor finished plus a delay (used for broadcast delay computation)
private long mServiceStartWithDelay;
private static final long DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD =
2 * 60 * 60 * 1000L; /* two hours */
UserManagerService mUserManager;
// Stores a list of users whose package restrictions file needs to be updated
private ArraySet<Integer> mDirtyUsers = new ArraySet<>();
// Recordkeeping of restore-after-install operations that are currently in flight
// between the Package Manager and the Backup Manager
static class PostInstallData {
@Nullable
public final InstallArgs args;
@NonNull
public final PackageInstalledInfo res;
@Nullable
public final Runnable mPostInstallRunnable;
PostInstallData(@Nullable InstallArgs args, @NonNull PackageInstalledInfo res,
@Nullable Runnable postInstallRunnable) {
this.args = args;
this.res = res;
mPostInstallRunnable = postInstallRunnable;
}
}
final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<>();
int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows
// XML tags for backup/restore of various bits of state
private static final String TAG_PREFERRED_BACKUP = "pa";
private static final String TAG_DEFAULT_APPS = "da";
private static final String TAG_INTENT_FILTER_VERIFICATION = "iv";
private static final String TAG_PERMISSION_BACKUP = "perm-grant-backup";
private static final String TAG_ALL_GRANTS = "rt-grants";
private static final String TAG_GRANT = "grant";
private static final String ATTR_PACKAGE_NAME = "pkg";
private static final String TAG_PERMISSION = "perm";
private static final String ATTR_PERMISSION_NAME = "name";
private static final String ATTR_IS_GRANTED = "g";
private static final String ATTR_USER_SET = "set";
private static final String ATTR_USER_FIXED = "fixed";
private static final String ATTR_REVOKE_ON_UPGRADE = "rou";
// System/policy permission grants are not backed up
private static final int SYSTEM_RUNTIME_GRANT_MASK =
FLAG_PERMISSION_POLICY_FIXED
| FLAG_PERMISSION_SYSTEM_FIXED
| FLAG_PERMISSION_GRANTED_BY_DEFAULT;
// And we back up these user-adjusted states
private static final int USER_RUNTIME_GRANT_MASK =
FLAG_PERMISSION_USER_SET
| FLAG_PERMISSION_USER_FIXED
| FLAG_PERMISSION_REVOKED_COMPAT;
final @Nullable String mRequiredVerifierPackage;
final @NonNull String mRequiredInstallerPackage;
final @NonNull String mRequiredUninstallerPackage;
final @NonNull String mRequiredPermissionControllerPackage;
final @Nullable String mSetupWizardPackage;
final @Nullable String mStorageManagerPackage;
final @Nullable String mDefaultTextClassifierPackage;
final @Nullable String mSystemTextClassifierPackageName;
final @Nullable String mWellbeingPackage;
final @Nullable String mDocumenterPackage;
final @Nullable String mConfiguratorPackage;
final @Nullable String mAppPredictionServicePackage;
final @Nullable String mIncidentReportApproverPackage;
final @Nullable String mServicesExtensionPackageName;
final @Nullable String mSharedSystemSharedLibraryPackageName;
final @Nullable String mRetailDemoPackage;
private final PackageUsage mPackageUsage = new PackageUsage();
private final CompilerStats mCompilerStats = new CompilerStats();
class PackageHandler extends Handler {
PackageHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
try {
doHandleMessage(msg);
} finally {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
}
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
if (params != null) {
if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(params));
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
params.startCopy();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
break;
}
case SEND_PENDING_BROADCAST: {
String packages[];
ArrayList<String> components[];
int size = 0;
int uids[];
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mLock) {
size = mPendingBroadcasts.size();
if (size <= 0) {
// Nothing to be done. Just return
return;
}
packages = new String[size];
components = new ArrayList[size];
uids = new int[size];
int i = 0; // filling out the above arrays
for (int n = 0; n < mPendingBroadcasts.userIdCount(); n++) {
int packageUserId = mPendingBroadcasts.userIdAt(n);
Iterator<Map.Entry<String, ArrayList<String>>> it
= mPendingBroadcasts.packagesForUserId(packageUserId)
.entrySet().iterator();
while (it.hasNext() && i < size) {
Map.Entry<String, ArrayList<String>> ent = it.next();
packages[i] = ent.getKey();
components[i] = ent.getValue();
PackageSetting ps = mSettings.mPackages.get(ent.getKey());
uids[i] = (ps != null)
? UserHandle.getUid(packageUserId, ps.appId)
: -1;
i++;
}
}
size = i;
mPendingBroadcasts.clear();
}
// Send broadcasts
for (int i = 0; i < size; i++) {
sendPackageChangedBroadcast(packages[i], true /* dontKillApp */,
components[i], uids[i], null /* reason */);
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
break;
}
case POST_INSTALL: {
if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
PostInstallData data = mRunningInstalls.get(msg.arg1);
final boolean didRestore = (msg.arg2 != 0);
mRunningInstalls.delete(msg.arg1);
if (data != null && data.res.freezer != null) {
data.res.freezer.close();
}
if (data != null && data.mPostInstallRunnable != null) {
data.mPostInstallRunnable.run();
} else if (data != null && data.args != null) {
InstallArgs args = data.args;
PackageInstalledInfo parentRes = data.res;
final boolean grantPermissions = (args.installFlags
& PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
final boolean killApp = (args.installFlags
& PackageManager.INSTALL_DONT_KILL_APP) == 0;
final boolean virtualPreload = ((args.installFlags
& PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
final String[] grantedPermissions = args.installGrantPermissions;
final List<String> whitelistedRestrictedPermissions = ((args.installFlags
& PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0
&& parentRes.pkg != null)
? parentRes.pkg.getRequestedPermissions()
: args.whitelistedRestrictedPermissions;
int autoRevokePermissionsMode = args.autoRevokePermissionsMode;
handlePackagePostInstall(parentRes, grantPermissions,
killApp, virtualPreload, grantedPermissions,
whitelistedRestrictedPermissions, autoRevokePermissionsMode,
didRestore, args.installSource.installerPackageName, args.observer,
args.mDataLoaderType);
// Log tracing if needed
if (args.traceMethod != null) {
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.traceMethod,
args.traceCookie);
}
} else if (DEBUG_INSTALL) {
// No post-install when we run restore from installExistingPackageForUser
Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1);
}
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
} break;
case DEFERRED_NO_KILL_POST_DELETE: {
synchronized (mInstallLock) {
InstallArgs args = (InstallArgs) msg.obj;
if (args != null) {
args.doPostDeleteLI(true);
}
}
} break;
case DEFERRED_NO_KILL_INSTALL_OBSERVER: {
String packageName = (String) msg.obj;
if (packageName != null) {
notifyInstallObserver(packageName);
}
} break;
case WRITE_SETTINGS: {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mLock) {
removeMessages(WRITE_SETTINGS);
removeMessages(WRITE_PACKAGE_RESTRICTIONS);
mSettings.writeLPr();
mDirtyUsers.clear();
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
} break;
case WRITE_PACKAGE_RESTRICTIONS: {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mLock) {
removeMessages(WRITE_PACKAGE_RESTRICTIONS);
for (int userId : mDirtyUsers) {
mSettings.writePackageRestrictionsLPr(userId);
}
mDirtyUsers.clear();
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
} break;
case WRITE_PACKAGE_LIST: {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mLock) {
removeMessages(WRITE_PACKAGE_LIST);
mSettings.writePackageListLPr(msg.arg1);
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
} break;
case CHECK_PENDING_VERIFICATION: {
final int verificationId = msg.arg1;
final PackageVerificationState state = mPendingVerification.get(verificationId);
if ((state != null) && !state.isVerificationComplete()
&& !state.timeoutExtended()) {
final InstallParams params = state.getInstallParams();
final InstallArgs args = params.mArgs;
final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
Slog.i(TAG, "Verification timed out for " + originUri);
final UserHandle user = args.getUser();
if (getDefaultVerificationResponse(user)
== PackageManager.VERIFICATION_ALLOW) {
Slog.i(TAG, "Continuing with installation of " + originUri);
state.setVerifierResponse(Binder.getCallingUid(),
PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_ALLOW, null, args.mDataLoaderType,
user);
} else {
broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_REJECT, null, args.mDataLoaderType,
user);
params.setReturnCode(
PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
state.setVerifierResponse(Binder.getCallingUid(),
PackageManager.VERIFICATION_REJECT);
}
if (state.areAllVerificationsComplete()) {
mPendingVerification.remove(verificationId);
}
Trace.asyncTraceEnd(
TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
params.handleVerificationFinished();
}
break;
}
case CHECK_PENDING_INTEGRITY_VERIFICATION: {
final int verificationId = msg.arg1;
final PackageVerificationState state = mPendingVerification.get(verificationId);
if (state != null && !state.isIntegrityVerificationComplete()) {
final InstallParams params = state.getInstallParams();
final InstallArgs args = params.mArgs;
final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
Slog.i(TAG, "Integrity verification timed out for " + originUri);
state.setIntegrityVerificationResult(
getDefaultIntegrityVerificationResponse());
if (getDefaultIntegrityVerificationResponse()
== PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) {
Slog.i(TAG, "Integrity check times out, continuing with " + originUri);
} else {
params.setReturnCode(
PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
}
if (state.areAllVerificationsComplete()) {
mPendingVerification.remove(verificationId);
}
Trace.asyncTraceEnd(
TRACE_TAG_PACKAGE_MANAGER,
"integrity_verification",
verificationId);
params.handleIntegrityVerificationFinished();
}
break;
}
case PACKAGE_VERIFIED: {
final int verificationId = msg.arg1;
final PackageVerificationState state = mPendingVerification.get(verificationId);
if (state == null) {
Slog.w(TAG, "Verification with id " + verificationId
+ " not found."
+ " It may be invalid or overridden by integrity verification");
break;
}
final PackageVerificationResponse response = (PackageVerificationResponse) msg.obj;
state.setVerifierResponse(response.callerUid, response.code);
if (state.isVerificationComplete()) {
final InstallParams params = state.getInstallParams();
final InstallArgs args = params.mArgs;
final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
if (state.isInstallAllowed()) {
broadcastPackageVerified(verificationId, originUri,
response.code, null, args.mDataLoaderType, args.getUser());
} else {
params.setReturnCode(
PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
}
if (state.areAllVerificationsComplete()) {
mPendingVerification.remove(verificationId);
}
Trace.asyncTraceEnd(
TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
params.handleVerificationFinished();
}
break;
}
case INTEGRITY_VERIFICATION_COMPLETE: {
final int verificationId = msg.arg1;
final PackageVerificationState state = mPendingVerification.get(verificationId);
if (state == null) {
Slog.w(TAG, "Integrity verification with id " + verificationId
+ " not found. It may be invalid or overridden by verifier");
break;
}
final int response = (Integer) msg.obj;
final InstallParams params = state.getInstallParams();
final InstallArgs args = params.mArgs;
final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
state.setIntegrityVerificationResult(response);
if (response == PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) {
Slog.i(TAG, "Integrity check passed for " + originUri);
} else {
params.setReturnCode(
PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
}
if (state.areAllVerificationsComplete()) {
mPendingVerification.remove(verificationId);
}
Trace.asyncTraceEnd(
TRACE_TAG_PACKAGE_MANAGER,
"integrity_verification",
verificationId);
params.handleIntegrityVerificationFinished();
break;
}
case START_INTENT_FILTER_VERIFICATIONS: {
IFVerificationParams params = (IFVerificationParams) msg.obj;
verifyIntentFiltersIfNeeded(params.userId, params.verifierUid, params.replacing,
params.packageName, params.hasDomainUrls, params.activities);
break;
}
case INTENT_FILTER_VERIFIED: {
final int verificationId = msg.arg1;
final IntentFilterVerificationState state = mIntentFilterVerificationStates.get(
verificationId);
if (state == null) {
Slog.w(TAG, "Invalid IntentFilter verification token "
+ verificationId + " received");
break;
}
final int userId = state.getUserId();
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"Processing IntentFilter verification with token:"
+ verificationId + " and userId:" + userId);
final IntentFilterVerificationResponse response =
(IntentFilterVerificationResponse) msg.obj;
state.setVerifierResponse(response.callerUid, response.code);
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"IntentFilter verification with token:" + verificationId
+ " and userId:" + userId
+ " is settings verifier response with response code:"
+ response.code);
if (response.code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Domains failing verification: "
+ response.getFailedDomainsString());
}
if (state.isVerificationComplete()) {
mIntentFilterVerifier.receiveVerificationResponse(verificationId);
} else {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"IntentFilter verification with token:" + verificationId
+ " was not said to be complete");
}
break;
}
case INSTANT_APP_RESOLUTION_PHASE_TWO: {
InstantAppResolver.doInstantAppResolutionPhaseTwo(mContext,
mInstantAppResolverConnection,
(InstantAppRequest) msg.obj,
mInstantAppInstallerActivity,
mHandler);
break;
}
case ENABLE_ROLLBACK_STATUS: {
final int enableRollbackToken = msg.arg1;
final int enableRollbackCode = msg.arg2;
InstallParams params = mPendingEnableRollback.get(enableRollbackToken);
if (params == null) {
Slog.w(TAG, "Invalid rollback enabled token "
+ enableRollbackToken + " received");
break;
}
mPendingEnableRollback.remove(enableRollbackToken);
if (enableRollbackCode != PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED) {
final InstallArgs args = params.mArgs;
final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
Slog.w(TAG, "Failed to enable rollback for " + originUri);
Slog.w(TAG, "Continuing with installation of " + originUri);
}
Trace.asyncTraceEnd(
TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
params.handleRollbackEnabled();
break;
}
case ENABLE_ROLLBACK_TIMEOUT: {
final int enableRollbackToken = msg.arg1;
final int sessionId = msg.arg2;
final InstallParams params = mPendingEnableRollback.get(enableRollbackToken);
if (params != null) {
final InstallArgs args = params.mArgs;
final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
Slog.w(TAG, "Enable rollback timed out for " + originUri);
mPendingEnableRollback.remove(enableRollbackToken);
Slog.w(TAG, "Continuing with installation of " + originUri);
Trace.asyncTraceEnd(
TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
params.handleRollbackEnabled();
Intent rollbackTimeoutIntent = new Intent(
Intent.ACTION_CANCEL_ENABLE_ROLLBACK);
rollbackTimeoutIntent.putExtra(
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID,
sessionId);
rollbackTimeoutIntent.addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(rollbackTimeoutIntent, UserHandle.SYSTEM,
android.Manifest.permission.PACKAGE_ROLLBACK_AGENT);
}
break;
}
}
}
}
private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
boolean killApp, boolean virtualPreload,
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
int autoRevokePermissionsMode,
boolean launchedForRestore, String installerPackage,
IPackageInstallObserver2 installObserver, int dataLoaderType) {
boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED;
final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null;
final String packageName = res.name;
final PackageSetting pkgSetting = succeeded ? getPackageSetting(packageName) : null;
if (succeeded && pkgSetting == null) {
Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "
+ "could be executed");
res.returnCode = INSTALL_FAILED_PACKAGE_CHANGED;
res.returnMsg = "Package was removed before install could complete.";
notifyInstallObserver(res, installObserver);
return;
}
if (succeeded) {
// Send the removed broadcasts
if (res.removedInfo != null) {
res.removedInfo.sendPackageRemovedBroadcasts(killApp);
}
// Whitelist any restricted permissions first as some may be runtime
// that the installer requested to be granted at install time.
if (whitelistedRestrictedPermissions != null
&& !whitelistedRestrictedPermissions.isEmpty()) {
mPermissionManager.setWhitelistedRestrictedPermissions(
res.pkg, res.newUsers, whitelistedRestrictedPermissions,
Process.myUid(), FLAG_PERMISSION_WHITELIST_INSTALLER);
}
if (autoRevokePermissionsMode == MODE_ALLOWED || autoRevokePermissionsMode == MODE_IGNORED) {
mPermissionManager.setAutoRevokeWhitelisted(res.pkg.getPackageName(),
autoRevokePermissionsMode == MODE_IGNORED, UserHandle.myUserId());
}
// Now that we successfully installed the package, grant runtime
// permissions if requested before broadcasting the install. Also
// for legacy apps in permission review mode we clear the permission
// review flag which is used to emulate runtime permissions for
// legacy apps.
if (grantPermissions) {
final int callingUid = Binder.getCallingUid();
mPermissionManager.grantRequestedRuntimePermissions(
res.pkg, res.newUsers, grantedPermissions, callingUid);
}
final String installerPackageName =
res.installerPackageName != null
? res.installerPackageName
: res.removedInfo != null
? res.removedInfo.installerPackageName
: null;
synchronized (mLock) {
mInstantAppRegistry.onPackageInstalledLPw(res.pkg, res.newUsers);
}
// Determine the set of users who are adding this package for
// the first time vs. those who are seeing an update.
int[] firstUserIds = EMPTY_INT_ARRAY;
int[] firstInstantUserIds = EMPTY_INT_ARRAY;
int[] updateUserIds = EMPTY_INT_ARRAY;
int[] instantUserIds = EMPTY_INT_ARRAY;
final boolean allNewUsers = res.origUsers == null || res.origUsers.length == 0;
final PackageSetting ps = pkgSetting;
for (int newUser : res.newUsers) {
final boolean isInstantApp = ps.getInstantApp(newUser);
if (allNewUsers) {
if (isInstantApp) {
firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
} else {
firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
}
continue;
}
boolean isNew = true;
for (int origUser : res.origUsers) {
if (origUser == newUser) {
isNew = false;
break;
}
}
if (isNew) {
if (isInstantApp) {
firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
} else {
firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
}
} else {
if (isInstantApp) {
instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser);
} else {
updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser);
}
}
}
// Send installed broadcasts if the package is not a static shared lib.
if (res.pkg.getStaticSharedLibName() == null) {
mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(
res.pkg.getBaseCodePath());
// Send added for users that see the package for the first time
// sendPackageAddedForNewUsers also deals with system apps
int appId = UserHandle.getAppId(res.uid);
boolean isSystem = res.pkg.isSystem();
sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,
virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds,
dataLoaderType);
// Send added for users that don't see the package for the first time
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, res.uid);
if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
// Send to all running apps.
final SparseArray<int[]> newBroadcastWhitelist;
synchronized (mLock) {
newBroadcastWhitelist = mAppsFilter.getVisibilityWhitelist(
getPackageSettingInternal(res.name, Process.SYSTEM_UID),
updateUserIds, mSettings.mPackages);
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
extras, 0 /*flags*/,
null /*targetPackage*/, null /*finishedReceiver*/,
updateUserIds, instantUserIds, newBroadcastWhitelist);
if (installerPackageName != null) {
// Send to the installer, even if it's not running.
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
extras, 0 /*flags*/,
installerPackageName, null /*finishedReceiver*/,
updateUserIds, instantUserIds, null /* broadcastWhitelist */);
}
// if the required verifier is defined, but, is not the installer of record
// for the package, it gets notified
final boolean notifyVerifier = mRequiredVerifierPackage != null
&& !mRequiredVerifierPackage.equals(installerPackageName);
if (notifyVerifier) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
extras, 0 /*flags*/,
mRequiredVerifierPackage, null /*finishedReceiver*/,
updateUserIds, instantUserIds, null /* broadcastWhitelist */);
}
// If package installer is defined, notify package installer about new
// app installed
if (mRequiredInstallerPackage != null) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
mRequiredInstallerPackage, null /*finishedReceiver*/,
firstUserIds, instantUserIds, null /* broadcastWhitelist */);
}
// Send replaced for users that don't see the package for the first time
if (update) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
packageName, extras, 0 /*flags*/,
null /*targetPackage*/, null /*finishedReceiver*/,
updateUserIds, instantUserIds, res.removedInfo.broadcastWhitelist);
if (installerPackageName != null) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
extras, 0 /*flags*/,
installerPackageName, null /*finishedReceiver*/,
updateUserIds, instantUserIds, null /*broadcastWhitelist*/);
}
if (notifyVerifier) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
extras, 0 /*flags*/,
mRequiredVerifierPackage, null /*finishedReceiver*/,
updateUserIds, instantUserIds, null /*broadcastWhitelist*/);
}
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null /*package*/, null /*extras*/, 0 /*flags*/,
packageName /*targetPackage*/,
null /*finishedReceiver*/, updateUserIds, instantUserIds,
null /*broadcastWhitelist*/);
} else if (launchedForRestore && !res.pkg.isSystem()) {
// First-install and we did a restore, so we're responsible for the
// first-launch broadcast.
if (DEBUG_BACKUP) {
Slog.i(TAG, "Post-restore of " + packageName
+ " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
}
sendFirstLaunchBroadcast(packageName, installerPackage,
firstUserIds, firstInstantUserIds);
}
// Send broadcast package appeared if external for all users
if (res.pkg.isExternalStorage()) {
if (!update) {
final StorageManager storage = mInjector.getStorageManager();
VolumeInfo volume =
storage.findVolumeByUuid(
res.pkg.getStorageUuid().toString());
int packageExternalStorageType =
getPackageExternalStorageType(volume, res.pkg.isExternalStorage());
// If the package was installed externally, log it.
if (packageExternalStorageType != StorageEnums.UNKNOWN) {
FrameworkStatsLog.write(
FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
packageExternalStorageType, packageName);
}
}
if (DEBUG_INSTALL) {
Slog.i(TAG, "upgrading pkg " + res.pkg + " is external");
}
final int[] uidArray = new int[]{res.pkg.getUid()};
ArrayList<String> pkgList = new ArrayList<>(1);
pkgList.add(packageName);
sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);
}
} else if (!ArrayUtils.isEmpty(res.libraryConsumers)) { // if static shared lib
int[] allUsers = mInjector.getUserManagerService().getUserIds();
for (int i = 0; i < res.libraryConsumers.size(); i++) {
AndroidPackage pkg = res.libraryConsumers.get(i);
// send broadcast that all consumers of the static shared library have changed
sendPackageChangedBroadcast(pkg.getPackageName(), false /* dontKillApp */,
new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
pkg.getUid(), null);
}
}
// Work that needs to happen on first install within each user
if (firstUserIds != null && firstUserIds.length > 0) {
for (int userId : firstUserIds) {
clearRolesAndRestorePermissionsForNewUserInstall(packageName,
pkgSetting.getInstallReason(userId), userId);
}
}
if (allNewUsers && !update) {
notifyPackageAdded(packageName, res.uid);
} else {
notifyPackageChanged(packageName, res.uid);
}
// Log current value of "unknown sources" setting
EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
getUnknownSourcesSettings());
// Remove the replaced package's older resources safely now
InstallArgs args = res.removedInfo != null ? res.removedInfo.args : null;
if (args != null) {
if (!killApp) {
// If we didn't kill the app, defer the deletion of code/resource files, since
// they may still be in use by the running application. This mitigates problems
// in cases where resources or code is loaded by a new Activity before
// ApplicationInfo changes have propagated to all application threads.
scheduleDeferredNoKillPostDelete(args);
} else {
synchronized (mInstallLock) {
args.doPostDeleteLI(true);
}
}
} else {
// Force a gc to clear up things. Ask for a background one, it's fine to go on
// and not block here.
VMRuntime.getRuntime().requestConcurrentGC();
}
// Notify DexManager that the package was installed for new users.
// The updated users should already be indexed and the package code paths
// should not change.
// Don't notify the manager for ephemeral apps as they are not expected to
// survive long enough to benefit of background optimizations.
for (int userId : firstUserIds) {
PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);
// There's a race currently where some install events may interleave with an
// uninstall. This can lead to package info being null (b/36642664).
if (info != null) {
mDexManager.notifyPackageInstalled(info, userId);
}
}
}
final boolean deferInstallObserver = succeeded && update && !killApp;
if (deferInstallObserver) {
scheduleDeferredNoKillInstallObserver(res, installObserver);
} else {
notifyInstallObserver(res, installObserver);
}
}
@Override
public void notifyPackagesReplacedReceived(String[] packages) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
for (String packageName : packages) {
final boolean filterApp;
synchronized (mLock) {
final PackageSetting ps = mSettings.getPackageLPr(packageName);
filterApp = shouldFilterApplicationLocked(ps, callingUid, callingUserId);
}
if (!filterApp) {
notifyInstallObserver(packageName);
}
}
}
private void notifyInstallObserver(String packageName) {
Pair<PackageInstalledInfo, IPackageInstallObserver2> pair =
mNoKillInstallObservers.remove(packageName);
if (pair != null) {
notifyInstallObserver(pair.first, pair.second);
}
}
private void notifyInstallObserver(PackageInstalledInfo info,
IPackageInstallObserver2 installObserver) {
if (installObserver != null) {
try {
Bundle extras = extrasForInstallResult(info);
installObserver.onPackageInstalled(info.name, info.returnCode,
info.returnMsg, extras);
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
}
}
private void scheduleDeferredNoKillPostDelete(InstallArgs args) {
Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args);
mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_POST_DELETE_DELAY_MS);
}
private void scheduleDeferredNoKillInstallObserver(PackageInstalledInfo info,
IPackageInstallObserver2 observer) {
String packageName = info.pkg.getPackageName();
mNoKillInstallObservers.put(packageName, Pair.create(info, observer));
Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_INSTALL_OBSERVER, packageName);
mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS);
}
/**
* Gets the type of the external storage a package is installed on.
* @param packageVolume The storage volume of the package.
* @param packageIsExternal true if the package is currently installed on
* external/removable/unprotected storage.
* @return {@link StorageEnums#UNKNOWN} if the package is not stored externally or the
* corresponding {@link StorageEnums} storage type value if it is.
* corresponding {@link StorageEnums} storage type value if it is.
*/
private static int getPackageExternalStorageType(VolumeInfo packageVolume,
boolean packageIsExternal) {
if (packageVolume != null) {
DiskInfo disk = packageVolume.getDisk();
if (disk != null) {
if (disk.isSd()) {
return StorageEnums.SD_CARD;
}
if (disk.isUsb()) {
return StorageEnums.USB;
}
if (packageIsExternal) {
return StorageEnums.OTHER;
}
}
}
return StorageEnums.UNKNOWN;
}
private StorageEventListener mStorageListener = new StorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
if (vol.type == VolumeInfo.TYPE_PRIVATE) {
if (vol.state == VolumeInfo.STATE_MOUNTED) {
final String volumeUuid = vol.getFsUuid();
// Clean up any users or apps that were removed or recreated
// while this volume was missing
mUserManager.reconcileUsers(volumeUuid);
reconcileApps(volumeUuid);
// Clean up any install sessions that expired or were
// cancelled while this volume was missing
mInstallerService.onPrivateVolumeMounted(volumeUuid);
loadPrivatePackages(vol);
} else if (vol.state == VolumeInfo.STATE_EJECTING) {
unloadPrivatePackages(vol);
}
}
}
@Override
public void onVolumeForgotten(String fsUuid) {
if (TextUtils.isEmpty(fsUuid)) {
Slog.e(TAG, "Forgetting internal storage is probably a mistake; ignoring");
return;
}
// Remove any apps installed on the forgotten volume
synchronized (mLock) {
final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(fsUuid);
for (PackageSetting ps : packages) {
Slog.d(TAG, "Destroying " + ps.name + " because volume was forgotten");
deletePackageVersioned(new VersionedPackage(ps.name,
PackageManager.VERSION_CODE_HIGHEST),
new LegacyPackageDeleteObserver(null).getBinder(),
UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS);
// Try very hard to release any references to this package
// so we don't risk the system server being killed due to
// open FDs
AttributeCache.instance().removePackage(ps.name);
}
mSettings.onVolumeForgotten(fsUuid);
mSettings.writeLPr();
}
}
};
Bundle extrasForInstallResult(PackageInstalledInfo res) {
Bundle extras = null;
switch (res.returnCode) {
case PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION: {
extras = new Bundle();
extras.putString(PackageManager.EXTRA_FAILURE_EXISTING_PERMISSION,
res.origPermission);
extras.putString(PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE,
res.origPackage);
break;
}
case PackageManager.INSTALL_SUCCEEDED: {
extras = new Bundle();
extras.putBoolean(Intent.EXTRA_REPLACING,
res.removedInfo != null && res.removedInfo.removedPackage != null);
break;
}
}
return extras;
}
void scheduleWriteSettingsLocked() {
// We normally invalidate when we write settings, but in cases where we delay and
// coalesce settings writes, this strategy would have us invalidate the cache too late.
// Invalidating on schedule addresses this problem.
PackageManager.invalidatePackageInfoCache();
if (!mHandler.hasMessages(WRITE_SETTINGS)) {
mHandler.sendEmptyMessageDelayed(WRITE_SETTINGS, WRITE_SETTINGS_DELAY);
}
}
void scheduleWritePackageListLocked(int userId) {
PackageManager.invalidatePackageInfoCache();
if (!mHandler.hasMessages(WRITE_PACKAGE_LIST)) {
Message msg = mHandler.obtainMessage(WRITE_PACKAGE_LIST);
msg.arg1 = userId;
mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY);
}
}
void scheduleWritePackageRestrictionsLocked(UserHandle user) {
final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
scheduleWritePackageRestrictionsLocked(userId);
}
void scheduleWritePackageRestrictionsLocked(int userId) {
PackageManager.invalidatePackageInfoCache();
final int[] userIds = (userId == UserHandle.USER_ALL)
? mUserManager.getUserIds() : new int[]{userId};
for (int nextUserId : userIds) {
if (!mUserManager.exists(nextUserId)) return;
mDirtyUsers.add(nextUserId);
if (!mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
mHandler.sendEmptyMessageDelayed(WRITE_PACKAGE_RESTRICTIONS, WRITE_SETTINGS_DELAY);
}
}
}
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// Self-check for initial settings.
PackageManagerServiceCompilerMapping.checkProperties();
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
Trace.TRACE_TAG_PACKAGE_MANAGER);
t.traceBegin("create package manager");
final Object lock = new Object();
final Object installLock = new Object();
Injector injector = new Injector(
context, lock, installer, installLock, new PackageAbiHelperImpl(),
(i, pm) ->
new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock),
(i, pm) ->
PermissionManagerService.create(context, lock),
(i, pm) ->
new UserManagerService(context, pm,
new UserDataPreparer(installer, installLock, context, onlyCore),
lock),
(i, pm) ->
new Settings(Environment.getDataDirectory(),
i.getPermissionManagerServiceInternal().getPermissionSettings(),
lock),
new Injector.LocalServicesProducer<>(ActivityTaskManagerInternal.class),
new Injector.LocalServicesProducer<>(ActivityManagerInternal.class),
new Injector.LocalServicesProducer<>(DeviceIdleInternal.class),
new Injector.LocalServicesProducer<>(StorageManagerInternal.class),
new Injector.LocalServicesProducer<>(NetworkPolicyManagerInternal.class),
new Injector.LocalServicesProducer<>(PermissionPolicyInternal.class),
new Injector.LocalServicesProducer<>(DeviceStorageMonitorInternal.class),
new Injector.SystemServiceProducer<>(DisplayManager.class),
new Injector.SystemServiceProducer<>(StorageManager.class),
new Injector.SystemServiceProducer<>(AppOpsManager.class),
(i, pm) -> AppsFilter.create(pm.mPmInternal, i),
(i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"));
PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest);
t.traceEnd(); // "create package manager"
injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_LATEST_CHANGES,
packageName -> {
synchronized (m.mInstallLock) {
final AndroidPackage pkg;
final PackageSetting ps;
final SharedUserSetting sharedUser;
final String oldSeInfo;
synchronized (m.mLock) {
ps = m.mSettings.getPackageLPr(packageName);
if (ps == null) {
Slog.e(TAG, "Failed to find package setting " + packageName);
return;
}
pkg = ps.pkg;
sharedUser = ps.getSharedUser();
oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
}
if (pkg == null) {
Slog.e(TAG, "Failed to find package " + packageName);
return;
}
final String newSeInfo = SELinuxMMAC.getSeInfo(pkg, sharedUser,
m.mInjector.getCompatibility());
if (!newSeInfo.equals(oldSeInfo)) {
Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "
+ oldSeInfo + " to: " + newSeInfo);
ps.getPkgState().setOverrideSeInfo(newSeInfo);
m.prepareAppDataAfterInstallLIF(pkg);
}
}
});
m.installWhitelistedSystemPackages();
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
/** Install/uninstall system packages for all users based on their user-type, as applicable. */
private void installWhitelistedSystemPackages() {
synchronized (mLock) {
final boolean scheduleWrite = mUserManager.installWhitelistedSystemPackages(
isFirstBoot(), isDeviceUpgrading(), mExistingPackages);
if (scheduleWrite) {
scheduleWritePackageRestrictionsLocked(UserHandle.USER_ALL);
scheduleWriteSettingsLocked();
}
}
}
private static void getDefaultDisplayMetrics(
DisplayManager displayManager, DisplayMetrics metrics) {
displayManager.getDisplay(Display.DEFAULT_DISPLAY).getMetrics(metrics);
}
/**
* Requests that files preopted on a secondary system partition be copied to the data partition
* if possible. Note that the actual copying of the files is accomplished by init for security
* reasons. This simply requests that the copy takes place and awaits confirmation of its
* completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy.
*/
private static void requestCopyPreoptedFiles() {
final int WAIT_TIME_MS = 100;
final String CP_PREOPT_PROPERTY = "sys.cppreopt";
if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
SystemProperties.set(CP_PREOPT_PROPERTY, "requested");
// We will wait for up to 100 seconds.
final long timeStart = SystemClock.uptimeMillis();
final long timeEnd = timeStart + 100 * 1000;
long timeNow = timeStart;
while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) {
try {
Thread.sleep(WAIT_TIME_MS);
} catch (InterruptedException e) {
// Do nothing
}
timeNow = SystemClock.uptimeMillis();
if (timeNow > timeEnd) {
SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out");
Slog.wtf(TAG, "cppreopt did not finish!");
break;
}
}
Slog.i(TAG, "cppreopts took " + (timeNow - timeStart) + " ms");
}
}
@VisibleForTesting
public static class ScanPartition extends SystemPartition {
@ScanFlags
public final int scanFlag;
public ScanPartition(@NonNull SystemPartition partition) {
super(partition);
scanFlag = scanFlagForPartition(partition);
}
/**
* Creates a partition containing the same folders as the original partition but with a
* different root folder. The new partition will include the scan flags of the original
* partition along with any specified additional scan flags.
*/
public ScanPartition(@NonNull File folder, @NonNull ScanPartition original,
@ScanFlags int additionalScanFlag) {
super(folder, original);
this.scanFlag = original.scanFlag | additionalScanFlag;
}
private static int scanFlagForPartition(PackagePartitions.SystemPartition partition) {
switch (partition.type) {
case PackagePartitions.PARTITION_SYSTEM:
return 0;
case PackagePartitions.PARTITION_VENDOR:
return SCAN_AS_VENDOR;
case PackagePartitions.PARTITION_ODM:
return SCAN_AS_ODM;
case PackagePartitions.PARTITION_OEM:
return SCAN_AS_OEM;
case PackagePartitions.PARTITION_PRODUCT:
return SCAN_AS_PRODUCT;
case PackagePartitions.PARTITION_SYSTEM_EXT:
return SCAN_AS_SYSTEM_EXT;
default:
throw new IllegalStateException("Unable to determine scan flag for "
+ partition.getFolder());
}
}
@Override
public String toString() {
return getFolder().getAbsolutePath() + ":" + scanFlag;
}
}
/**
* A extremely minimal constructor designed to start up a PackageManagerService instance for
* testing.
*
* It is assumed that all methods under test will mock the internal fields and thus
* none of the initialization is needed.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public PackageManagerService(@NonNull Injector injector, @NonNull TestParams testParams) {
mInjector = injector;
mInjector.bootstrap(this);
mAppsFilter = injector.getAppsFilter();
mComponentResolver = injector.getComponentResolver();
mContext = injector.getContext();
mInstaller = injector.getInstaller();
mInstallLock = injector.getInstallLock();
mLock = injector.getLock();
mPermissionManager = injector.getPermissionManagerServiceInternal();
mSettings = injector.getSettings();
mUserManager = injector.getUserManagerService();
mApexManager = testParams.apexManager;
mArtManagerService = testParams.artManagerService;
mAvailableFeatures = testParams.availableFeatures;
mDefParseFlags = testParams.defParseFlags;
mDexManager = testParams.dexManager;
mDirsToScanAsSystem = testParams.dirsToScanAsSystem;
mFactoryTest = testParams.factoryTest;
mHandler = testParams.handler;
mHandlerThread = testParams.handlerThread;
mIncrementalManager = testParams.incrementalManager;
mInstallerService = testParams.installerService;
mInstantAppRegistry = testParams.instantAppRegistry;
mInstantAppResolverConnection = testParams.instantAppResolverConnection;
mInstantAppResolverSettingsComponent = testParams.instantAppResolverSettingsComponent;
mIntentFilterVerifier = testParams.intentFilterVerifier;
mIntentFilterVerifierComponent = testParams.intentFilterVerifierComponent;
mIsPreNMR1Upgrade = testParams.isPreNmr1Upgrade;
mIsPreNUpgrade = testParams.isPreNupgrade;
mIsPreQUpgrade = testParams.isPreQupgrade;
mIsUpgrade = testParams.isUpgrade;
mMetrics = testParams.Metrics;
mModuleInfoProvider = testParams.moduleInfoProvider;
mMoveCallbacks = testParams.moveCallbacks;
mOnlyCore = testParams.onlyCore;
mOverlayConfig = testParams.overlayConfig;
mPackageDexOptimizer = testParams.packageDexOptimizer;
mPackageParserCallback = testParams.packageParserCallback;
mPendingBroadcasts = testParams.pendingPackageBroadcasts;
mPermissionManagerService = testParams.permissionManagerService;
mPmInternal = testParams.pmInternal;
mProcessLoggingHandler = testParams.processLoggingHandler;
mProtectedPackages = testParams.protectedPackages;
mSeparateProcesses = testParams.separateProcesses;
mViewCompiler = testParams.viewCompiler;
mRequiredVerifierPackage = testParams.requiredVerifierPackage;
mRequiredInstallerPackage = testParams.requiredInstallerPackage;
mRequiredUninstallerPackage = testParams.requiredUninstallerPackage;
mRequiredPermissionControllerPackage = testParams.requiredPermissionControllerPackage;
mSetupWizardPackage = testParams.setupWizardPackage;
mStorageManagerPackage = testParams.storageManagerPackage;
mDefaultTextClassifierPackage = testParams.defaultTextClassifierPackage;
mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage;
mWellbeingPackage = testParams.wellbeingPackage;
mRetailDemoPackage = testParams.retailDemoPackage;
mDocumenterPackage = testParams.documenterPackage;
mConfiguratorPackage = testParams.configuratorPackage;
mAppPredictionServicePackage = testParams.appPredictionServicePackage;
mIncidentReportApproverPackage = testParams.incidentReportApproverPackage;
mServicesExtensionPackageName = testParams.servicesExtensionPackageName;
mSharedSystemSharedLibraryPackageName = testParams.sharedSystemSharedLibraryPackageName;
mResolveComponentName = testParams.resolveComponentName;
mPackages.putAll(testParams.packages);
}
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
PackageManager.disableApplicationInfoCache();
PackageManager.disablePackageInfoCache();
// Avoid invalidation-thrashing by preventing cache invalidations from causing property
// writes if the cache isn't enabled yet. We re-enable writes later when we're
// done initializing.
PackageManager.corkPackageInfoCache();
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
Trace.TRACE_TAG_PACKAGE_MANAGER);
mPendingBroadcasts = new PendingPackageBroadcasts();
mInjector = injector;
mInjector.bootstrap(this);
mLock = injector.getLock();
mInstallLock = injector.getInstallLock();
LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES);
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
SystemClock.uptimeMillis());
if (mSdkVersion <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
}
mContext = injector.getContext();
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
mMetrics = new DisplayMetrics();
mInstaller = injector.getInstaller();
// Create sub-components that provide services / data. Order here is important.
t.traceBegin("createSubComponents");
// Expose private service for system components to use.
mPmInternal = new PackageManagerInternalImpl();
LocalServices.addService(PackageManagerInternal.class, mPmInternal);
mUserManager = injector.getUserManagerService();
mComponentResolver = injector.getComponentResolver();
mPermissionManager = injector.getPermissionManagerServiceInternal();
mSettings = injector.getSettings();
mPermissionManagerService = (IPermissionManager) ServiceManager.getService("permissionmgr");
mIncrementalManager =
(IncrementalManager) mContext.getSystemService(Context.INCREMENTAL_SERVICE);
PlatformCompat platformCompat = mInjector.getCompatibility();
mPackageParserCallback = new PackageParser2.Callback() {
@Override
public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) {
return platformCompat.isChangeEnabled(changeId, appInfo);
}
@Override
public boolean hasFeature(String feature) {
return PackageManagerService.this.hasSystemFeature(feature, 0);
}
};
// CHECKSTYLE:ON IndentationCheck
t.traceEnd();
t.traceBegin("addSharedUsers");
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.se", SE_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
t.traceEnd();
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
if ("*".equals(separateProcesses)) {
mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
mSeparateProcesses = null;
Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");
} else {
mDefParseFlags = 0;
mSeparateProcesses = separateProcesses.split(",");
Slog.w(TAG, "Running with debug.separate_processes: "
+ separateProcesses);
}
} else {
mDefParseFlags = 0;
mSeparateProcesses = null;
}
mPackageDexOptimizer = new PackageDexOptimizer(mInstaller, mInstallLock, mContext,
"*dexopt*");
mDexManager =
new DexManager(mContext, this, mPackageDexOptimizer, mInstaller, mInstallLock);
mArtManagerService = new ArtManagerService(mContext, this, mInstaller, mInstallLock);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mViewCompiler = new ViewCompiler(mInstallLock, mInstaller);
getDefaultDisplayMetrics(mInjector.getDisplayManager(), mMetrics);
t.traceBegin("get system config");
SystemConfig systemConfig = SystemConfig.getInstance();
mAvailableFeatures = systemConfig.getAvailableFeatures();
ApplicationPackageManager.invalidateHasSystemFeatureCache();
t.traceEnd();
mProtectedPackages = new ProtectedPackages(mContext);
mApexManager = ApexManager.getInstance();
mAppsFilter = mInjector.getAppsFilter();
final List<ScanPartition> scanPartitions = new ArrayList<>();
final List<ApexManager.ActiveApexInfo> activeApexInfos = mApexManager.getActiveApexInfos();
for (int i = 0; i < activeApexInfos.size(); i++) {
final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i));
if (scanPartition != null) {
scanPartitions.add(scanPartition);
}
}
mDirsToScanAsSystem = new ArrayList<>();
mDirsToScanAsSystem.addAll(SYSTEM_PARTITIONS);
mDirsToScanAsSystem.addAll(scanPartitions);
Slog.d(TAG, "Directories scanned as system partitions: " + mDirsToScanAsSystem);
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
synchronized (mLock) {
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
mProcessLoggingHandler = new ProcessLoggingHandler();
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
mInstantAppRegistry = new InstantAppRegistry(this);
ArrayMap<String, SystemConfig.SharedLibraryEntry> libConfig
= systemConfig.getSharedLibraries();
final int builtInLibCount = libConfig.size();
for (int i = 0; i < builtInLibCount; i++) {
String name = libConfig.keyAt(i);
SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
addBuiltInSharedLibraryLocked(entry.filename, name);
}
// Now that we have added all the libraries, iterate again to add dependency
// information IFF their dependencies are added.
long undefinedVersion = SharedLibraryInfo.VERSION_UNDEFINED;
for (int i = 0; i < builtInLibCount; i++) {
String name = libConfig.keyAt(i);
SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
final int dependencyCount = entry.dependencies.length;
for (int j = 0; j < dependencyCount; j++) {
final SharedLibraryInfo dependency =
getSharedLibraryInfoLPr(entry.dependencies[j], undefinedVersion);
if (dependency != null) {
getSharedLibraryInfoLPr(name, undefinedVersion).addDependency(dependency);
}
}
}
SELinuxMMAC.readInstallPolicy();
t.traceBegin("loadFallbacks");
FallbackCategoryProvider.loadFallbacks();
t.traceEnd();
t.traceBegin("read user settings");
mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers(
/* excludePartial= */ true,
/* excludeDying= */ false,
/* excludePreCreated= */ false));
t.traceEnd();
// Clean up orphaned packages for which the code path doesn't exist
// and they are an update to a system app - caused by bug/32321269
final int packageSettingCount = mSettings.mPackages.size();
for (int i = packageSettingCount - 1; i >= 0; i--) {
PackageSetting ps = mSettings.mPackages.valueAt(i);
if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists())
&& mSettings.getDisabledSystemPkgLPr(ps.name) != null) {
mSettings.mPackages.removeAt(i);
mSettings.enableSystemPackageLPw(ps.name);
}
}
if (!mOnlyCore && mFirstBoot) {
requestCopyPreoptedFiles();
}
String customResolverActivityName = Resources.getSystem().getString(
R.string.config_customResolverActivity);
if (!TextUtils.isEmpty(customResolverActivityName)) {
mCustomResolverComponentName = ComponentName.unflattenFromString(
customResolverActivityName);
}
long startTime = SystemClock.uptimeMillis();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
startTime);
final String bootClassPath = System.getenv("BOOTCLASSPATH");
final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
if (bootClassPath == null) {
Slog.w(TAG, "No BOOTCLASSPATH found!");
}
if (systemServerClassPath == null) {
Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
}
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
final VersionInfo ver = mSettings.getInternalVersion();
mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
if (mIsUpgrade) {
logCriticalInfo(Log.INFO,
"Upgrading from " + ver.fingerprint + " to " + Build.FINGERPRINT);
}
// when upgrading from pre-M, promote system app permissions from install to runtime
mPromoteSystemApps =
mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
// When upgrading from pre-N, we need to handle package extraction like first boot,
// as there is no profiling data available.
mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;
mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q;
// Save the names of pre-existing packages prior to scanning, so we can determine
// which system packages are completely new due to an upgrade.
if (isDeviceUpgrading()) {
mExistingPackages = new ArraySet<>(mSettings.mPackages.size());
for (PackageSetting ps : mSettings.mPackages.values()) {
mExistingPackages.add(ps.name);
}
}
mCacheDir = preparePackageParserCache();
// Set flag to monitor and not change apk file paths when
// scanning install directories.
int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
if (mIsUpgrade || mFirstBoot) {
scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
}
final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR;
final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
PackageParser2 packageParser = new PackageParser2(mSeparateProcesses, mOnlyCore,
mMetrics, mCacheDir, mPackageParserCallback);
ExecutorService executorService = ParallelPackageParser.makeExecutorService();
// Prepare apex package info before scanning APKs, these information are needed when
// scanning apk in apex.
mApexManager.scanApexPackagesTraced(packageParser, executorService);
// Collect vendor/product/system_ext overlay packages. (Do this before scanning
// any apps.)
// For security and version matching reason, only consider overlay packages if they
// reside in the right directory.
for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getOverlayFolder() == null) {
continue;
}
scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,
systemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
scanDirTracedLI(frameworkDir, systemParseFlags,
systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
packageParser, executorService);
if (!mPackages.containsKey("android")) {
throw new IllegalStateException(
"Failed to load frameworks package; check log for warnings");
}
for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getPrivAppFolder() != null) {
scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
packageParser, executorService);
}
scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
systemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
// Parse overlay configuration files to set default enable state, mutability, and
// priority of system overlays.
mOverlayConfig = OverlayConfig.initializeSystemInstance(
consumer -> mPmInternal.forEachPackage(
pkg -> consumer.accept(pkg, pkg.isSystem())));
// Prune any system packages that no longer exist.
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
// Stub packages must either be replaced with full versions in the /data
// partition or be disabled.
final List<String> stubSystemApps = new ArrayList<>();
if (!mOnlyCore) {
// do this first before mucking with mPackages for the "expecting better" case
final Iterator<AndroidPackage> pkgIterator = mPackages.values().iterator();
while (pkgIterator.hasNext()) {
final AndroidPackage pkg = pkgIterator.next();
if (pkg.isStub()) {
stubSystemApps.add(pkg.getPackageName());
}
}
final Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
/*
* If this is not a system app, it can't be a
* disable system app.
*/
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
continue;
}
/*
* If the package is scanned, it's not erased.
*/
final AndroidPackage scannedPkg = mPackages.get(ps.name);
if (scannedPkg != null) {
/*
* If the system app is both scanned and in the
* disabled packages list, then it must have been
* added via OTA. Remove it from the currently
* scanned package so the previously user-installed
* application can be scanned.
*/
if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
logCriticalInfo(Log.WARN,
"Expecting better updated system app for " + ps.name
+ "; removing system app. Last known"
+ " codePath=" + ps.codePathString
+ ", versionCode=" + ps.versionCode
+ "; scanned versionCode=" + scannedPkg.getLongVersionCode());
removePackageLI(scannedPkg, true);
mExpectingBetter.put(ps.name, ps.codePath);
}
continue;
}
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
psit.remove();
logCriticalInfo(Log.WARN, "System package " + ps.name
+ " no longer exists; it's data will be wiped");
// Assume package is truly gone and wipe residual permissions.
mPermissionManager.updatePermissions(ps.name, null);
// Actual deletion of code and data will be handled by later
// reconciliation step
} else {
// we still have a disabled system package, but, it still might have
// been removed. check the code path still exists and check there's
// still a package. the latter can happen if an OTA keeps the same
// code path, but, changes the package name.
final PackageSetting disabledPs =
mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.codePath == null || !disabledPs.codePath.exists()
|| disabledPs.pkg == null) {
possiblyDeletedUpdatedSystemApps.add(ps.name);
} else {
// We're expecting that the system app should remain disabled, but add
// it to expecting better to recover in case the data version cannot
// be scanned.
mExpectingBetter.put(disabledPs.name, disabledPs.codePath);
}
}
}
}
final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
// Remove any shared userIDs that have no associated packages
mSettings.pruneSharedUsersLPw();
final long systemScanTime = SystemClock.uptimeMillis() - startTime;
final int systemPackagesCount = mPackages.size();
Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
+ " ms, packageCount: " + systemPackagesCount
+ " , timePerPackage: "
+ (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
+ " , cached: " + cachedSystemApps);
if (mIsUpgrade && systemPackagesCount > 0) {
//CHECKSTYLE:OFF IndentationCheck
FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
systemScanTime / systemPackagesCount);
//CHECKSTYLE:ON IndentationCheck
}
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
packageParser, executorService);
}
packageParser.close();
List<Runnable> unfinishedTasks = executorService.shutdownNow();
if (!unfinishedTasks.isEmpty()) {
throw new IllegalStateException("Not all tasks finished before calling close: "
+ unfinishedTasks);
}
if (!mOnlyCore) {
// Remove disable package settings for updated system apps that were
// removed via an OTA. If the update is no longer present, remove the
// app completely. Otherwise, revoke their system privileges.
for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
final AndroidPackage pkg = mPackages.get(packageName);
final String msg;
// remove from the disabled system list; do this first so any future
// scans of this package are performed without this state
mSettings.removeDisabledSystemPackageLPw(packageName);
if (pkg == null) {
// should have found an update, but, we didn't; remove everything
msg = "Updated system package " + packageName
+ " no longer exists; removing its data";
// Actual deletion of code and data will be handled by later
// reconciliation step
} else {
// found an update; revoke system privileges
msg = "Updated system package " + packageName
+ " no longer exists; rescanning package on data";
// NOTE: We don't do anything special if a stub is removed from the
// system image. But, if we were [like removing the uncompressed
// version from the /data partition], this is where it'd be done.
// remove the package from the system and re-scan it without any
// special privileges
removePackageLI(pkg, true);
try {
final File codePath = new File(pkg.getCodePath());
scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse updated, ex-system package: "
+ e.getMessage());
}
}
// one final check. if we still have a package setting [ie. it was
// previously scanned and known to the system], but, we don't have
// a package [ie. there was an error scanning it from the /data
// partition], completely remove the package data.
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null && mPackages.get(packageName) == null) {
removePackageDataLIF(ps, null, null, 0, false);
}
logCriticalInfo(Log.WARN, msg);
}
/*
* Make sure all system apps that we expected to appear on
* the userdata partition actually showed up. If they never
* appeared, crawl back and revive the system version.
*/
for (int i = 0; i < mExpectingBetter.size(); i++) {
final String packageName = mExpectingBetter.keyAt(i);
if (!mPackages.containsKey(packageName)) {
final File scanFile = mExpectingBetter.valueAt(i);
logCriticalInfo(Log.WARN, "Expected better " + packageName
+ " but never showed up; reverting to system");
@ParseFlags int reparseFlags = 0;
@ScanFlags int rescanFlags = 0;
for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
final ScanPartition partition = mDirsToScanAsSystem.get(i1);
if (partition.containsPrivApp(scanFile)) {
reparseFlags = systemParseFlags;
rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
| partition.scanFlag;
break;
}
if (partition.containsApp(scanFile)) {
reparseFlags = systemParseFlags;
rescanFlags = systemScanFlags | partition.scanFlag;
break;
}
}
if (rescanFlags == 0) {
Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
continue;
}
mSettings.enableSystemPackageLPw(packageName);
try {
scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse original system package: "
+ e.getMessage());
}
}
}
// Uncompress and install any stubbed system applications.
// This must be done last to ensure all stubs are replaced or disabled.
installSystemStubPackages(stubSystemApps, scanFlags);
final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
- cachedSystemApps;
final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
final int dataPackagesCount = mPackages.size() - systemPackagesCount;
Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
+ " ms, packageCount: " + dataPackagesCount
+ " , timePerPackage: "
+ (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
+ " , cached: " + cachedNonSystemApps);
if (mIsUpgrade && dataPackagesCount > 0) {
//CHECKSTYLE:OFF IndentationCheck
FrameworkStatsLog.write(
FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
dataScanTime / dataPackagesCount);
//CHECKSTYLE:OFF IndentationCheck
}
}
mExpectingBetter.clear();
// Resolve the storage manager.
mStorageManagerPackage = getStorageManagerPackageName();
// Resolve protected action filters. Only the setup wizard is allowed to
// have a high priority filter for these actions.
mSetupWizardPackage = getSetupWizardPackageNameImpl();
mComponentResolver.fixProtectedFilterPriorities();
mDefaultTextClassifierPackage = getDefaultTextClassifierPackageName();
mSystemTextClassifierPackageName = getSystemTextClassifierPackageName();
mWellbeingPackage = getWellbeingPackageName();
mDocumenterPackage = getDocumenterPackageName();
mConfiguratorPackage = getDeviceConfiguratorPackageName();
mAppPredictionServicePackage = getAppPredictionServicePackageName();
mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
mRetailDemoPackage = getRetailDemoPackageName();
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
updateAllSharedLibrariesLocked(null, null, Collections.unmodifiableMap(mPackages));
for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
// NOTE: We ignore potential failures here during a system scan (like
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
final List<String> changedAbiCodePath =
applyAdjustedAbiToSharedUser(setting, null /*scannedPackage*/,
mInjector.getAbiHelper().getAdjustedAbiForSharedUser(
setting.packages, null /*scannedPackage*/));
if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
final String codePathString = changedAbiCodePath.get(i);
try {
mInstaller.rmdex(codePathString,
getDexCodeInstructionSet(getPreferredInstructionSet()));
} catch (InstallerException ignored) {
}
}
}
// Adjust seInfo to ensure apps which share a sharedUserId are placed in the same
// SELinux domain.
setting.fixSeInfoLocked();
setting.updateProcesses();
}
// Now that we know all the packages we are keeping,
// read and update their last usage times.
mPackageUsage.read(mSettings.mPackages);
mCompilerStats.read();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
Slog.i(TAG, "Time to scan packages: "
+ ((SystemClock.uptimeMillis()-startTime)/1000f)
+ " seconds");
// If the platform SDK has changed since the last time we booted,
// we need to re-grant app permission to catch any new ones that
// appear. This is really a hack, and means that apps can in some
// cases get permissions that the user didn't initially explicitly
// allow... it would be nice to have some better way to handle
// this situation.
final boolean sdkUpdated = (ver.sdkVersion != mSdkVersion);
if (sdkUpdated) {
Slog.i(TAG, "Platform changed from " + ver.sdkVersion + " to "
+ mSdkVersion + "; regranting permissions for internal storage");
}
mPermissionManager.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated);
ver.sdkVersion = mSdkVersion;
// If this is the first boot or an update from pre-M, and it is a normal
// boot, then we need to initialize the default preferred apps across
// all defined users.
if (!mOnlyCore && (mPromoteSystemApps || mFirstBoot)) {
for (UserInfo user : mInjector.getUserManagerInternal().getUsers(true)) {
mSettings.applyDefaultPreferredAppsLPw(user.id);
primeDomainVerificationsLPw(user.id);
}
}
// Prepare storage for system user really early during boot,
// since core system apps like SettingsProvider and SystemUI
// can't wait for user to start
final int storageFlags;
if (StorageManager.isFileEncryptedNativeOrEmulated()) {
storageFlags = StorageManager.FLAG_STORAGE_DE;
} else {
storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
}
List<String> deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL,
UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */,
true /* onlyCoreApps */);
mPrepareAppDataFuture = SystemServerInitThreadPool.submit(() -> {
TimingsTraceLog traceLog = new TimingsTraceLog("SystemServerTimingAsync",
Trace.TRACE_TAG_PACKAGE_MANAGER);
traceLog.traceBegin("AppDataFixup");
try {
mInstaller.fixupAppData(StorageManager.UUID_PRIVATE_INTERNAL,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
} catch (InstallerException e) {
Slog.w(TAG, "Trouble fixing GIDs", e);
}
traceLog.traceEnd();
traceLog.traceBegin("AppDataPrepare");
if (deferPackages == null || deferPackages.isEmpty()) {
return;
}
int count = 0;
for (String pkgName : deferPackages) {
AndroidPackage pkg = null;
synchronized (mLock) {
PackageSetting ps = mSettings.getPackageLPr(pkgName);
if (ps != null && ps.getInstalled(UserHandle.USER_SYSTEM)) {
pkg = ps.pkg;
}
}
if (pkg != null) {
synchronized (mInstallLock) {
prepareAppDataAndMigrateLIF(pkg, UserHandle.USER_SYSTEM, storageFlags,
true /* maybeMigrateAppData */);
}
count++;
}
}
traceLog.traceEnd();
Slog.i(TAG, "Deferred reconcileAppsData finished " + count + " packages");
}, "prepareAppData");
// If this is first boot after an OTA, and a normal boot, then
// we need to clear code cache directories.
// Note that we do *not* clear the application profiles. These remain valid
// across OTAs and are used to drive profile verification (post OTA) and
// profile compilation (without waiting to collect a fresh set of profiles).
if (mIsUpgrade && !mOnlyCore) {
Slog.i(TAG, "Build fingerprint changed; clearing code caches");
for (int i = 0; i < mSettings.mPackages.size(); i++) {
final PackageSetting ps = mSettings.mPackages.valueAt(i);
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
// No apps are running this early, so no need to freeze
clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY
| Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
}
}
ver.fingerprint = Build.FINGERPRINT;
}
// Grandfather existing (installed before Q) non-system apps to hide
// their icons in launcher.
if (!mOnlyCore && mIsPreQUpgrade) {
Slog.i(TAG, "Whitelisting all existing apps to hide their icons");
int size = mSettings.mPackages.size();
for (int i = 0; i < size; i++) {
final PackageSetting ps = mSettings.mPackages.valueAt(i);
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
continue;
}
ps.disableComponentLPw(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME,
UserHandle.USER_SYSTEM);
}
}
// clear only after permissions and other defaults have been updated
mPromoteSystemApps = false;
// All the changes are done during package scanning.
ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;
// can downgrade to reader
t.traceBegin("write settings");
mSettings.writeLPr();
t.traceEnd();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
if (!mOnlyCore) {
mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr();
mRequiredInstallerPackage = getRequiredInstallerLPr();
mRequiredUninstallerPackage = getRequiredUninstallerLPr();
mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
if (mIntentFilterVerifierComponent != null) {
mIntentFilterVerifier = new IntentVerifierProxy(mContext,
mIntentFilterVerifierComponent);
} else {
mIntentFilterVerifier = null;
}
mServicesExtensionPackageName = getRequiredServicesExtensionPackageLPr();
mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
PackageManager.SYSTEM_SHARED_LIBRARY_SHARED,
SharedLibraryInfo.VERSION_UNDEFINED);
} else {
mRequiredVerifierPackage = null;
mRequiredInstallerPackage = null;
mRequiredUninstallerPackage = null;
mIntentFilterVerifierComponent = null;
mIntentFilterVerifier = null;
mServicesExtensionPackageName = null;
mSharedSystemSharedLibraryPackageName = null;
}
// PermissionController hosts default permission granting and role management, so it's a
// critical part of the core system.
mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr();
mSettings.setPermissionControllerVersion(
getPackageInfo(mRequiredPermissionControllerPackage, 0,
UserHandle.USER_SYSTEM).getLongVersionCode());
// Initialize InstantAppRegistry's Instant App list for all users.
final int[] userIds = UserManagerService.getInstance().getUserIds();
for (AndroidPackage pkg : mPackages.values()) {
if (pkg.isSystem()) {
continue;
}
for (int userId : userIds) {
final PackageSetting ps = getPackageSetting(pkg.getPackageName());
if (ps == null || !ps.getInstantApp(userId) || !ps.getInstalled(userId)) {
continue;
}
mInstantAppRegistry.addInstantAppLPw(userId, ps.appId);
}
}
// Prepare a supplier of package parser for the staging manager to parse apex file
// during the staging installation.
final Supplier<PackageParser2> apexParserSupplier = () -> new PackageParser2(
mSeparateProcesses, mOnlyCore, mMetrics, null /* cacheDir */,
mPackageParserCallback);
mInstallerService = new PackageInstallerService(mContext, this, apexParserSupplier);
final Pair<ComponentName, String> instantAppResolverComponent =
getInstantAppResolverLPr();
if (instantAppResolverComponent != null) {
if (DEBUG_INSTANT) {
Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent);
}
mInstantAppResolverConnection = new InstantAppResolverConnection(
mContext, instantAppResolverComponent.first,
instantAppResolverComponent.second);
mInstantAppResolverSettingsComponent =
getInstantAppResolverSettingsLPr(instantAppResolverComponent.first);
} else {
mInstantAppResolverConnection = null;
mInstantAppResolverSettingsComponent = null;
}
updateInstantAppInstallerLocked(null);
// Read and update the usage of dex files.
// Do this at the end of PM init so that all the packages have their
// data directory reconciled.
// At this point we know the code paths of the packages, so we can validate
// the disk file and build the internal cache.
// The usage file is expected to be small so loading and verifying it
// should take a fairly small time compare to the other activities (e.g. package
// scanning).
final Map<Integer, List<PackageInfo>> userPackages = new HashMap<>();
for (int userId : userIds) {
userPackages.put(userId, getInstalledPackages(/*flags*/ 0, userId).getList());
}
mDexManager.load(userPackages);
if (mIsUpgrade) {
FrameworkStatsLog.write(
FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME,
SystemClock.uptimeMillis() - startTime);
}
} // synchronized (mLock)
} // synchronized (mInstallLock)
// CHECKSTYLE:ON IndentationCheck
mModuleInfoProvider = new ModuleInfoProvider(mContext, this);
// Uncork cache invalidations and allow clients to cache package information.
PackageManager.uncorkPackageInfoCache();
// Now after opening every single application zip, make sure they
// are all flushed. Not really needed, but keeps things nice and
// tidy.
t.traceBegin("GC");
Runtime.getRuntime().gc();
t.traceEnd();
// The initial scanning above does many calls into installd while
// holding the mPackages lock, but we're mostly interested in yelling
// once we have a booted system.
mInstaller.setWarnIfHeld(mLock);
PackageParser.readConfigUseRoundIcon(mContext.getResources());
mServiceStartWithDelay = SystemClock.uptimeMillis() + (60 * 1000L);
Slog.i(TAG, "Fix for b/169414761 is applied");
}
/**
* Uncompress and install stub applications.
* <p>In order to save space on the system partition, some applications are shipped in a
* compressed form. In addition the compressed bits for the full application, the
* system image contains a tiny stub comprised of only the Android manifest.
* <p>During the first boot, attempt to uncompress and install the full application. If
* the application can't be installed for any reason, disable the stub and prevent
* uncompressing the full application during future boots.
* <p>In order to forcefully attempt an installation of a full application, go to app
* settings and enable the application.
*/
private void installSystemStubPackages(@NonNull List<String> systemStubPackageNames,
@ScanFlags int scanFlags) {
for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
final String packageName = systemStubPackageNames.get(i);
// skip if the system package is already disabled
if (mSettings.isDisabledSystemPackageLPr(packageName)) {
systemStubPackageNames.remove(i);
continue;
}
// skip if the package isn't installed (?!); this should never happen
final AndroidPackage pkg = mPackages.get(packageName);
if (pkg == null) {
systemStubPackageNames.remove(i);
continue;
}
// skip if the package has been disabled by the user
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM);
if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
systemStubPackageNames.remove(i);
continue;
}
}
// install the package to replace the stub on /system
try {
installStubPackageLI(pkg, 0, scanFlags);
ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
UserHandle.USER_SYSTEM, "android");
systemStubPackageNames.remove(i);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage());
}
// any failed attempt to install the package will be cleaned up later
}
// disable any stub still left; these failed to install the full application
for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
final String pkgName = systemStubPackageNames.get(i);
final PackageSetting ps = mSettings.mPackages.get(pkgName);
ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
UserHandle.USER_SYSTEM, "android");
logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName);
}
}
/**
* Extract, install and enable a stub package.
* <p>If the compressed file can not be extracted / installed for any reason, the stub
* APK will be installed and the package will be disabled. To recover from this situation,
* the user will need to go into system settings and re-enable the package.
*/
private boolean enableCompressedPackage(AndroidPackage stubPkg,
@NonNull PackageSetting stubPkgSetting) {
final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
| PackageParser.PARSE_ENFORCE_CODE;
synchronized (mInstallLock) {
final AndroidPackage pkg;
try (PackageFreezer freezer =
freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/);
synchronized (mLock) {
prepareAppDataAfterInstallLIF(pkg);
try {
updateSharedLibrariesLocked(pkg, stubPkgSetting, null, null,
Collections.unmodifiableMap(mPackages));
} catch (PackageManagerException e) {
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e);
}
mPermissionManager.updatePermissions(pkg.getPackageName(), pkg);
mSettings.writeLPr();
}
} catch (PackageManagerException e) {
// Whoops! Something went very wrong; roll back to the stub and disable the package
try (PackageFreezer freezer =
freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
synchronized (mLock) {
// NOTE: Ensure the system package is enabled; even for a compressed stub.
// If we don't, installing the system package fails during scan
enableSystemPackageLPw(stubPkg);
}
installPackageFromSystemLIF(stubPkg.getCodePath(),
null /*allUserHandles*/, null /*origUserHandles*/,
null /*origPermissionsState*/, true /*writeSettings*/);
} catch (PackageManagerException pme) {
// Serious WTF; we have to be able to install the stub
Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(),
pme);
} finally {
// Disable the package; the stub by itself is not runnable
synchronized (mLock) {
final PackageSetting stubPs = mSettings.mPackages.get(
stubPkg.getPackageName());
if (stubPs != null) {
stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED,
UserHandle.USER_SYSTEM, "android");
}
mSettings.writeLPr();
}
}
return false;
}
clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
| FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
mDexManager.notifyPackageUpdated(pkg.getPackageName(),
pkg.getBaseCodePath(), pkg.getSplitCodePaths());
}
return true;
}
private AndroidPackage installStubPackageLI(AndroidPackage stubPkg,
@ParseFlags int parseFlags, @ScanFlags int scanFlags)
throws PackageManagerException {
if (DEBUG_COMPRESSION) {
Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.getPackageName());
}
// uncompress the binary to its eventual destination on /data
final File scanFile = decompressPackage(stubPkg.getPackageName(), stubPkg.getCodePath());
if (scanFile == null) {
throw new PackageManagerException(
"Unable to decompress stub at " + stubPkg.getCodePath());
}
synchronized (mLock) {
mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/);
}
removePackageLI(stubPkg, true /*chatty*/);
try {
return scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
e);
// Remove the failed install
removeCodePathLI(scanFile);
throw e;
}
}
/**
* Decompresses the given package on the system image onto
* the /data partition.
* @return The directory the package was decompressed into. Otherwise, {@code null}.
*/
private File decompressPackage(String packageName, String codePath) {
final File[] compressedFiles = getCompressedFiles(codePath);
if (compressedFiles == null || compressedFiles.length == 0) {
if (DEBUG_COMPRESSION) {
Slog.i(TAG, "No files to decompress: " + codePath);
}
return null;
}
final File dstCodePath =
getNextCodePath(Environment.getDataAppDirectory(null), packageName);
int ret = PackageManager.INSTALL_SUCCEEDED;
try {
makeDirRecursive(dstCodePath, 0755);
for (File srcFile : compressedFiles) {
final String srcFileName = srcFile.getName();
final String dstFileName = srcFileName.substring(
0, srcFileName.length() - COMPRESSED_EXTENSION.length());
final File dstFile = new File(dstCodePath, dstFileName);
ret = decompressFile(srcFile, dstFile);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
logCriticalInfo(Log.ERROR, "Failed to decompress"
+ "; pkg: " + packageName
+ ", file: " + dstFileName);
break;
}
}
} catch (ErrnoException e) {
logCriticalInfo(Log.ERROR, "Failed to decompress"
+ "; pkg: " + packageName
+ ", err: " + e.errno);
}
if (ret == PackageManager.INSTALL_SUCCEEDED) {
final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(dstCodePath);
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
null /*abiOverride*/, false /*isIncremental*/);
} catch (IOException e) {
logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
+ "; pkg: " + packageName);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
} finally {
IoUtils.closeQuietly(handle);
}
}
if (ret != PackageManager.INSTALL_SUCCEEDED) {
if (!dstCodePath.exists()) {
return null;
}
removeCodePathLI(dstCodePath);
return null;
}
return dstCodePath;
}
@GuardedBy("mLock")
private void updateInstantAppInstallerLocked(String modifiedPackage) {
// we're only interested in updating the installer appliction when 1) it's not
// already set or 2) the modified package is the installer
if (mInstantAppInstallerActivity != null
&& !mInstantAppInstallerActivity.getComponentName().getPackageName()
.equals(modifiedPackage)) {
return;
}
setUpInstantAppInstallerActivityLP(getInstantAppInstallerLPr());
}
private static @Nullable File preparePackageParserCache() {
if (!FORCE_PACKAGE_PARSED_CACHE_ENABLED) {
if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) {
return null;
}
// Disable package parsing on eng builds to allow for faster incremental development.
if (Build.IS_ENG) {
return null;
}
if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) {
Slog.i(TAG, "Disabling package parser cache due to system property.");
return null;
}
}
// The base directory for the package parser cache lives under /data/system/.
final File cacheBaseDir = Environment.getPackageCacheDirectory();
if (!FileUtils.createDir(cacheBaseDir)) {
return null;
}
// There are several items that need to be combined together to safely
// identify cached items. In particular, changing the value of certain
// feature flags should cause us to invalidate any caches.
final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug"
: SystemProperties.digestOf(
"ro.build.fingerprint",
StorageManager.PROP_ISOLATED_STORAGE,
StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT
);
// Reconcile cache directories, keeping only what we'd actually use.
for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
if (Objects.equals(cacheName, cacheDir.getName())) {
Slog.d(TAG, "Keeping known cache " + cacheDir.getName());
} else {
Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName());
FileUtils.deleteContentsAndDir(cacheDir);
}
}
// Return the versioned package cache directory.
File cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
if (cacheDir == null) {
// Something went wrong. Attempt to delete everything and return.
Slog.wtf(TAG, "Cache directory cannot be created - wiping base dir " + cacheBaseDir);
FileUtils.deleteContentsAndDir(cacheBaseDir);
return null;
}
// The following is a workaround to aid development on non-numbered userdebug
// builds or cases where "adb sync" is used on userdebug builds. If we detect that
// the system partition is newer.
//
// NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build
// that starts with "eng." to signify that this is an engineering build and not
// destined for release.
if (Build.IS_USERDEBUG && Build.VERSION.INCREMENTAL.startsWith("eng.")) {
Slog.w(TAG, "Wiping cache directory because the system partition changed.");
// Heuristic: If the /system directory has been modified recently due to an "adb sync"
// or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable
// in general and should not be used for production changes. In this specific case,
// we know that they will work.
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
if (cacheDir.lastModified() < frameworkDir.lastModified()) {
FileUtils.deleteContents(cacheBaseDir);
cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
}
}
return cacheDir;
}
@Override
public boolean isFirstBoot() {
// allow instant applications
return mFirstBoot;
}
@Override
public boolean isOnlyCoreApps() {
// allow instant applications
return mOnlyCore;
}
@Override
public boolean isDeviceUpgrading() {
// allow instant applications
// The system property allows testing ota flow when upgraded to the same image.
return mIsUpgrade || SystemProperties.getBoolean(
"persist.pm.mock-upgrade", false /* default */);
}
private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() {
final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
final List<ResolveInfo> matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/);
if (matches.size() == 1) {
return matches.get(0).getComponentInfo().packageName;
} else if (matches.size() == 0) {
Log.e(TAG, "There should probably be a verifier, but, none were found");
return null;
}
throw new RuntimeException("There must be exactly one verifier; found " + matches);
}
private @NonNull String getRequiredSharedLibraryLPr(String name, int version) {
synchronized (mLock) {
SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(name, version);
if (libraryInfo == null) {
throw new IllegalStateException("Missing required shared library:" + name);
}
String packageName = libraryInfo.getPackageName();
if (packageName == null) {
throw new IllegalStateException("Expected a package for shared library " + name);
}
return packageName;
}
}
@NonNull
private String getRequiredServicesExtensionPackageLPr() {
String servicesExtensionPackage =
ensureSystemPackageName(
mContext.getString(R.string.config_servicesExtensionPackage));
if (TextUtils.isEmpty(servicesExtensionPackage)) {
throw new RuntimeException(
"Required services extension package is missing, check "
+ "config_servicesExtensionPackage.");
}
return servicesExtensionPackage;
}
private @NonNull String getRequiredInstallerLPr() {
final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(Uri.parse("content://com.example/foo.apk"), PACKAGE_MIME_TYPE);
final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM);
if (matches.size() == 1) {
ResolveInfo resolveInfo = matches.get(0);
if (!resolveInfo.activityInfo.applicationInfo.isPrivilegedApp()) {
throw new RuntimeException("The installer must be a privileged app");
}
return matches.get(0).getComponentInfo().packageName;
} else {
throw new RuntimeException("There must be exactly one installer; found " + matches);
}
}
private @NonNull String getRequiredUninstallerLPr() {
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.fromParts(PACKAGE_SCHEME, "foo.bar", null));
final ResolveInfo resolveInfo = resolveIntent(intent, null,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM);
if (resolveInfo == null ||
mResolveActivity.name.equals(resolveInfo.getComponentInfo().name)) {
throw new RuntimeException("There must be exactly one uninstaller; found "
+ resolveInfo);
}
return resolveInfo.getComponentInfo().packageName;
}
private @NonNull String getRequiredPermissionControllerLPr() {
final Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSIONS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM);
if (matches.size() == 1) {
ResolveInfo resolveInfo = matches.get(0);
if (!resolveInfo.activityInfo.applicationInfo.isPrivilegedApp()) {
throw new RuntimeException("The permissions manager must be a privileged app");
}
return matches.get(0).getComponentInfo().packageName;
} else {
throw new RuntimeException("There must be exactly one permissions manager; found "
+ matches);
}
}
private @NonNull ComponentName getIntentFilterVerifierComponentNameLPr() {
final Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
final List<ResolveInfo> matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/);
ResolveInfo best = null;
final int N = matches.size();
for (int i = 0; i < N; i++) {
final ResolveInfo cur = matches.get(i);
final String packageName = cur.getComponentInfo().packageName;
if (checkPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) {
continue;
}
if (best == null || cur.priority > best.priority) {
best = cur;
}
}
if (best != null) {
return best.getComponentInfo().getComponentName();
}
Slog.w(TAG, "Intent filter verifier not found");
return null;
}
@Override
public @Nullable ComponentName getInstantAppResolverComponent() {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
synchronized (mLock) {
final Pair<ComponentName, String> instantAppResolver = getInstantAppResolverLPr();
if (instantAppResolver == null) {
return null;
}
return instantAppResolver.first;
}
}
private @Nullable Pair<ComponentName, String> getInstantAppResolverLPr() {
final String[] packageArray =
mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage);
if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) {
if (DEBUG_INSTANT) {
Slog.d(TAG, "Ephemeral resolver NOT found; empty package list");
}
return null;
}
final int callingUid = Binder.getCallingUid();
final int resolveFlags =
MATCH_DIRECT_BOOT_AWARE
| MATCH_DIRECT_BOOT_UNAWARE
| (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
String actionName = Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE;
final Intent resolverIntent = new Intent(actionName);
List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null,
resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
final int N = resolvers.size();
if (N == 0) {
if (DEBUG_INSTANT) {
Slog.d(TAG, "Ephemeral resolver NOT found; no matching intent filters");
}
return null;
}
final Set<String> possiblePackages = new ArraySet<>(Arrays.asList(packageArray));
for (int i = 0; i < N; i++) {
final ResolveInfo info = resolvers.get(i);
if (info.serviceInfo == null) {
continue;
}
final String packageName = info.serviceInfo.packageName;
if (!possiblePackages.contains(packageName) && !Build.IS_DEBUGGABLE) {
if (DEBUG_INSTANT) {
Slog.d(TAG, "Ephemeral resolver not in allowed package list;"
+ " pkg: " + packageName + ", info:" + info);
}
continue;
}
if (DEBUG_INSTANT) {
Slog.v(TAG, "Ephemeral resolver found;"
+ " pkg: " + packageName + ", info:" + info);
}
return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName);
}
if (DEBUG_INSTANT) {
Slog.v(TAG, "Ephemeral resolver NOT found");
}
return null;
}
@GuardedBy("mLock")
private @Nullable ActivityInfo getInstantAppInstallerLPr() {
String[] orderedActions = Build.IS_ENG
? new String[]{
Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE + "_TEST",
Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE}
: new String[]{
Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE};
final int resolveFlags =
MATCH_DIRECT_BOOT_AWARE
| MATCH_DIRECT_BOOT_UNAWARE
| Intent.FLAG_IGNORE_EPHEMERAL
| (!Build.IS_ENG ? MATCH_SYSTEM_ONLY : 0);
final Intent intent = new Intent();
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
List<ResolveInfo> matches = null;
for (String action : orderedActions) {
intent.setAction(action);
matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
resolveFlags, UserHandle.USER_SYSTEM);
if (matches.isEmpty()) {
if (DEBUG_INSTANT) {
Slog.d(TAG, "Instant App installer not found with " + action);
}
} else {
break;
}
}
Iterator<ResolveInfo> iter = matches.iterator();
while (iter.hasNext()) {
final ResolveInfo rInfo = iter.next();
if (checkPermission(Manifest.permission.INSTALL_PACKAGES,
rInfo.activityInfo.packageName, 0) == PERMISSION_GRANTED || Build.IS_ENG) {
continue;
}
iter.remove();
}
if (matches.size() == 0) {
return null;
} else if (matches.size() == 1) {
return (ActivityInfo) matches.get(0).getComponentInfo();
} else {
throw new RuntimeException(
"There must be at most one ephemeral installer; found " + matches);
}
}
private @Nullable ComponentName getInstantAppResolverSettingsLPr(
@NonNull ComponentName resolver) {
final Intent intent = new Intent(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS)
.addCategory(Intent.CATEGORY_DEFAULT)
.setPackage(resolver.getPackageName());
final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
UserHandle.USER_SYSTEM);
if (matches.isEmpty()) {
return null;
}
return matches.get(0).getComponentInfo().getComponentName();
}
@GuardedBy("mLock")
private void primeDomainVerificationsLPw(int userId) {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG, "Priming domain verifications in user " + userId);
}
SystemConfig systemConfig = SystemConfig.getInstance();
ArraySet<String> packages = systemConfig.getLinkedApps();
for (String packageName : packages) {
AndroidPackage pkg = mPackages.get(packageName);
if (pkg != null) {
if (!pkg.isSystem()) {
Slog.w(TAG, "Non-system app '" + packageName + "' in sysconfig <app-link>");
continue;
}
ArraySet<String> domains = null;
for (ParsedActivity a : pkg.getActivities()) {
for (ParsedIntentInfo filter : a.getIntents()) {
if (hasValidDomains(filter)) {
if (domains == null) {
domains = new ArraySet<>();
}
domains.addAll(filter.getHostsList());
}
}
}
if (domains != null && domains.size() > 0) {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.v(TAG, " + " + packageName);
}
// 'Undefined' in the global IntentFilterVerificationInfo, i.e. the usual
// state w.r.t. the formal app-linkage "no verification attempted" state;
// and then 'always' in the per-user state actually used for intent resolution.
final IntentFilterVerificationInfo ivi;
ivi = mSettings.createIntentFilterVerificationIfNeededLPw(packageName, domains);
ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
mSettings.updateIntentFilterVerificationStatusLPw(packageName,
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, userId);
} else {
Slog.w(TAG, "Sysconfig <app-link> package '" + packageName
+ "' does not handle web links");
}
} else {
Slog.w(TAG, "Unknown package " + packageName + " in sysconfig <app-link>");
}
}
scheduleWritePackageRestrictionsLocked(userId);
scheduleWriteSettingsLocked();
}
private boolean packageIsBrowser(String packageName, int userId) {
List<ResolveInfo> list = queryIntentActivitiesInternal(sBrowserIntent, null,
PackageManager.MATCH_ALL, userId);
final int N = list.size();
for (int i = 0; i < N; i++) {
ResolveInfo info = list.get(i);
if (info.priority >= 0 && packageName.equals(info.activityInfo.packageName)) {
return true;
}
}
return false;
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
try {
return super.onTransact(code, data, reply, flags);
} catch (RuntimeException e) {
if (!(e instanceof SecurityException) && !(e instanceof IllegalArgumentException)) {
Slog.wtf(TAG, "Package Manager Crash", e);
}
throw e;
}
}
/**
* Returns whether or not a full application can see an instant application.
* <p>
* Currently, there are four cases in which this can occur:
* <ol>
* <li>The calling application is a "special" process. Special processes
* are those with a UID < {@link Process#FIRST_APPLICATION_UID}.</li>
* <li>The calling application has the permission
* {@link android.Manifest.permission#ACCESS_INSTANT_APPS}.</li>
* <li>The calling application is the default launcher on the
* system partition.</li>
* <li>The calling application is the default app prediction service.</li>
* </ol>
*/
private boolean canViewInstantApps(int callingUid, int userId) {
if (callingUid < Process.FIRST_APPLICATION_UID) {
return true;
}
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.ACCESS_INSTANT_APPS) == PERMISSION_GRANTED) {
return true;
}
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.VIEW_INSTANT_APPS) == PERMISSION_GRANTED) {
final ComponentName homeComponent = getDefaultHomeActivity(userId);
if (homeComponent != null
&& isCallerSameApp(homeComponent.getPackageName(), callingUid)) {
return true;
}
// TODO(b/122900055) Change/Remove this and replace with new permission role.
if (mAppPredictionServicePackage != null
&& isCallerSameApp(mAppPredictionServicePackage, callingUid)) {
return true;
}
}
return false;
}
private PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
if (ps == null) {
return null;
}
final int callingUid = Binder.getCallingUid();
// Filter out ephemeral app metadata:
// * The system/shell/root can see metadata for any app
// * An installed app can see metadata for 1) other installed apps
// and 2) ephemeral apps that have explicitly interacted with it
// * Ephemeral apps can only see their own data and exposed installed apps
// * Holding a signature permission allows seeing instant apps
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
return null;
}
if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0
&& ps.isSystem()) {
flags |= MATCH_ANY_USER;
}
final PackageUserState state = ps.readUserState(userId);
AndroidPackage p = ps.pkg;
if (p != null) {
final PermissionsState permissionsState = ps.getPermissionsState();
// Compute GIDs only if requested
final int[] gids = (flags & PackageManager.GET_GIDS) == 0
? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
// Compute granted permissions only if package has requested permissions
Set<String> permissions = ArrayUtils.isEmpty(p.getRequestedPermissions())
? Collections.emptySet() : permissionsState.getPermissions(userId);
if (state.instantApp) {
permissions = new ArraySet<>(permissions);
permissions.removeIf(permissionName -> {
BasePermission permission = mPermissionManager.getPermissionTEMP(
permissionName);
if (permission == null) {
return true;
}
if (!permission.isInstant()) {
EventLog.writeEvent(0x534e4554, "140256621", UserHandle.getUid(userId,
ps.appId), permissionName);
return true;
}
return false;
});
}
PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags,
ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId, ps);
if (packageInfo == null) {
return null;
}
packageInfo.packageName = packageInfo.applicationInfo.packageName =
resolveExternalPackageNameLPr(p);
return packageInfo;
} else if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0 && state.isAvailable(flags)) {
PackageInfo pi = new PackageInfo();
pi.packageName = ps.name;
pi.setLongVersionCode(ps.versionCode);
pi.sharedUserId = (ps.sharedUser != null) ? ps.sharedUser.name : null;
pi.firstInstallTime = ps.firstInstallTime;
pi.lastUpdateTime = ps.lastUpdateTime;
ApplicationInfo ai = new ApplicationInfo();
ai.packageName = ps.name;
ai.uid = UserHandle.getUid(userId, ps.appId);
ai.primaryCpuAbi = ps.primaryCpuAbiString;
ai.secondaryCpuAbi = ps.secondaryCpuAbiString;
ai.setVersionCode(ps.versionCode);
ai.flags = ps.pkgFlags;
ai.privateFlags = ps.pkgPrivateFlags;
pi.applicationInfo = PackageParser.generateApplicationInfo(ai, flags, state, userId);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "ps.pkg is n/a for ["
+ ps.name + "]. Provides a minimum info.");
return pi;
} else {
return null;
}
}
@Override
public void checkPackageStartable(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
throw new SecurityException("Instant applications don't have access to this method");
}
if (!mUserManager.exists(userId)) {
throw new SecurityException("User doesn't exist");
}
mPermissionManager.enforceCrossUserPermission(
callingUid, userId, false, false, "checkPackageStartable");
final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId);
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
throw new SecurityException("Package " + packageName + " was not found!");
}
if (!ps.getInstalled(userId)) {
throw new SecurityException(
"Package " + packageName + " was not installed for user " + userId + "!");
}
if (mSafeMode && !ps.isSystem()) {
throw new SecurityException("Package " + packageName + " not a system app!");
}
if (mFrozenPackages.contains(packageName)) {
throw new SecurityException("Package " + packageName + " is currently frozen!");
}
if (!userKeyUnlocked && !AndroidPackageUtils.isEncryptionAware(ps.pkg)) {
throw new SecurityException("Package " + packageName + " is not encryption aware!");
}
}
}
@Override
public boolean isPackageAvailable(String packageName, int userId) {
if (!mUserManager.exists(userId)) return false;
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "is package available");
synchronized (mLock) {
AndroidPackage p = mPackages.get(packageName);
if (p != null) {
final PackageSetting ps = getPackageSetting(p.getPackageName());
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
return false;
}
if (ps != null) {
final PackageUserState state = ps.readUserState(userId);
if (state != null) {
return PackageParser.isAvailable(state);
}
}
}
}
return false;
}
@Override
public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
flags, Binder.getCallingUid(), userId);
}
@Override
public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage,
int flags, int userId) {
return getPackageInfoInternal(versionedPackage.getPackageName(),
versionedPackage.getLongVersionCode(), flags, Binder.getCallingUid(), userId);
}
/**
* Important: The provided filterCallingUid is used exclusively to filter out packages
* that can be seen based on user state. It's typically the original caller uid prior
* to clearing. Because it can only be provided by trusted code, it's value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
private PackageInfo getPackageInfoInternal(String packageName, long versionCode,
int flags, int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForPackage(flags, userId);
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get package info");
// reader
synchronized (mLock) {
// Normalize package name to handle renamed packages and static libs
packageName = resolveInternalPackageNameLPr(packageName, versionCode);
final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
if (matchFactoryOnly) {
// Instant app filtering for APEX modules is ignored
if ((flags & MATCH_APEX) != 0) {
return mApexManager.getPackageInfo(packageName,
ApexManager.MATCH_FACTORY_PACKAGE);
}
final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
if (ps != null) {
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
}
if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
return null;
}
return generatePackageInfo(ps, flags, userId);
}
}
AndroidPackage p = mPackages.get(packageName);
if (matchFactoryOnly && p != null && !p.isSystem()) {
return null;
}
if (DEBUG_PACKAGE_INFO)
Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
if (p != null) {
final PackageSetting ps = getPackageSetting(p.getPackageName());
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
}
if (ps != null && shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
return null;
}
return generatePackageInfo(ps, flags, userId);
}
if (!matchFactoryOnly && (flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) return null;
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
}
if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
return null;
}
return generatePackageInfo(ps, flags, userId);
}
if ((flags & MATCH_APEX) != 0) {
return mApexManager.getPackageInfo(packageName, ApexManager.MATCH_ACTIVE_PACKAGE);
}
}
return null;
}
private boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
if (isComponentVisibleToInstantApp(component, TYPE_ACTIVITY)) {
return true;
}
if (isComponentVisibleToInstantApp(component, TYPE_SERVICE)) {
return true;
}
if (isComponentVisibleToInstantApp(component, TYPE_PROVIDER)) {
return true;
}
return false;
}
private boolean isComponentVisibleToInstantApp(
@Nullable ComponentName component, @ComponentType int type) {
if (type == TYPE_ACTIVITY) {
final ParsedActivity activity = mComponentResolver.getActivity(component);
if (activity == null) {
return false;
}
final boolean visibleToInstantApp =
(activity.getFlags() & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
final boolean explicitlyVisibleToInstantApp =
(activity.getFlags() & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
return visibleToInstantApp && explicitlyVisibleToInstantApp;
} else if (type == TYPE_RECEIVER) {
final ParsedActivity activity = mComponentResolver.getReceiver(component);
if (activity == null) {
return false;
}
final boolean visibleToInstantApp =
(activity.getFlags() & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
final boolean explicitlyVisibleToInstantApp =
(activity.getFlags() & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
return visibleToInstantApp && !explicitlyVisibleToInstantApp;
} else if (type == TYPE_SERVICE) {
final ParsedService service = mComponentResolver.getService(component);
return service != null
&& (service.getFlags() & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
} else if (type == TYPE_PROVIDER) {
final ParsedProvider provider = mComponentResolver.getProvider(component);
return provider != null
&& (provider.getFlags() & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
} else if (type == TYPE_UNKNOWN) {
return isComponentVisibleToInstantApp(component);
}
return false;
}
/**
* Returns whether or not access to the application should be filtered.
* <p>
* Access may be limited based upon whether the calling or target applications
* are instant applications.
*
* @see #canViewInstantApps(int, int)
*/
@GuardedBy("mLock")
private boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
@Nullable ComponentName component, @ComponentType int componentType, int userId) {
// if we're in an isolated process, get the real calling UID
if (Process.isIsolated(callingUid)) {
callingUid = mIsolatedOwners.get(callingUid);
}
final String instantAppPkgName = getInstantAppPackageName(callingUid);
final boolean callerIsInstantApp = instantAppPkgName != null;
if (ps == null) {
if (callerIsInstantApp) {
// pretend the application exists, but, needs to be filtered
return true;
}
return false;
}
// if the target and caller are the same application, don't filter
if (isCallerSameApp(ps.name, callingUid)) {
return false;
}
if (callerIsInstantApp) {
// both caller and target are both instant, but, different applications, filter
if (ps.getInstantApp(userId)) {
return true;
}
// request for a specific component; if it hasn't been explicitly exposed through
// property or instrumentation target, filter
if (component != null) {
final ParsedInstrumentation instrumentation =
mInstrumentation.get(component);
if (instrumentation != null
&& isCallerSameApp(instrumentation.getTargetPackage(), callingUid)) {
return false;
}
return !isComponentVisibleToInstantApp(component, componentType);
}
// request for application; if no components have been explicitly exposed, filter
return !ps.pkg.isVisibleToInstantApps();
}
if (ps.getInstantApp(userId)) {
// caller can see all components of all instant applications, don't filter
if (canViewInstantApps(callingUid, userId)) {
return false;
}
// request for a specific instant application component, filter
if (component != null) {
return true;
}
// request for an instant application; if the caller hasn't been granted access, filter
return !mInstantAppRegistry.isInstantAccessGranted(
userId, UserHandle.getAppId(callingUid), ps.appId);
}
int appId = UserHandle.getAppId(callingUid);
final SettingBase callingPs = mSettings.getSettingLPr(appId);
return mAppsFilter.shouldFilterApplication(callingUid, callingPs, ps, userId);
}
/**
* @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int)
*/
@GuardedBy("mLock")
private boolean shouldFilterApplicationLocked(
@Nullable PackageSetting ps, int callingUid, int userId) {
return shouldFilterApplicationLocked(ps, callingUid, null, TYPE_UNKNOWN, userId);
}
@GuardedBy("mLock")
private boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
int flags) {
// Callers can access only the libs they depend on, otherwise they need to explicitly
// ask for the shared libraries given the caller is allowed to access all static libs.
if ((flags & PackageManager.MATCH_STATIC_SHARED_LIBRARIES) != 0) {
// System/shell/root get to see all static libs
final int appId = UserHandle.getAppId(uid);
if (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID
|| appId == Process.ROOT_UID) {
return false;
}
// Installer gets to see all static libs.
if (PackageManager.PERMISSION_GRANTED
== checkUidPermission(Manifest.permission.INSTALL_PACKAGES, uid)) {
return false;
}
}
// No package means no static lib as it is always on internal storage
if (ps == null || ps.pkg == null || !ps.pkg.isStaticSharedLibrary()) {
return false;
}
final SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(
ps.pkg.getStaticSharedLibName(), ps.pkg.getStaticSharedLibVersion());
if (libraryInfo == null) {
return false;
}
final int resolvedUid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
final String[] uidPackageNames = getPackagesForUid(resolvedUid);
if (uidPackageNames == null) {
return true;
}
for (String uidPackageName : uidPackageNames) {
if (ps.name.equals(uidPackageName)) {
return false;
}
PackageSetting uidPs = mSettings.getPackageLPr(uidPackageName);
if (uidPs != null) {
final int index = ArrayUtils.indexOf(uidPs.usesStaticLibraries,
libraryInfo.getName());
if (index < 0) {
continue;
}
if (uidPs.pkg.getUsesStaticLibrariesVersions()[index]
== libraryInfo.getLongVersion()) {
return false;
}
}
}
return true;
}
@Override
public String[] currentToCanonicalPackageNames(String[] names) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return names;
}
final String[] out = new String[names.length];
// reader
synchronized (mLock) {
final int callingUserId = UserHandle.getUserId(callingUid);
final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId);
for (int i=names.length-1; i>=0; i--) {
final PackageSetting ps = mSettings.mPackages.get(names[i]);
boolean translateName = false;
if (ps != null && ps.realName != null) {
final boolean targetIsInstantApp = ps.getInstantApp(callingUserId);
translateName = !targetIsInstantApp
|| canViewInstantApps
|| mInstantAppRegistry.isInstantAccessGranted(callingUserId,
UserHandle.getAppId(callingUid), ps.appId);
}
out[i] = translateName ? ps.realName : names[i];
}
}
return out;
}
@Override
public String[] canonicalToCurrentPackageNames(String[] names) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return names;
}
final String[] out = new String[names.length];
// reader
synchronized (mLock) {
final int callingUserId = UserHandle.getUserId(callingUid);
final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId);
for (int i=names.length-1; i>=0; i--) {
final String cur = mSettings.getRenamedPackageLPr(names[i]);
boolean translateName = false;
if (cur != null) {
final PackageSetting ps = mSettings.mPackages.get(names[i]);
final boolean targetIsInstantApp =
ps != null && ps.getInstantApp(callingUserId);
translateName = !targetIsInstantApp
|| canViewInstantApps
|| mInstantAppRegistry.isInstantAccessGranted(callingUserId,
UserHandle.getAppId(callingUid), ps.appId);
}
out[i] = translateName ? cur : names[i];
}
}
return out;
}
@Override
public int getPackageUid(String packageName, int flags, int userId) {
if (!mUserManager.exists(userId)) return -1;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid");
return getPackageUidInternal(packageName, flags, userId, callingUid);
}
private int getPackageUidInternal(String packageName, int flags, int userId, int callingUid) {
// reader
synchronized (mLock) {
final AndroidPackage p = mPackages.get(packageName);
if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
PackageSetting ps = getPackageSettingInternal(p.getPackageName(), callingUid);
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
return -1;
}
return UserHandle.getUid(userId, p.getUid());
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null && ps.isMatch(flags)
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
return UserHandle.getUid(userId, ps.appId);
}
}
}
return -1;
}
@Override
public int[] getPackageGids(String packageName, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids");
// reader
synchronized (mLock) {
final AndroidPackage p = mPackages.get(packageName);
if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
PackageSetting ps = getPackageSetting(p.getPackageName());
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
return null;
}
// TODO: Shouldn't this be checking for package installed state for userId and
// return null?
return ps.getPermissionsState().computeGids(userId);
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null && ps.isMatch(flags)
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
return ps.getPermissionsState().computeGids(userId);
}
}
}
return null;
}
// NOTE: Can't remove due to unsupported app usage
@Override
public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) {
try {
// Because this is accessed via the package manager service AIDL,
// go through the permission manager service AIDL
return mPermissionManagerService.getPermissionGroupInfo(groupName, flags);
} catch (RemoteException ignore) { }
return null;
}
@GuardedBy("mLock")
private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
}
if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
return null;
}
if (ps.pkg == null) {
final PackageInfo pInfo = generatePackageInfo(ps, flags, userId);
if (pInfo != null) {
return pInfo.applicationInfo;
}
return null;
}
ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(ps.pkg, flags,
ps.readUserState(userId), userId, ps);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
}
return ai;
}
return null;
}
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
return getApplicationInfoInternal(packageName, flags, Binder.getCallingUid(), userId);
}
/**
* Important: The provided filterCallingUid is used exclusively to filter out applications
* that can be seen based on user state. It's typically the original caller uid prior
* to clearing. Because it can only be provided by trusted code, it's value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
private ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForApplication(flags, userId);
if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */,
"get application info");
}
// writer
synchronized (mLock) {
// Normalize package name to handle renamed packages and static libs
packageName = resolveInternalPackageNameLPr(packageName,
PackageManager.VERSION_CODE_HIGHEST);
AndroidPackage p = mPackages.get(packageName);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getApplicationInfo " + packageName
+ ": " + p);
if (p != null) {
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) return null;
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
}
if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
return null;
}
// Note: isEnabledLP() does not apply here - always return info
ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(
p, flags, ps.readUserState(userId), userId, ps);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(p);
}
return ai;
}
if ((flags & PackageManager.MATCH_APEX) != 0) {
// For APKs, PackageInfo.applicationInfo is not exactly the same as ApplicationInfo
// returned from getApplicationInfo, but for APEX packages difference shouldn't be
// very big.
// TODO(b/155328545): generate proper application info for APEXes as well.
int apexFlags = ApexManager.MATCH_ACTIVE_PACKAGE;
if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
apexFlags = ApexManager.MATCH_FACTORY_PACKAGE;
}
final PackageInfo pi = mApexManager.getPackageInfo(packageName, apexFlags);
if (pi == null) {
return null;
}
return pi.applicationInfo;
}
if ("android".equals(packageName)||"system".equals(packageName)) {
return mAndroidApplication;
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
// Already generates the external package name
return generateApplicationInfoFromSettingsLPw(packageName,
flags, filterCallingUid, userId);
}
}
return null;
}
@GuardedBy("mLock")
private String normalizePackageNameLPr(String packageName) {
String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
return normalizedPackageName != null ? normalizedPackageName : packageName;
}
@Override
public void deletePreloadsFileCache() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CLEAR_APP_CACHE,
"deletePreloadsFileCache");
File dir = Environment.getDataPreloadsFileCacheDirectory();
Slog.i(TAG, "Deleting preloaded file cache " + dir);
FileUtils.deleteContents(dir);
}
@Override
public void freeStorageAndNotify(final String volumeUuid, final long freeStorageSize,
final int storageFlags, final IPackageDataObserver observer) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CLEAR_APP_CACHE, null);
mHandler.post(() -> {
boolean success = false;
try {
freeStorage(volumeUuid, freeStorageSize, storageFlags);
success = true;
} catch (IOException e) {
Slog.w(TAG, e);
}
if (observer != null) {
try {
observer.onRemoveCompleted(null, success);
} catch (RemoteException e) {
Slog.w(TAG, e);
}
}
});
}
@Override
public void freeStorage(final String volumeUuid, final long freeStorageSize,
final int storageFlags, final IntentSender pi) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CLEAR_APP_CACHE, TAG);
mHandler.post(() -> {
boolean success = false;
try {
freeStorage(volumeUuid, freeStorageSize, storageFlags);
success = true;
} catch (IOException e) {
Slog.w(TAG, e);
}
if (pi != null) {
try {
pi.sendIntent(null, success ? 1 : 0, null, null, null);
} catch (SendIntentException e) {
Slog.w(TAG, e);
}
}
});
}
/**
* Blocking call to clear various types of cached data across the system
* until the requested bytes are available.
*/
public void freeStorage(String volumeUuid, long bytes, int storageFlags) throws IOException {
final StorageManager storage = mInjector.getStorageManager();
final File file = storage.findPathForUuid(volumeUuid);
if (file.getUsableSpace() >= bytes) return;
if (ENABLE_FREE_CACHE_V2) {
final boolean internalVolume = Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL,
volumeUuid);
final boolean aggressive = (storageFlags
& StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0;
final long reservedBytes = storage.getStorageCacheBytes(file, storageFlags);
// 1. Pre-flight to determine if we have any chance to succeed
// 2. Consider preloaded data (after 1w honeymoon, unless aggressive)
if (internalVolume && (aggressive || SystemProperties
.getBoolean("persist.sys.preloads.file_cache_expired", false))) {
deletePreloadsFileCache();
if (file.getUsableSpace() >= bytes) return;
}
// 3. Consider parsed APK data (aggressive only)
if (internalVolume && aggressive) {
FileUtils.deleteContents(mCacheDir);
if (file.getUsableSpace() >= bytes) return;
}
// 4. Consider cached app data (above quotas)
try {
mInstaller.freeCache(volumeUuid, bytes, reservedBytes,
Installer.FLAG_FREE_CACHE_V2);
} catch (InstallerException ignored) {
}
if (file.getUsableSpace() >= bytes) return;
// 5. Consider shared libraries with refcount=0 and age>min cache period
if (internalVolume && pruneUnusedStaticSharedLibraries(bytes,
android.provider.Settings.Global.getLong(mContext.getContentResolver(),
Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD))) {
return;
}
// 6. Consider dexopt output (aggressive only)
// TODO: Implement
// 7. Consider installed instant apps unused longer than min cache period
if (internalVolume && mInstantAppRegistry.pruneInstalledInstantApps(bytes,
android.provider.Settings.Global.getLong(mContext.getContentResolver(),
Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
InstantAppRegistry.DEFAULT_INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) {
return;
}
// 8. Consider cached app data (below quotas)
try {
mInstaller.freeCache(volumeUuid, bytes, reservedBytes,
Installer.FLAG_FREE_CACHE_V2 | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA);
} catch (InstallerException ignored) {
}
if (file.getUsableSpace() >= bytes) return;
// 9. Consider DropBox entries
// TODO: Implement
// 10. Consider instant meta-data (uninstalled apps) older that min cache period
if (internalVolume && mInstantAppRegistry.pruneUninstalledInstantApps(bytes,
android.provider.Settings.Global.getLong(mContext.getContentResolver(),
Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
InstantAppRegistry.DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) {
return;
}
} else {
try {
mInstaller.freeCache(volumeUuid, bytes, 0, 0);
} catch (InstallerException ignored) {
}
if (file.getUsableSpace() >= bytes) return;
}
throw new IOException("Failed to free " + bytes + " on storage device at " + file);
}
private boolean pruneUnusedStaticSharedLibraries(long neededSpace, long maxCachePeriod)
throws IOException {
final StorageManager storage = mInjector.getStorageManager();
final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL);
List<VersionedPackage> packagesToDelete = null;
final long now = System.currentTimeMillis();
synchronized (mLock) {
final int[] allUsers = mUserManager.getUserIds();
final int libCount = mSharedLibraries.size();
for (int i = 0; i < libCount; i++) {
final LongSparseArray<SharedLibraryInfo> versionedLib
= mSharedLibraries.valueAt(i);
if (versionedLib == null) {
continue;
}
final int versionCount = versionedLib.size();
for (int j = 0; j < versionCount; j++) {
SharedLibraryInfo libInfo = versionedLib.valueAt(j);
// Skip packages that are not static shared libs.
if (!libInfo.isStatic()) {
break;
}
// Important: We skip static shared libs used for some user since
// in such a case we need to keep the APK on the device. The check for
// a lib being used for any user is performed by the uninstall call.
final VersionedPackage declaringPackage = libInfo.getDeclaringPackage();
// Resolve the package name - we use synthetic package names internally
final String internalPackageName = resolveInternalPackageNameLPr(
declaringPackage.getPackageName(),
declaringPackage.getLongVersionCode());
final PackageSetting ps = mSettings.getPackageLPr(internalPackageName);
// Skip unused static shared libs cached less than the min period
// to prevent pruning a lib needed by a subsequently installed package.
if (ps == null || now - ps.lastUpdateTime < maxCachePeriod) {
continue;
}
if (ps.pkg.isSystem()) {
continue;
}
if (packagesToDelete == null) {
packagesToDelete = new ArrayList<>();
}
packagesToDelete.add(new VersionedPackage(internalPackageName,
declaringPackage.getLongVersionCode()));
}
}
}
if (packagesToDelete != null) {
final int packageCount = packagesToDelete.size();
for (int i = 0; i < packageCount; i++) {
final VersionedPackage pkgToDelete = packagesToDelete.get(i);
// Delete the package synchronously (will fail of the lib used for any user).
if (deletePackageX(pkgToDelete.getPackageName(), pkgToDelete.getLongVersionCode(),
UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS)
== PackageManager.DELETE_SUCCEEDED) {
if (volume.getUsableSpace() >= neededSpace) {
return true;
}
}
}
}
return false;
}
/**
* Update given flags based on encryption status of current user.
*/
private int updateFlags(int flags, int userId) {
if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DIRECT_BOOT_AWARE)) != 0) {
// Caller expressed an explicit opinion about what encryption
// aware/unaware components they want to see, so fall through and
// give them what they want
} else {
// Caller expressed no opinion, so match based on user state
if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
} else {
flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE;
}
}
return flags;
}
/**
* Update given flags when being used to request {@link PackageInfo}.
*/
private int updateFlagsForPackage(int flags, int userId) {
final boolean isCallerSystemUser = UserHandle.getCallingUserId() == UserHandle.USER_SYSTEM;
if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
// require the permission to be held; the calling uid and given user id referring
// to the same user is not sufficient
mPermissionManager.enforceCrossUserPermission(
Binder.getCallingUid(), userId, false, false,
!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId),
"MATCH_ANY_USER flag requires INTERACT_ACROSS_USERS permission at "
+ Debug.getCallers(5));
} else if ((flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0 && isCallerSystemUser
&& mUserManager.hasManagedProfile(UserHandle.USER_SYSTEM)) {
// If the caller wants all packages and has a restricted profile associated with it,
// then match all users. This is to make sure that launchers that need to access work
// profile apps don't start breaking. TODO: Remove this hack when launchers stop using
// MATCH_UNINSTALLED_PACKAGES to query apps in other profiles. b/31000380
flags |= PackageManager.MATCH_ANY_USER;
}
return updateFlags(flags, userId);
}
/**
* Update given flags when being used to request {@link ApplicationInfo}.
*/
private int updateFlagsForApplication(int flags, int userId) {
return updateFlagsForPackage(flags, userId);
}
/**
* Update given flags when being used to request {@link ComponentInfo}.
*/
private int updateFlagsForComponent(int flags, int userId) {
return updateFlags(flags, userId);
}
/**
* Update given intent when being used to request {@link ResolveInfo}.
*/
private Intent updateIntentForResolve(Intent intent) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
}
if (DEBUG_PREFERRED) {
intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
}
return intent;
}
/**
* Update given flags when being used to request {@link ResolveInfo}.
* <p>Instant apps are resolved specially, depending upon context. Minimally,
* {@code}flags{@code} must have the {@link PackageManager#MATCH_INSTANT}
* flag set. However, this flag is only honoured in three circumstances:
* <ul>
* <li>when called from a system process</li>
* <li>when the caller holds the permission {@code android.permission.ACCESS_INSTANT_APPS}</li>
* <li>when resolution occurs to start an activity with a {@code android.intent.action.VIEW}
* action and a {@code android.intent.category.BROWSABLE} category</li>
* </ul>
*/
int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
return updateFlagsForResolve(flags, userId, callingUid,
wantInstantApps, false /*onlyExposedExplicitly*/,
isImplicitImageCaptureIntentAndNotSetByDpc);
}
int updateFlagsForResolve(int flags, int userId, int callingUid,
boolean wantInstantApps, boolean onlyExposedExplicitly,
boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
// Safe mode means we shouldn't match any third-party components
if (mSafeMode || isImplicitImageCaptureIntentAndNotSetByDpc) {
flags |= PackageManager.MATCH_SYSTEM_ONLY;
}
if (getInstantAppPackageName(callingUid) != null) {
// But, ephemeral apps see both ephemeral and exposed, non-ephemeral components
if (onlyExposedExplicitly) {
flags |= PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY;
}
flags |= PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY;
flags |= PackageManager.MATCH_INSTANT;
} else {
final boolean wantMatchInstant = (flags & PackageManager.MATCH_INSTANT) != 0;
final boolean allowMatchInstant = wantInstantApps
|| (wantMatchInstant && canViewInstantApps(callingUid, userId));
flags &= ~(PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY
| PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY);
if (!allowMatchInstant) {
flags &= ~PackageManager.MATCH_INSTANT;
}
}
return updateFlagsForComponent(flags, userId);
}
@Override
public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId);
}
/**
* Important: The provided filterCallingUid is used exclusively to filter out activities
* that can be seen based on user state. It's typically the original caller uid prior
* to clearing. Because it can only be provided by trusted code, it's value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
private ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId);
if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get activity info");
}
synchronized (mLock) {
ParsedActivity a = mComponentResolver.getActivity(component);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
if (pkg != null && mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) {
PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
if (ps == null) return null;
if (shouldFilterApplicationLocked(
ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
return null;
}
return PackageInfoUtils.generateActivityInfo(pkg,
a, flags, ps.readUserState(userId), userId, ps);
}
if (mResolveComponentName.equals(component)) {
return PackageParser.generateActivityInfo(
mResolveActivity, flags, new PackageUserState(), userId);
}
}
return null;
}
private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) {
if (!mInjector.getActivityTaskManagerInternal().isCallerRecents(callingUid)) {
return false;
}
final long token = Binder.clearCallingIdentity();
try {
final int callingUserId = UserHandle.getUserId(callingUid);
if (ActivityManager.getCurrentUser() != callingUserId) {
return false;
}
return mUserManager.isSameProfileGroup(callingUserId, targetUserId);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public boolean activitySupportsIntent(ComponentName component, Intent intent,
String resolvedType) {
synchronized (mLock) {
if (component.equals(mResolveComponentName)) {
// The resolver supports EVERYTHING!
return true;
}
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
ParsedActivity a = mComponentResolver.getActivity(component);
if (a == null) {
return false;
}
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) {
return false;
}
if (shouldFilterApplicationLocked(
ps, callingUid, component, TYPE_ACTIVITY, callingUserId)) {
return false;
}
for (int i=0; i< a.getIntents().size(); i++) {
if (a.getIntents().get(i).match(intent.getAction(), resolvedType, intent.getScheme(),
intent.getData(), intent.getCategories(), TAG) >= 0) {
return true;
}
}
return false;
}
}
@Override
public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get receiver info");
synchronized (mLock) {
ParsedActivity a = mComponentResolver.getReceiver(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getReceiverInfo " + component + ": " + a);
if (a == null) {
return null;
}
AndroidPackage pkg = mPackages.get(a.getPackageName());
if (pkg == null) {
return null;
}
if (mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
if (shouldFilterApplicationLocked(
ps, callingUid, component, TYPE_RECEIVER, userId)) {
return null;
}
return PackageInfoUtils.generateActivityInfo(pkg,
a, flags, ps.readUserState(userId), userId, ps);
}
}
return null;
}
@Override
public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(String packageName,
int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0");
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
flags = updateFlagsForPackage(flags, userId);
final boolean canSeeStaticLibraries =
mContext.checkCallingOrSelfPermission(INSTALL_PACKAGES)
== PERMISSION_GRANTED
|| mContext.checkCallingOrSelfPermission(DELETE_PACKAGES)
== PERMISSION_GRANTED
|| canRequestPackageInstallsInternal(packageName,
PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId,
false /* throwIfPermNotDeclared*/)
|| mContext.checkCallingOrSelfPermission(REQUEST_DELETE_PACKAGES)
== PERMISSION_GRANTED
|| mContext.checkCallingOrSelfPermission(
Manifest.permission.ACCESS_SHARED_LIBRARIES) == PERMISSION_GRANTED;
synchronized (mLock) {
List<SharedLibraryInfo> result = null;
final int libCount = mSharedLibraries.size();
for (int i = 0; i < libCount; i++) {
LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.valueAt(i);
if (versionedLib == null) {
continue;
}
final int versionCount = versionedLib.size();
for (int j = 0; j < versionCount; j++) {
SharedLibraryInfo libInfo = versionedLib.valueAt(j);
if (!canSeeStaticLibraries && libInfo.isStatic()) {
break;
}
final long identity = Binder.clearCallingIdentity();
try {
PackageInfo packageInfo = getPackageInfoVersioned(
libInfo.getDeclaringPackage(), flags
| PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId);
if (packageInfo == null) {
continue;
}
} finally {
Binder.restoreCallingIdentity(identity);
}
SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getPath(),
libInfo.getPackageName(), libInfo.getAllCodePaths(),
libInfo.getName(), libInfo.getLongVersion(),
libInfo.getType(), libInfo.getDeclaringPackage(),
getPackagesUsingSharedLibraryLPr(libInfo, flags, userId),
(libInfo.getDependencies() == null
? null
: new ArrayList<>(libInfo.getDependencies())));
if (result == null) {
result = new ArrayList<>();
}
result.add(resLibInfo);
}
}
return result != null ? new ParceledListSlice<>(result) : null;
}
}
@Nullable
@Override
public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
@NonNull String packageName, int flags, @NonNull int userId) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES,
"getDeclaredSharedLibraries");
int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"getDeclaredSharedLibraries");
Preconditions.checkNotNull(packageName, "packageName cannot be null");
Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0");
if (!mUserManager.exists(userId)) {
return null;
}
if (getInstantAppPackageName(callingUid) != null) {
return null;
}
synchronized (mLock) {
List<SharedLibraryInfo> result = null;
int libraryCount = mSharedLibraries.size();
for (int i = 0; i < libraryCount; i++) {
LongSparseArray<SharedLibraryInfo> versionedLibrary = mSharedLibraries.valueAt(i);
if (versionedLibrary == null) {
continue;
}
int versionCount = versionedLibrary.size();
for (int j = 0; j < versionCount; j++) {
SharedLibraryInfo libraryInfo = versionedLibrary.valueAt(j);
VersionedPackage declaringPackage = libraryInfo.getDeclaringPackage();
if (!Objects.equals(declaringPackage.getPackageName(), packageName)) {
continue;
}
long identity = Binder.clearCallingIdentity();
try {
PackageInfo packageInfo = getPackageInfoVersioned(declaringPackage, flags
| PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId);
if (packageInfo == null) {
continue;
}
} finally {
Binder.restoreCallingIdentity(identity);
}
SharedLibraryInfo resultLibraryInfo = new SharedLibraryInfo(
libraryInfo.getPath(), libraryInfo.getPackageName(),
libraryInfo.getAllCodePaths(), libraryInfo.getName(),
libraryInfo.getLongVersion(), libraryInfo.getType(),
libraryInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr(
libraryInfo, flags, userId), libraryInfo.getDependencies() == null
? null : new ArrayList<>(libraryInfo.getDependencies()));
if (result == null) {
result = new ArrayList<>();
}
result.add(resultLibraryInfo);
}
}
return result != null ? new ParceledListSlice<>(result) : null;
}
}
@GuardedBy("mLock")
private List<VersionedPackage> getPackagesUsingSharedLibraryLPr(
SharedLibraryInfo libInfo, int flags, int userId) {
List<VersionedPackage> versionedPackages = null;
final int packageCount = mSettings.mPackages.size();
for (int i = 0; i < packageCount; i++) {
PackageSetting ps = mSettings.mPackages.valueAt(i);
if (ps == null) {
continue;
}
if (!ps.readUserState(userId).isAvailable(flags)) {
continue;
}
final String libName = libInfo.getName();
if (libInfo.isStatic()) {
final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName);
if (libIdx < 0) {
continue;
}
if (ps.usesStaticLibrariesVersions[libIdx] != libInfo.getLongVersion()) {
continue;
}
if (versionedPackages == null) {
versionedPackages = new ArrayList<>();
}
// If the dependent is a static shared lib, use the public package name
String dependentPackageName = ps.name;
if (ps.pkg != null && ps.pkg.isStaticSharedLibrary()) {
dependentPackageName = ps.pkg.getManifestPackageName();
}
versionedPackages.add(new VersionedPackage(dependentPackageName, ps.versionCode));
} else if (ps.pkg != null) {
if (ArrayUtils.contains(ps.pkg.getUsesLibraries(), libName)
|| ArrayUtils.contains(ps.pkg.getUsesOptionalLibraries(), libName)) {
if (versionedPackages == null) {
versionedPackages = new ArrayList<>();
}
versionedPackages.add(new VersionedPackage(ps.name, ps.versionCode));
}
}
}
return versionedPackages;
}
@Override
public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
mPermissionManager.enforceCrossUserOrProfilePermission(
callingUid, userId, false /* requireFullPermission */, false /* checkShell */,
"get service info");
synchronized (mLock) {
ParsedService s = mComponentResolver.getService(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getServiceInfo " + component + ": " + s);
if (s == null) {
return null;
}
AndroidPackage pkg = mPackages.get(s.getPackageName());
if (mSettings.isEnabledAndMatchLPr(pkg, s, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
if (shouldFilterApplicationLocked(
ps, callingUid, component, TYPE_SERVICE, userId)) {
return null;
}
return PackageInfoUtils.generateServiceInfo(pkg,
s, flags, ps.readUserState(userId), userId, ps);
}
}
return null;
}
@Override
public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get provider info");
synchronized (mLock) {
ParsedProvider p = mComponentResolver.getProvider(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getProviderInfo " + component + ": " + p);
if (p == null) {
return null;
}
AndroidPackage pkg = mPackages.get(p.getPackageName());
if (pkg == null) {
return null;
}
if (mSettings.isEnabledAndMatchLPr(pkg, p, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
if (shouldFilterApplicationLocked(
ps, callingUid, component, TYPE_PROVIDER, userId)) {
return null;
}
PackageUserState state = ps.readUserState(userId);
final ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
pkg, flags, state, userId, ps);
if (appInfo == null) {
return null;
}
return PackageInfoUtils.generateProviderInfo(
pkg, p, flags, state, appInfo, userId, ps);
}
}
return null;
}
@Override
public ModuleInfo getModuleInfo(String packageName, @ModuleInfoFlags int flags) {
return mModuleInfoProvider.getModuleInfo(packageName, flags);
}
@Override
public List<ModuleInfo> getInstalledModules(int flags) {
return mModuleInfoProvider.getInstalledModules(flags);
}
@Override
public String[] getSystemSharedLibraryNames() {
// allow instant applications
synchronized (mLock) {
Set<String> libs = null;
final int libCount = mSharedLibraries.size();
for (int i = 0; i < libCount; i++) {
LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.valueAt(i);
if (versionedLib == null) {
continue;
}
final int versionCount = versionedLib.size();
for (int j = 0; j < versionCount; j++) {
SharedLibraryInfo libraryInfo = versionedLib.valueAt(j);
if (!libraryInfo.isStatic()) {
if (libs == null) {
libs = new ArraySet<>();
}
libs.add(libraryInfo.getName());
break;
}
PackageSetting ps = mSettings.getPackageLPr(libraryInfo.getPackageName());
if (ps != null && !filterSharedLibPackageLPr(ps, Binder.getCallingUid(),
UserHandle.getUserId(Binder.getCallingUid()),
PackageManager.MATCH_STATIC_SHARED_LIBRARIES)) {
if (libs == null) {
libs = new ArraySet<>();
}
libs.add(libraryInfo.getName());
break;
}
}
}
if (libs != null) {
String[] libsArray = new String[libs.size()];
libs.toArray(libsArray);
return libsArray;
}
return null;
}
}
@Override
public @NonNull String getServicesSystemSharedLibraryPackageName() {
// allow instant applications
synchronized (mLock) {
return mServicesExtensionPackageName;
}
}
@Override
public @NonNull String getSharedSystemSharedLibraryPackageName() {
// allow instant applications
synchronized (mLock) {
return mSharedSystemSharedLibraryPackageName;
}
}
@GuardedBy("mLock")
private void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) {
for (int i = userList.length - 1; i >= 0; --i) {
final int userId = userList[i];
// don't add instant app to the list of updates
if (pkgSetting.getInstantApp(userId)) {
continue;
}
SparseArray<String> changedPackages = mChangedPackages.get(userId);
if (changedPackages == null) {
changedPackages = new SparseArray<>();
mChangedPackages.put(userId, changedPackages);
}
Map<String, Integer> sequenceNumbers = mChangedPackagesSequenceNumbers.get(userId);
if (sequenceNumbers == null) {
sequenceNumbers = new HashMap<>();
mChangedPackagesSequenceNumbers.put(userId, sequenceNumbers);
}
final Integer sequenceNumber = sequenceNumbers.get(pkgSetting.name);
if (sequenceNumber != null) {
changedPackages.remove(sequenceNumber);
}
changedPackages.put(mChangedPackagesSequenceNumber, pkgSetting.name);
sequenceNumbers.put(pkgSetting.name, mChangedPackagesSequenceNumber);
}
mChangedPackagesSequenceNumber++;
}
@Override
public ChangedPackages getChangedPackages(int sequenceNumber, int userId) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return null;
}
if (!mUserManager.exists(userId)) {
return null;
}
mPermissionManager.enforceCrossUserPermission(
callingUid, userId, false, false, "getChangedPackages");
synchronized (mLock) {
if (sequenceNumber >= mChangedPackagesSequenceNumber) {
return null;
}
final SparseArray<String> changedPackages = mChangedPackages.get(userId);
if (changedPackages == null) {
return null;
}
final List<String> packageNames =
new ArrayList<>(mChangedPackagesSequenceNumber - sequenceNumber);
for (int i = sequenceNumber; i < mChangedPackagesSequenceNumber; i++) {
final String packageName = changedPackages.get(i);
if (packageName != null) {
packageNames.add(packageName);
}
}
return packageNames.isEmpty()
? null : new ChangedPackages(mChangedPackagesSequenceNumber, packageNames);
}
}
@Override
public @NonNull ParceledListSlice<FeatureInfo> getSystemAvailableFeatures() {
// allow instant applications
ArrayList<FeatureInfo> res;
synchronized (mAvailableFeatures) {
res = new ArrayList<>(mAvailableFeatures.size() + 1);
res.addAll(mAvailableFeatures.values());
}
final FeatureInfo fi = new FeatureInfo();
fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version",
FeatureInfo.GL_ES_VERSION_UNDEFINED);
res.add(fi);
return new ParceledListSlice<>(res);
}
@Override
public boolean hasSystemFeature(String name, int version) {
// allow instant applications
synchronized (mAvailableFeatures) {
final FeatureInfo feat = mAvailableFeatures.get(name);
if (feat == null) {
return false;
} else {
return feat.version >= version;
}
}
}
// NOTE: Can't remove due to unsupported app usage
@Override
public int checkPermission(String permName, String pkgName, int userId) {
try {
// Because this is accessed via the package manager service AIDL,
// go through the permission manager service AIDL
return mPermissionManagerService.checkPermission(permName, pkgName, userId);
} catch (RemoteException ignore) { }
return PackageManager.PERMISSION_DENIED;
}
// NOTE: Can't remove without a major refactor. Keep around for now.
@Override
public int checkUidPermission(String permName, int uid) {
try {
// Because this is accessed via the package manager service AIDL,
// go through the permission manager service AIDL
return mPermissionManagerService.checkUidPermission(permName, uid);
} catch (RemoteException ignore) { }
return PackageManager.PERMISSION_DENIED;
}
@Override
public String getPermissionControllerPackageName() {
synchronized (mLock) {
return mRequiredPermissionControllerPackage;
}
}
String getPackageInstallerPackageName() {
synchronized (mLock) {
return mRequiredInstallerPackage;
}
}
// NOTE: Can't remove due to unsupported app usage
@Override
public boolean addPermission(PermissionInfo info) {
try {
// Because this is accessed via the package manager service AIDL,
// go through the permission manager service AIDL
return mPermissionManagerService.addPermission(info, false);
} catch (RemoteException ignore) { }
return false;
}
// NOTE: Can't remove due to unsupported app usage
@Override
public boolean addPermissionAsync(PermissionInfo info) {
try {
// Because this is accessed via the package manager service AIDL,
// go through the permission manager service AIDL
return mPermissionManagerService.addPermission(info, true);
} catch (RemoteException ignore) { }
return false;
}
// NOTE: Can't remove due to unsupported app usage
@Override
public void removePermission(String permName) {
try {
// Because this is accessed via the package manager service AIDL,
// go through the permission manager service AIDL
mPermissionManagerService.removePermission(permName);
} catch (RemoteException ignore) { }
}
// NOTE: Can't remove due to unsupported app usage
@Override
public void grantRuntimePermission(String packageName, String permName, final int userId) {
try {
// Because this is accessed via the package manager service AIDL,
// go through the permission manager service AIDL
mPermissionManagerService.grantRuntimePermission(packageName, permName, userId);
} catch (RemoteException ignore) { }
}
@Override
public boolean isProtectedBroadcast(String actionName) {
// allow instant applications
synchronized (mProtectedBroadcasts) {
if (mProtectedBroadcasts.contains(actionName)) {
return true;
} else if (actionName != null) {
// TODO: remove these terrible hacks
if (actionName.startsWith("android.net.netmon.lingerExpired")
|| actionName.startsWith("com.android.server.sip.SipWakeupTimer")
|| actionName.startsWith("com.android.internal.telephony.data-reconnect")
|| actionName.startsWith("android.net.netmon.launchCaptivePortalApp")) {
return true;
}
}
}
return false;
}
@Override
public int checkSignatures(String pkg1, String pkg2) {
synchronized (mLock) {
final AndroidPackage p1 = mPackages.get(pkg1);
final AndroidPackage p2 = mPackages.get(pkg2);
final PackageSetting ps1 = p1 == null ? null : getPackageSetting(p1.getPackageName());
final PackageSetting ps2 = p2 == null ? null : getPackageSetting(p2.getPackageName());
if (p1 == null || ps1 == null || p2 == null || ps2 == null) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
if (shouldFilterApplicationLocked(ps1, callingUid, callingUserId)
|| shouldFilterApplicationLocked(ps2, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
return checkSignaturesInternal(p1.getSigningDetails(), p2.getSigningDetails());
}
}
@Override
public int checkUidSignatures(int uid1, int uid2) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
// Map to base uids.
final int appId1 = UserHandle.getAppId(uid1);
final int appId2 = UserHandle.getAppId(uid2);
// reader
synchronized (mLock) {
SigningDetails p1SigningDetails;
SigningDetails p2SigningDetails;
Object obj = mSettings.getSettingLPr(appId1);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
p1SigningDetails = ((SharedUserSetting) obj).signatures.mSigningDetails;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
p1SigningDetails = ps.signatures.mSigningDetails;
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
obj = mSettings.getSettingLPr(appId2);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
p2SigningDetails = ((SharedUserSetting) obj).signatures.mSigningDetails;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
p2SigningDetails = ps.signatures.mSigningDetails;
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
return checkSignaturesInternal(p1SigningDetails, p2SigningDetails);
}
}
private int checkSignaturesInternal(SigningDetails p1SigningDetails,
SigningDetails p2SigningDetails) {
if (p1SigningDetails == null) {
return p2SigningDetails == null
? PackageManager.SIGNATURE_NEITHER_SIGNED
: PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
}
if (p2SigningDetails == null) {
return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
}
int result = compareSignatures(p1SigningDetails.signatures, p2SigningDetails.signatures);
if (result == PackageManager.SIGNATURE_MATCH) {
return result;
}
// To support backwards compatibility with clients of this API expecting pre-key
// rotation results if either of the packages has a signing lineage the oldest signer
// in the lineage is used for signature verification.
if (p1SigningDetails.hasPastSigningCertificates()
|| p2SigningDetails.hasPastSigningCertificates()) {
Signature[] p1Signatures = p1SigningDetails.hasPastSigningCertificates()
? new Signature[]{p1SigningDetails.pastSigningCertificates[0]}
: p1SigningDetails.signatures;
Signature[] p2Signatures = p2SigningDetails.hasPastSigningCertificates()
? new Signature[]{p2SigningDetails.pastSigningCertificates[0]}
: p2SigningDetails.signatures;
result = compareSignatures(p1Signatures, p2Signatures);
}
return result;
}
@Override
public boolean hasSigningCertificate(
String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
synchronized (mLock) {
final AndroidPackage p = mPackages.get(packageName);
final PackageSetting ps = getPackageSetting(p.getPackageName());
if (p == null || ps == null) {
return false;
}
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return false;
}
switch (type) {
case CERT_INPUT_RAW_X509:
return p.getSigningDetails().hasCertificate(certificate);
case CERT_INPUT_SHA256:
return p.getSigningDetails().hasSha256Certificate(certificate);
default:
return false;
}
}
}
@Override
public boolean hasUidSigningCertificate(
int uid, byte[] certificate, @PackageManager.CertificateInputType int type) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
// Map to base uids.
final int appId = UserHandle.getAppId(uid);
// reader
synchronized (mLock) {
final PackageParser.SigningDetails signingDetails;
final Object obj = mSettings.getSettingLPr(appId);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
if (isCallerInstantApp) {
return false;
}
signingDetails = ((SharedUserSetting)obj).signatures.mSigningDetails;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return false;
}
signingDetails = ps.signatures.mSigningDetails;
} else {
return false;
}
} else {
return false;
}
switch (type) {
case CERT_INPUT_RAW_X509:
return signingDetails.hasCertificate(certificate);
case CERT_INPUT_SHA256:
return signingDetails.hasSha256Certificate(certificate);
default:
return false;
}
}
}
/**
* If the database version for this type of package (internal storage or
* external storage) is less than the version where package signatures
* were updated, return true.
*/
private boolean isCompatSignatureUpdateNeeded(AndroidPackage pkg) {
return isCompatSignatureUpdateNeeded(getSettingsVersionForPackage(pkg));
}
private static boolean isCompatSignatureUpdateNeeded(VersionInfo ver) {
return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY;
}
private boolean isRecoverSignatureUpdateNeeded(AndroidPackage pkg) {
return isRecoverSignatureUpdateNeeded(getSettingsVersionForPackage(pkg));
}
private static boolean isRecoverSignatureUpdateNeeded(VersionInfo ver) {
return ver.databaseVersion < DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
}
@Override
public List<String> getAllPackages() {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
synchronized (mLock) {
if (canViewInstantApps(callingUid, callingUserId)) {
return new ArrayList<>(mPackages.keySet());
}
final String instantAppPkgName = getInstantAppPackageName(callingUid);
final List<String> result = new ArrayList<>();
if (instantAppPkgName != null) {
// caller is an instant application; filter unexposed applications
for (AndroidPackage pkg : mPackages.values()) {
if (!pkg.isVisibleToInstantApps()) {
continue;
}
result.add(pkg.getPackageName());
}
} else {
// caller is a normal application; filter instant applications
for (AndroidPackage pkg : mPackages.values()) {
final PackageSetting ps = getPackageSetting(pkg.getPackageName());
if (ps != null
&& ps.getInstantApp(callingUserId)
&& !mInstantAppRegistry.isInstantAccessGranted(
callingUserId, UserHandle.getAppId(callingUid), ps.appId)) {
continue;
}
result.add(pkg.getPackageName());
}
}
return result;
}
}
/**
* <em>IMPORTANT:</em> Not all packages returned by this method may be known
* to the system. There are two conditions in which this may occur:
* <ol>
* <li>The package is on adoptable storage and the device has been removed</li>
* <li>The package is being removed and the internal structures are partially updated</li>
* </ol>
* The second is an artifact of the current data structures and should be fixed. See
* b/111075456 for one such instance.
*/
@Override
public String[] getPackagesForUid(int uid) {
return getPackagesForUidInternal(uid, Binder.getCallingUid());
}
private String[] getPackagesForUidInternal(int uid, int callingUid) {
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
final int userId = UserHandle.getUserId(uid);
final int appId = UserHandle.getAppId(uid);
// reader
synchronized (mLock) {
final Object obj = mSettings.getSettingLPr(appId);
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
return null;
}
final SharedUserSetting sus = (SharedUserSetting) obj;
final int N = sus.packages.size();
String[] res = new String[N];
final Iterator<PackageSetting> it = sus.packages.iterator();
int i = 0;
while (it.hasNext()) {
PackageSetting ps = it.next();
if (ps.getInstalled(userId)) {
res[i++] = ps.name;
}
}
return ArrayUtils.trimToSize(res, i);
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (ps.getInstalled(userId)
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
return new String[]{ps.name};
}
}
}
return null;
}
@Override
public String getNameForUid(int uid) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return null;
}
final int appId = UserHandle.getAppId(uid);
synchronized (mLock) {
final Object obj = mSettings.getSettingLPr(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.name + ":" + sus.userId;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplicationLocked(
ps, callingUid, UserHandle.getUserId(callingUid))) {
return null;
}
return ps.name;
}
return null;
}
}
@Override
public String[] getNamesForUids(int[] uids) {
if (uids == null || uids.length == 0) {
return null;
}
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return null;
}
final String[] names = new String[uids.length];
synchronized (mLock) {
for (int i = uids.length - 1; i >= 0; i--) {
final int appId = UserHandle.getAppId(uids[i]);
final Object obj = mSettings.getSettingLPr(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
names[i] = "shared:" + sus.name;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplicationLocked(
ps, callingUid, UserHandle.getUserId(callingUid))) {
names[i] = null;
} else {
names[i] = ps.name;
}
} else {
names[i] = null;
}
}
}
return names;
}
@Override
public int getUidForSharedUser(String sharedUserName) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return -1;
}
if (sharedUserName == null) {
return -1;
}
// reader
synchronized (mLock) {
SharedUserSetting suid;
try {
suid = mSettings.getSharedUserLPw(sharedUserName, 0, 0, false);
if (suid != null) {
return suid.userId;
}
} catch (PackageManagerException ignore) {
// can't happen, but, still need to catch it
}
return -1;
}
}
@Override
public int getFlagsForUid(int uid) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return 0;
}
final int appId = UserHandle.getAppId(uid);
synchronized (mLock) {
final Object obj = mSettings.getSettingLPr(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.pkgFlags;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplicationLocked(
ps, callingUid, UserHandle.getUserId(callingUid))) {
return 0;
}
return ps.pkgFlags;
}
}
return 0;
}
@Override
public int getPrivateFlagsForUid(int uid) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return 0;
}
final int appId = UserHandle.getAppId(uid);
synchronized (mLock) {
final Object obj = mSettings.getSettingLPr(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.pkgPrivateFlags;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplicationLocked(
ps, callingUid, UserHandle.getUserId(callingUid))) {
return 0;
}
return ps.pkgPrivateFlags;
}
}
return 0;
}
@Override
public boolean isUidPrivileged(int uid) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return false;
}
final int appId = UserHandle.getAppId(uid);
// reader
synchronized (mLock) {
final Object obj = mSettings.getSettingLPr(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
final Iterator<PackageSetting> it = sus.packages.iterator();
while (it.hasNext()) {
if (it.next().isPrivileged()) {
return true;
}
}
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
return ps.isPrivileged();
}
}
return false;
}
// NOTE: Can't remove due to unsupported app usage
@Override
public String[] getAppOpPermissionPackages(String permName) {
try {
// Because this is accessed via the package manager service AIDL,
// go through the permission manager service AIDL
return mPermissionManagerService.getAppOpPermissionPackages(permName);
} catch (RemoteException ignore) { }
return null;
}
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
return resolveIntentInternal(intent, resolvedType, flags, 0 /*privateResolveFlags*/,
userId, false, Binder.getCallingUid());
}
/**
* Normally instant apps can only be resolved when they're visible to the caller.
* However, if {@code resolveForStart} is {@code true}, all instant apps are visible
* since we need to allow the system to start any installed application.
*/
private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType, int flags,
@PrivateResolveFlags int privateResolveFlags, int userId, boolean resolveForStart,
int filterCallingUid) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
flags));
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
flags, privateResolveFlags, filterCallingUid, userId, resolveForStart,
true /*allowDynamicSplits*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
final boolean queryMayBeFiltered =
UserHandle.getAppId(filterCallingUid) >= Process.FIRST_APPLICATION_UID
&& !resolveForStart;
final ResolveInfo bestChoice =
chooseBestActivity(
intent, resolvedType, flags, privateResolveFlags, query, userId,
queryMayBeFiltered);
final boolean nonBrowserOnly =
(privateResolveFlags & PackageManagerInternal.RESOLVE_NON_BROWSER_ONLY) != 0;
if (nonBrowserOnly && bestChoice != null && bestChoice.handleAllWebDataURI) {
return null;
}
return bestChoice;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
@Override
public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) {
if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID)) {
throw new SecurityException(
"findPersistentPreferredActivity can only be run by the system");
}
if (!mUserManager.exists(userId)) {
return null;
}
final int callingUid = Binder.getCallingUid();
intent = updateIntentForResolve(intent);
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
final int flags = updateFlagsForResolve(
0, userId, callingUid, false /*includeInstantApps*/,
isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, 0));
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
userId);
synchronized (mLock) {
return findPersistentPreferredActivityLP(intent, resolvedType, flags, query, false,
userId);
}
}
@Override
public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
IntentFilter filter, int match, ComponentName activity) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return;
}
final int userId = UserHandle.getCallingUserId();
if (DEBUG_PREFERRED) {
Log.v(TAG, "setLastChosenActivity intent=" + intent
+ " resolvedType=" + resolvedType
+ " flags=" + flags
+ " filter=" + filter
+ " match=" + match
+ " activity=" + activity);
filter.dump(new PrintStreamPrinter(System.out), " ");
}
intent.setComponent(null);
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
userId);
// Find any earlier preferred or last chosen entries and nuke them
findPreferredActivityNotLocked(
intent, resolvedType, flags, query, 0, false, true, false, userId);
// Add the new activity as the last chosen for this filter
addPreferredActivityInternal(filter, match, null, activity, false, userId,
"Setting last chosen");
}
@Override
public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
final int userId = UserHandle.getCallingUserId();
if (DEBUG_PREFERRED) Log.v(TAG, "Querying last chosen activity for " + intent);
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
userId);
return findPreferredActivityNotLocked(
intent, resolvedType, flags, query, 0, false, false, false, userId);
}
/**
* Returns whether or not instant apps have been disabled remotely.
*/
private boolean areWebInstantAppsDisabled(int userId) {
return mWebInstantAppsDisabled.get(userId);
}
private boolean isInstantAppResolutionAllowed(
Intent intent, List<ResolveInfo> resolvedActivities, int userId,
boolean skipPackageCheck) {
if (mInstantAppResolverConnection == null) {
return false;
}
if (mInstantAppInstallerActivity == null) {
return false;
}
if (intent.getComponent() != null) {
return false;
}
if ((intent.getFlags() & Intent.FLAG_IGNORE_EPHEMERAL) != 0) {
return false;
}
if (!skipPackageCheck && intent.getPackage() != null) {
return false;
}
if (!intent.isWebIntent()) {
// for non web intents, we should not resolve externally if an app already exists to
// handle it or if the caller didn't explicitly request it.
if ((resolvedActivities != null && resolvedActivities.size() != 0)
|| (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) {
return false;
}
} else {
if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) {
return false;
} else if (areWebInstantAppsDisabled(userId)) {
return false;
}
}
// Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
// Or if there's already an ephemeral app installed that handles the action
synchronized (mLock) {
final int count = (resolvedActivities == null ? 0 : resolvedActivities.size());
for (int n = 0; n < count; n++) {
final ResolveInfo info = resolvedActivities.get(n);
final String packageName = info.activityInfo.packageName;
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
// only check domain verification status if the app is not a browser
if (!info.handleAllWebDataURI) {
// Try to get the status from User settings first
final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
final int status = (int) (packedStatus >> 32);
if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
|| status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
if (DEBUG_INSTANT) {
Slog.v(TAG, "DENY instant app;"
+ " pkg: " + packageName + ", status: " + status);
}
return false;
}
}
if (ps.getInstantApp(userId)) {
if (DEBUG_INSTANT) {
Slog.v(TAG, "DENY instant app installed;"
+ " pkg: " + packageName);
}
return false;
}
}
}
}
// We've exhausted all ways to deny ephemeral application; let the system look for them.
return true;
}
private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
Intent origIntent, String resolvedType, String callingPackage,
@Nullable String callingFeatureId, boolean isRequesterInstantApp,
Bundle verificationBundle, int userId) {
final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO,
new InstantAppRequest(responseObj, origIntent, resolvedType,
callingPackage, callingFeatureId, isRequesterInstantApp, userId, verificationBundle,
false /*resolveForStart*/, responseObj.hostDigestPrefixSecure,
responseObj.token));
mHandler.sendMessage(msg);
}
private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
int flags, int privateResolveFlags, List<ResolveInfo> query, int userId,
boolean queryMayBeFiltered) {
if (query != null) {
final int N = query.size();
if (N == 1) {
return query.get(0);
} else if (N > 1) {
final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
// If there is more than one activity with the same priority,
// then let the user decide between them.
ResolveInfo r0 = query.get(0);
ResolveInfo r1 = query.get(1);
if (DEBUG_INTENT_MATCHING || debug) {
Slog.v(TAG, r0.activityInfo.name + "=" + r0.priority + " vs "
+ r1.activityInfo.name + "=" + r1.priority);
}
// If the first activity has a higher priority, or a different
// default, then it is always desirable to pick it.
if (r0.priority != r1.priority
|| r0.preferredOrder != r1.preferredOrder
|| r0.isDefault != r1.isDefault) {
return query.get(0);
}
// If we have saved a preference for a preferred activity for
// this Intent, use that.
ResolveInfo ri = findPreferredActivityNotLocked(intent, resolvedType,
flags, query, r0.priority, true, false, debug, userId, queryMayBeFiltered);
if (ri != null) {
return ri;
}
int browserCount = 0;
for (int i = 0; i < N; i++) {
ri = query.get(i);
if (ri.handleAllWebDataURI) {
browserCount++;
}
// If we have an ephemeral app, use it
if (ri.activityInfo.applicationInfo.isInstantApp()) {
final String packageName = ri.activityInfo.packageName;
final PackageSetting ps = mSettings.mPackages.get(packageName);
final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
final int status = (int)(packedStatus >> 32);
if (status != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
return ri;
}
}
}
if ((privateResolveFlags
& PackageManagerInternal.RESOLVE_NON_RESOLVER_ONLY) != 0) {
return null;
}
ri = new ResolveInfo(mResolveInfo);
// if all resolve options are browsers, mark the resolver's info as if it were
// also a browser.
ri.handleAllWebDataURI = browserCount == N;
ri.activityInfo = new ActivityInfo(ri.activityInfo);
ri.activityInfo.labelRes = ResolverActivity.getLabelRes(intent.getAction());
// If all of the options come from the same package, show the application's
// label and icon instead of the generic resolver's.
// Some calls like Intent.resolveActivityInfo query the ResolveInfo from here
// and then throw away the ResolveInfo itself, meaning that the caller loses
// the resolvePackageName. Therefore the activityInfo.labelRes above provides
// a fallback for this case; we only set the target package's resources on
// the ResolveInfo, not the ActivityInfo.
final String intentPackage = intent.getPackage();
if (!TextUtils.isEmpty(intentPackage) && allHavePackage(query, intentPackage)) {
final ApplicationInfo appi = query.get(0).activityInfo.applicationInfo;
ri.resolvePackageName = intentPackage;
if (userNeedsBadging(userId)) {
ri.noResourceId = true;
} else {
ri.icon = appi.icon;
}
ri.iconResourceId = appi.icon;
ri.labelRes = appi.labelRes;
}
ri.activityInfo.applicationInfo = new ApplicationInfo(
ri.activityInfo.applicationInfo);
if (userId != 0) {
ri.activityInfo.applicationInfo.uid = UserHandle.getUid(userId,
UserHandle.getAppId(ri.activityInfo.applicationInfo.uid));
}
// Make sure that the resolver is displayable in car mode
if (ri.activityInfo.metaData == null) ri.activityInfo.metaData = new Bundle();
ri.activityInfo.metaData.putBoolean(Intent.METADATA_DOCK_HOME, true);
return ri;
}
}
return null;
}
/**
* Return true if the given list is not empty and all of its contents have
* an activityInfo with the given package name.
*/
private boolean allHavePackage(List<ResolveInfo> list, String packageName) {
if (ArrayUtils.isEmpty(list)) {
return false;
}
for (int i = 0, N = list.size(); i < N; i++) {
final ResolveInfo ri = list.get(i);
final ActivityInfo ai = ri != null ? ri.activityInfo : null;
if (ai == null || !packageName.equals(ai.packageName)) {
return false;
}
}
return true;
}
/**
* From Android R, camera intents have to match system apps. The only exception to this is if
* the DPC has set the camera persistent preferred activity. This case was introduced
* because it is important that the DPC has the ability to set both system and non-system
* camera persistent preferred activities.
*
* @return {@code true} if the intent is a camera intent and the persistent preferred
* activity was not set by the DPC.
*/
@GuardedBy("mLock")
private boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
String resolvedType, int flags) {
return intent.isImplicitImageCaptureIntent() && !isPersistentPreferredActivitySetByDpm(
intent, userId, resolvedType, flags);
}
private boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId,
String resolvedType, int flags) {
PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities
.get(userId);
//TODO(b/158003772): Remove double query
List<PersistentPreferredActivity> pprefs = ppir != null
? ppir.queryIntent(intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
userId)
: new ArrayList<>();
for (PersistentPreferredActivity ppa : pprefs) {
if (ppa.mIsSetByDpm) {
return true;
}
}
return false;
}
@GuardedBy("mLock")
private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType,
int flags, List<ResolveInfo> query, boolean debug, int userId) {
final int N = query.size();
PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities
.get(userId);
// Get the list of persistent preferred activities that handle the intent
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for presistent preferred activities...");
List<PersistentPreferredActivity> pprefs = ppir != null
? ppir.queryIntent(intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
userId)
: null;
if (pprefs != null && pprefs.size() > 0) {
final int M = pprefs.size();
for (int i=0; i<M; i++) {
final PersistentPreferredActivity ppa = pprefs.get(i);
if (DEBUG_PREFERRED || debug) {
Slog.v(TAG, "Checking PersistentPreferredActivity ds="
+ (ppa.countDataSchemes() > 0 ? ppa.getDataScheme(0) : "<none>")
+ "\n component=" + ppa.mComponent);
ppa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
}
final ActivityInfo ai = getActivityInfo(ppa.mComponent,
flags | MATCH_DISABLED_COMPONENTS, userId);
if (DEBUG_PREFERRED || debug) {
Slog.v(TAG, "Found persistent preferred activity:");
if (ai != null) {
ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
} else {
Slog.v(TAG, " null");
}
}
if (ai == null) {
// This previously registered persistent preferred activity
// component is no longer known. Ignore it and do NOT remove it.
continue;
}
for (int j=0; j<N; j++) {
final ResolveInfo ri = query.get(j);
if (!ri.activityInfo.applicationInfo.packageName
.equals(ai.applicationInfo.packageName)) {
continue;
}
if (!ri.activityInfo.name.equals(ai.name)) {
continue;
}
// Found a persistent preference that can handle the intent.
if (DEBUG_PREFERRED || debug) {
Slog.v(TAG, "Returning persistent preferred activity: " +
ri.activityInfo.packageName + "/" + ri.activityInfo.name);
}
return ri;
}
}
}
return null;
}
private boolean isHomeIntent(Intent intent) {
return ACTION_MAIN.equals(intent.getAction())
&& intent.hasCategory(CATEGORY_HOME)
&& intent.hasCategory(CATEGORY_DEFAULT);
}
ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
List<ResolveInfo> query, int priority, boolean always,
boolean removeMatches, boolean debug, int userId) {
return findPreferredActivityNotLocked(
intent, resolvedType, flags, query, priority, always, removeMatches, debug, userId,
UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID);
}
// TODO: handle preferred activities missing while user has amnesia
/** <b>must not hold {@link #mLock}</b> */
ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
List<ResolveInfo> query, int priority, boolean always,
boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
if (Thread.holdsLock(mLock)) {
Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
+ " is holding mLock", new Throwable());
}
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
// Do NOT hold the packages lock; this calls up into the settings provider which
// could cause a deadlock.
final boolean isDeviceProvisioned =
android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1;
flags = updateFlagsForResolve(
flags, userId, callingUid, false /*includeInstantApps*/,
isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
flags));
intent = updateIntentForResolve(intent);
// writer
synchronized (mLock) {
// Try to find a matching persistent preferred activity.
ResolveInfo pri = findPersistentPreferredActivityLP(intent, resolvedType, flags, query,
debug, userId);
// If a persistent preferred activity matched, use it.
if (pri != null) {
return pri;
}
PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
// Get the list of preferred activities that handle the intent
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities...");
List<PreferredActivity> prefs = pir != null
? pir.queryIntent(intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
userId)
: null;
if (prefs != null && prefs.size() > 0) {
boolean changed = false;
try {
// First figure out how good the original match set is.
// We will only allow preferred activities that came
// from the same match quality.
int match = 0;
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Figuring out best match...");
final int N = query.size();
for (int j=0; j<N; j++) {
final ResolveInfo ri = query.get(j);
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Match for " + ri.activityInfo
+ ": 0x" + Integer.toHexString(match));
if (ri.match > match) {
match = ri.match;
}
}
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Best match: 0x"
+ Integer.toHexString(match));
match &= IntentFilter.MATCH_CATEGORY_MASK;
final int M = prefs.size();
for (int i=0; i<M; i++) {
final PreferredActivity pa = prefs.get(i);
if (DEBUG_PREFERRED || debug) {
Slog.v(TAG, "Checking PreferredActivity ds="
+ (pa.countDataSchemes() > 0 ? pa.getDataScheme(0) : "<none>")
+ "\n component=" + pa.mPref.mComponent);
pa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
}
if (pa.mPref.mMatch != match) {
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping bad match "
+ Integer.toHexString(pa.mPref.mMatch));
continue;
}
// If it's not an "always" type preferred activity and that's what we're
// looking for, skip it.
if (always && !pa.mPref.mAlways) {
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping mAlways=false entry");
continue;
}
final ActivityInfo ai = getActivityInfo(
pa.mPref.mComponent, flags | MATCH_DISABLED_COMPONENTS
| MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
userId);
if (DEBUG_PREFERRED || debug) {
Slog.v(TAG, "Found preferred activity:");
if (ai != null) {
ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
} else {
Slog.v(TAG, " null");
}
}
final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent)
&& !isDeviceProvisioned;
final boolean allowSetMutation = !excludeSetupWizardHomeActivity
&& !queryMayBeFiltered;
if (ai == null) {
// Do not remove launcher's preferred activity during SetupWizard
// due to it may not install yet
if (!allowSetMutation) {
continue;
}
// This previously registered preferred activity
// component is no longer known. Most likely an update
// to the app was installed and in the new version this
// component no longer exists. Clean it up by removing
// it from the preferred activities list, and skip it.
Slog.w(TAG, "Removing dangling preferred activity: "
+ pa.mPref.mComponent);
pir.removeFilter(pa);
changed = true;
continue;
}
for (int j=0; j<N; j++) {
final ResolveInfo ri = query.get(j);
if (!ri.activityInfo.applicationInfo.packageName
.equals(ai.applicationInfo.packageName)) {
continue;
}
if (!ri.activityInfo.name.equals(ai.name)) {
continue;
}
if (removeMatches && allowSetMutation) {
pir.removeFilter(pa);
changed = true;
if (DEBUG_PREFERRED) {
Slog.v(TAG, "Removing match " + pa.mPref.mComponent);
}
break;
}
// Okay we found a previously set preferred or last chosen app.
// If the result set is different from when this
// was created, and is not a subset of the preferred set, we need to
// clear it and re-ask the user their preference, if we're looking for
// an "always" type entry.
if (always && !pa.mPref.sameSet(query, excludeSetupWizardHomeActivity)) {
if (pa.mPref.isSuperset(query, excludeSetupWizardHomeActivity)) {
if (allowSetMutation) {
// some components of the set are no longer present in
// the query, but the preferred activity can still be reused
if (DEBUG_PREFERRED) {
Slog.i(TAG, "Result set changed, but PreferredActivity"
+ " is still valid as only non-preferred"
+ " components were removed for " + intent
+ " type " + resolvedType);
}
// remove obsolete components and re-add the up-to-date
// filter
PreferredActivity freshPa = new PreferredActivity(pa,
pa.mPref.mMatch,
pa.mPref.discardObsoleteComponents(query),
pa.mPref.mComponent,
pa.mPref.mAlways);
pir.removeFilter(pa);
pir.addFilter(freshPa);
changed = true;
} else {
if (DEBUG_PREFERRED) {
Slog.i(TAG, "Do not remove preferred activity");
}
}
} else {
if (allowSetMutation) {
Slog.i(TAG,
"Result set changed, dropping preferred activity "
+ "for " + intent + " type "
+ resolvedType);
if (DEBUG_PREFERRED) {
Slog.v(TAG,
"Removing preferred activity since set changed "
+ pa.mPref.mComponent);
}
pir.removeFilter(pa);
// Re-add the filter as a "last chosen" entry (!always)
PreferredActivity lastChosen = new PreferredActivity(
pa, pa.mPref.mMatch, null, pa.mPref.mComponent,
false);
pir.addFilter(lastChosen);
changed = true;
}
return null;
}
}
// Yay! Either the set matched or we're looking for the last chosen
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Returning preferred activity: "
+ ri.activityInfo.packageName + "/" + ri.activityInfo.name);
return ri;
}
}
} finally {
if (changed) {
if (DEBUG_PREFERRED) {
Slog.v(TAG, "Preferred activity bookkeeping changed; writing restrictions");
}
scheduleWritePackageRestrictionsLocked(userId);
}
}
}
}
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "No preferred activity to return");
return null;
}
/*
* Returns if intent can be forwarded from the sourceUserId to the targetUserId
*/
@Override
public boolean canForwardTo(Intent intent, String resolvedType, int sourceUserId,
int targetUserId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
List<CrossProfileIntentFilter> matches =
getMatchingCrossProfileIntentFilters(intent, resolvedType, sourceUserId);
if (matches != null) {
int size = matches.size();
for (int i = 0; i < size; i++) {
if (matches.get(i).getTargetUserId() == targetUserId) return true;
}
}
if (intent.hasWebURI()) {
// cross-profile app linking works only towards the parent.
final int callingUid = Binder.getCallingUid();
final UserInfo parent = getProfileParent(sourceUserId);
if (parent == null) {
return false;
}
synchronized (mLock) {
int flags = updateFlagsForResolve(0, parent.id, callingUid,
false /*includeInstantApps*/,
isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, parent.id,
resolvedType, 0));
CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr(
intent, resolvedType, flags, sourceUserId, parent.id);
return xpDomainInfo != null;
}
}
return false;
}
private UserInfo getProfileParent(int userId) {
final long identity = Binder.clearCallingIdentity();
try {
return mUserManager.getProfileParent(userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
private List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
String resolvedType, int userId) {
CrossProfileIntentResolver resolver = mSettings.mCrossProfileIntentResolvers.get(userId);
if (resolver != null) {
return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
}
return null;
}
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
return new ParceledListSlice<>(
queryIntentActivitiesInternal(intent, resolvedType, flags, userId));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
/**
* Returns the package name of the calling Uid if it's an instant app. If it isn't
* instant, returns {@code null}.
*/
private String getInstantAppPackageName(int callingUid) {
synchronized (mLock) {
// If the caller is an isolated app use the owner's uid for the lookup.
if (Process.isIsolated(callingUid)) {
callingUid = mIsolatedOwners.get(callingUid);
}
final int appId = UserHandle.getAppId(callingUid);
final Object obj = mSettings.getSettingLPr(appId);
if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid));
return isInstantApp ? ps.pkg.getPackageName() : null;
}
}
return null;
}
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, int userId) {
return queryIntentActivitiesInternal(
intent, resolvedType, flags, 0 /*privateResolveFlags*/, Binder.getCallingUid(),
userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
}
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags,
int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */,
"query intent activities");
final String pkgName = intent.getPackage();
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
comp != null || pkgName != null /*onlyExposedExplicitly*/,
isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
flags));
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<>(1);
final ActivityInfo ai = getActivityInfo(comp, flags, userId);
if (ai != null) {
// When specifying an explicit component, we prevent the activity from being
// used when either 1) the calling package is normal and the activity is within
// an ephemeral application or 2) the calling package is ephemeral and the
// activity is not visible to ephemeral applications.
final boolean matchInstantApp =
(flags & PackageManager.MATCH_INSTANT) != 0;
final boolean matchVisibleToInstantAppOnly =
(flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
final boolean matchExplicitlyVisibleOnly =
(flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
final boolean isCallerInstantApp =
instantAppPkgName != null;
final boolean isTargetSameInstantApp =
comp.getPackageName().equals(instantAppPkgName);
final boolean isTargetInstantApp =
(ai.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
final boolean isTargetVisibleToInstantApp =
(ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
final boolean isTargetExplicitlyVisibleToInstantApp =
isTargetVisibleToInstantApp
&& (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
final boolean isTargetHiddenFromInstantApp =
!isTargetVisibleToInstantApp
|| (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp);
final boolean blockInstantResolution =
!isTargetSameInstantApp
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
final boolean blockNormalResolution =
!resolveForStart && !isTargetInstantApp && !isCallerInstantApp
&& shouldFilterApplicationLocked(
getPackageSettingInternal(ai.applicationInfo.packageName,
Process.SYSTEM_UID), filterCallingUid, userId);
if (!blockInstantResolution && !blockNormalResolution) {
final ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
}
List<ResolveInfo> result = applyPostResolutionFilter(
list, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart,
userId, intent);
return result;
}
// reader
boolean sortResult = false;
boolean addInstant = false;
List<ResolveInfo> result;
synchronized (mLock) {
if (pkgName == null) {
List<CrossProfileIntentFilter> matchingFilters =
getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
// Check for results that need to skip the current profile.
ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent,
resolvedType, flags, userId);
if (xpResolveInfo != null) {
List<ResolveInfo> xpResult = new ArrayList<>(1);
xpResult.add(xpResolveInfo);
return applyPostResolutionFilter(
filterIfNotSystemUser(xpResult, userId), instantAppPkgName,
allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent);
}
// Check for results in the current profile.
result = filterIfNotSystemUser(mComponentResolver.queryActivities(
intent, resolvedType, flags, userId), userId);
addInstant = isInstantAppResolutionAllowed(intent, result, userId,
false /*skipPackageCheck*/);
// Check for cross profile results.
boolean hasNonNegativePriorityResult = hasNonNegativePriority(result);
xpResolveInfo = queryCrossProfileIntents(
matchingFilters, intent, resolvedType, flags, userId,
hasNonNegativePriorityResult);
if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) {
boolean isVisibleToUser = filterIfNotSystemUser(
Collections.singletonList(xpResolveInfo), userId).size() > 0;
if (isVisibleToUser) {
result.add(xpResolveInfo);
sortResult = true;
}
}
if (intent.hasWebURI()) {
CrossProfileDomainInfo xpDomainInfo = null;
final UserInfo parent = getProfileParent(userId);
if (parent != null) {
xpDomainInfo = getCrossProfileDomainPreferredLpr(intent, resolvedType,
flags, userId, parent.id);
}
if (xpDomainInfo != null) {
if (xpResolveInfo != null) {
// If we didn't remove it, the cross-profile ResolveInfo would be twice
// in the result.
result.remove(xpResolveInfo);
}
if (result.size() == 0 && !addInstant) {
// No result in current profile, but found candidate in parent user.
// And we are not going to add emphemeral app, so we can return the
// result straight away.
result.add(xpDomainInfo.resolveInfo);
return applyPostResolutionFilter(result, instantAppPkgName,
allowDynamicSplits, filterCallingUid, resolveForStart, userId,
intent);
}
} else if (result.size() <= 1 && !addInstant) {
// No result in parent user and <= 1 result in current profile, and we
// are not going to add emphemeral app, so we can return the result without
// further processing.
return applyPostResolutionFilter(result, instantAppPkgName,
allowDynamicSplits, filterCallingUid, resolveForStart, userId,
intent);
}
// We have more than one candidate (combining results from current and parent
// profile), so we need filtering and sorting.
result = filterCandidatesWithDomainPreferredActivitiesLPr(
intent, flags, result, xpDomainInfo, userId);
sortResult = true;
}
} else {
final PackageSetting setting =
getPackageSettingInternal(pkgName, Process.SYSTEM_UID);
result = null;
if (setting != null && setting.pkg != null && (resolveForStart
|| !shouldFilterApplicationLocked(setting, filterCallingUid, userId))) {
result = filterIfNotSystemUser(mComponentResolver.queryActivities(
intent, resolvedType, flags, setting.pkg.getActivities(), userId),
userId);
}
if (result == null || result.size() == 0) {
// the caller wants to resolve for a particular package; however, there
// were no installed results, so, try to find an ephemeral result
addInstant = isInstantAppResolutionAllowed(
intent, null /*result*/, userId, true /*skipPackageCheck*/);
if (result == null) {
result = new ArrayList<>();
}
}
}
}
if (addInstant) {
String callingPkgName = getInstantAppPackageName(filterCallingUid);
boolean isRequesterInstantApp = isInstantApp(callingPkgName, userId);
result = maybeAddInstantAppInstaller(result, intent, resolvedType, flags, userId,
resolveForStart, isRequesterInstantApp);
}
if (sortResult) {
Collections.sort(result, RESOLVE_PRIORITY_SORTER);
}
return applyPostResolutionFilter(
result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart,
userId, intent);
}
private List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result, Intent intent,
String resolvedType, int flags, int userId, boolean resolveForStart,
boolean isRequesterInstantApp) {
// first, check to see if we've got an instant app already installed
final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0;
ResolveInfo localInstantApp = null;
boolean blockResolution = false;
if (!alreadyResolvedLocally) {
final List<ResolveInfo> instantApps = mComponentResolver.queryActivities(
intent,
resolvedType,
flags
| PackageManager.GET_RESOLVED_FILTER
| PackageManager.MATCH_INSTANT
| PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY,
userId);
for (int i = instantApps.size() - 1; i >= 0; --i) {
final ResolveInfo info = instantApps.get(i);
final String packageName = info.activityInfo.packageName;
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps.getInstantApp(userId)) {
final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
final int status = (int)(packedStatus >> 32);
if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
// there's a local instant application installed, but, the user has
// chosen to never use it; skip resolution and don't acknowledge
// an instant application is even available
if (DEBUG_INSTANT) {
Slog.v(TAG, "Instant app marked to never run; pkg: " + packageName);
}
blockResolution = true;
break;
} else {
// we have a locally installed instant application; skip resolution
// but acknowledge there's an instant application available
if (DEBUG_INSTANT) {
Slog.v(TAG, "Found installed instant app; pkg: " + packageName);
}
localInstantApp = info;
break;
}
}
}
}
// no app installed, let's see if one's available
AuxiliaryResolveInfo auxiliaryResponse = null;
if (!blockResolution) {
if (localInstantApp == null) {
// we don't have an instant app locally, resolve externally
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
String token = UUID.randomUUID().toString();
InstantAppDigest digest = InstantAppResolver.parseDigest(intent);
final InstantAppRequest requestObject = new InstantAppRequest(null /*responseObj*/,
intent /*origIntent*/, resolvedType, null /*callingPackage*/,
null /*callingFeatureId*/, isRequesterInstantApp, userId,
null /*verificationBundle*/, resolveForStart,
digest.getDigestPrefixSecure(), token);
auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(
mInstantAppResolverConnection, requestObject);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
} else {
// we have an instant application locally, but, we can't admit that since
// callers shouldn't be able to determine prior browsing. create a dummy
// auxiliary response so the downstream code behaves as if there's an
// instant application available externally. when it comes time to start
// the instant application, we'll do the right thing.
final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo;
auxiliaryResponse = new AuxiliaryResolveInfo(null /* failureActivity */,
ai.packageName, ai.longVersionCode, null /* splitName */);
}
}
if (intent.isWebIntent() && auxiliaryResponse == null) {
return result;
}
final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName);
if (ps == null
|| !ps.readUserState(userId).isEnabled(mInstantAppInstallerActivity, 0)) {
return result;
}
final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo(
mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId);
ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
// add a non-generic filter
ephemeralInstaller.filter = new IntentFilter();
if (intent.getAction() != null) {
ephemeralInstaller.filter.addAction(intent.getAction());
}
if (intent.getData() != null && intent.getData().getPath() != null) {
ephemeralInstaller.filter.addDataPath(
intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL);
}
ephemeralInstaller.isInstantAppAvailable = true;
// make sure this resolver is the default
ephemeralInstaller.isDefault = true;
ephemeralInstaller.auxiliaryInfo = auxiliaryResponse;
if (DEBUG_INSTANT) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
result.add(ephemeralInstaller);
return result;
}
private static class CrossProfileDomainInfo {
/* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */
ResolveInfo resolveInfo;
/* Best domain verification status of the activities found in the other profile */
int bestDomainVerificationStatus;
}
private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
String resolvedType, int flags, int sourceUserId, int parentUserId) {
if (!mUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
sourceUserId)) {
return null;
}
List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
resolvedType, flags, parentUserId);
if (resultTargetUser == null || resultTargetUser.isEmpty()) {
return null;
}
CrossProfileDomainInfo result = null;
int size = resultTargetUser.size();
for (int i = 0; i < size; i++) {
ResolveInfo riTargetUser = resultTargetUser.get(i);
// Intent filter verification is only for filters that specify a host. So don't return
// those that handle all web uris.
if (riTargetUser.handleAllWebDataURI) {
continue;
}
String packageName = riTargetUser.activityInfo.packageName;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) {
continue;
}
long verificationState = getDomainVerificationStatusLPr(ps, parentUserId);
int status = (int)(verificationState >> 32);
if (result == null) {
result = new CrossProfileDomainInfo();
result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(),
sourceUserId, parentUserId);
result.bestDomainVerificationStatus = status;
} else {
result.bestDomainVerificationStatus = bestDomainVerificationStatus(status,
result.bestDomainVerificationStatus);
}
}
// Don't consider matches with status NEVER across profiles.
if (result != null && result.bestDomainVerificationStatus
== INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
return null;
}
return result;
}
/**
* Verification statuses are ordered from the worse to the best, except for
* INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse.
*/
private int bestDomainVerificationStatus(int status1, int status2) {
if (status1 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
return status2;
}
if (status2 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
return status1;
}
return (int) MathUtils.max(status1, status2);
}
private boolean isUserEnabled(int userId) {
long callingId = Binder.clearCallingIdentity();
try {
UserInfo userInfo = mUserManager.getUserInfo(userId);
return userInfo != null && userInfo.isEnabled();
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
/**
* Filter out activities with systemUserOnly flag set, when current user is not System.
*
* @return filtered list
*/
private List<ResolveInfo> filterIfNotSystemUser(List<ResolveInfo> resolveInfos, int userId) {
if (userId == UserHandle.USER_SYSTEM) {
return resolveInfos;
}
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
ResolveInfo info = resolveInfos.get(i);
if ((info.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
resolveInfos.remove(i);
}
}
return resolveInfos;
}
/**
* Filters out ephemeral activities.
* <p>When resolving for an ephemeral app, only activities that 1) are defined in the
* ephemeral app or 2) marked with {@code visibleToEphemeral} are returned.
*
* @param resolveInfos The pre-filtered list of resolved activities
* @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering
* is performed.
* @param intent
* @return A filtered list of resolved activities.
*/
private List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
boolean resolveForStart, int userId, Intent intent) {
final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
// remove locally resolved instant app web results when disabled
if (info.isInstantAppAvailable && blockInstant) {
resolveInfos.remove(i);
continue;
}
// allow activities that are defined in the provided package
if (allowDynamicSplits
&& info.activityInfo != null
&& info.activityInfo.splitName != null
&& !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames,
info.activityInfo.splitName)) {
if (mInstantAppInstallerActivity == null) {
if (DEBUG_INSTALL) {
Slog.v(TAG, "No installer - not adding it to the ResolveInfo list");
}
resolveInfos.remove(i);
continue;
}
if (blockInstant && isInstantApp(info.activityInfo.packageName, userId)) {
resolveInfos.remove(i);
continue;
}
// requested activity is defined in a split that hasn't been installed yet.
// add the installer to the resolve list
if (DEBUG_INSTALL) {
Slog.v(TAG, "Adding installer to the ResolveInfo list");
}
final ResolveInfo installerInfo = new ResolveInfo(
mInstantAppInstallerInfo);
final ComponentName installFailureActivity = findInstallFailureActivity(
info.activityInfo.packageName, filterCallingUid, userId);
installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
installFailureActivity,
info.activityInfo.packageName,
info.activityInfo.applicationInfo.longVersionCode,
info.activityInfo.splitName);
// add a non-generic filter
installerInfo.filter = new IntentFilter();
// This resolve info may appear in the chooser UI, so let us make it
// look as the one it replaces as far as the user is concerned which
// requires loading the correct label and icon for the resolve info.
installerInfo.resolvePackageName = info.getComponentInfo().packageName;
installerInfo.labelRes = info.resolveLabelResId();
installerInfo.icon = info.resolveIconResId();
installerInfo.isInstantAppAvailable = true;
resolveInfos.set(i, installerInfo);
continue;
}
if (ephemeralPkgName == null) {
// caller is a full app
SettingBase callingSetting =
mSettings.getSettingLPr(UserHandle.getAppId(filterCallingUid));
PackageSetting resolvedSetting =
getPackageSettingInternal(info.activityInfo.packageName, 0);
if (resolveForStart
|| !mAppsFilter.shouldFilterApplication(
filterCallingUid, callingSetting, resolvedSetting, userId)) {
continue;
}
} else if (ephemeralPkgName.equals(info.activityInfo.packageName)) {
// caller is same app; don't need to apply any other filtering
continue;
} else if (resolveForStart
&& (intent.isWebIntent()
|| (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0)
&& intent.getPackage() == null
&& intent.getComponent() == null) {
// ephemeral apps can launch other ephemeral apps indirectly
continue;
} else if (((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)
&& !info.activityInfo.applicationInfo.isInstantApp()) {
// allow activities that have been explicitly exposed to ephemeral apps
continue;
}
resolveInfos.remove(i);
}
return resolveInfos;
}
/**
* Returns the activity component that can handle install failures.
* <p>By default, the instant application installer handles failures. However, an
* application may want to handle failures on its own. Applications do this by
* creating an activity with an intent filter that handles the action
* {@link Intent#ACTION_INSTALL_FAILURE}.
*/
private @Nullable ComponentName findInstallFailureActivity(
String packageName, int filterCallingUid, int userId) {
final Intent failureActivityIntent = new Intent(Intent.ACTION_INSTALL_FAILURE);
failureActivityIntent.setPackage(packageName);
// IMPORTANT: disallow dynamic splits to avoid an infinite loop
final List<ResolveInfo> result = queryIntentActivitiesInternal(
failureActivityIntent, null /*resolvedType*/, 0 /*flags*/,
0 /*privateResolveFlags*/, filterCallingUid, userId, false /*resolveForStart*/,
false /*allowDynamicSplits*/);
final int NR = result.size();
if (NR > 0) {
for (int i = 0; i < NR; i++) {
final ResolveInfo info = result.get(i);
if (info.activityInfo.splitName != null) {
continue;
}
return new ComponentName(packageName, info.activityInfo.name);
}
}
return null;
}
/**
* @param resolveInfos list of resolve infos in descending priority order
* @return if the list contains a resolve info with non-negative priority
*/
private boolean hasNonNegativePriority(List<ResolveInfo> resolveInfos) {
return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0;
}
private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
int userId) {
final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0;
if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) {
Slog.v(TAG, "Filtering results with preferred activities. Candidates count: " +
candidates.size());
}
final ArrayList<ResolveInfo> result = new ArrayList<>();
final ArrayList<ResolveInfo> alwaysList = new ArrayList<>();
final ArrayList<ResolveInfo> undefinedList = new ArrayList<>();
final ArrayList<ResolveInfo> alwaysAskList = new ArrayList<>();
final ArrayList<ResolveInfo> neverList = new ArrayList<>();
final ArrayList<ResolveInfo> matchAllList = new ArrayList<>();
synchronized (mLock) {
final int count = candidates.size();
// First, try to use linked apps. Partition the candidates into four lists:
// one for the final results, one for the "do not use ever", one for "undefined status"
// and finally one for "browser app type".
for (int n=0; n<count; n++) {
ResolveInfo info = candidates.get(n);
String packageName = info.activityInfo.packageName;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
// Add to the special match all list (Browser use case)
if (info.handleAllWebDataURI) {
matchAllList.add(info);
continue;
}
// Try to get the status from User settings first
long packedStatus = getDomainVerificationStatusLPr(ps, userId);
int status = (int)(packedStatus >> 32);
int linkGeneration = (int)(packedStatus & 0xFFFFFFFF);
if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
if (DEBUG_DOMAIN_VERIFICATION || debug) {
Slog.i(TAG, " + always: " + info.activityInfo.packageName
+ " : linkgen=" + linkGeneration);
}
// Use link-enabled generation as preferredOrder, i.e.
// prefer newly-enabled over earlier-enabled.
info.preferredOrder = linkGeneration;
alwaysList.add(info);
} else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
if (DEBUG_DOMAIN_VERIFICATION || debug) {
Slog.i(TAG, " + never: " + info.activityInfo.packageName);
}
neverList.add(info);
} else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
if (DEBUG_DOMAIN_VERIFICATION || debug) {
Slog.i(TAG, " + always-ask: " + info.activityInfo.packageName);
}
alwaysAskList.add(info);
} else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED ||
status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK) {
if (DEBUG_DOMAIN_VERIFICATION || debug) {
Slog.i(TAG, " + ask: " + info.activityInfo.packageName);
}
undefinedList.add(info);
}
}
}
// We'll want to include browser possibilities in a few cases
boolean includeBrowser = false;
// First try to add the "always" resolution(s) for the current user, if any
if (alwaysList.size() > 0) {
result.addAll(alwaysList);
} else {
// Add all undefined apps as we want them to appear in the disambiguation dialog.
result.addAll(undefinedList);
// Maybe add one for the other profile.
if (xpDomainInfo != null && (
xpDomainInfo.bestDomainVerificationStatus
!= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)) {
result.add(xpDomainInfo.resolveInfo);
}
includeBrowser = true;
}
// The presence of any 'always ask' alternatives means we'll also offer browsers.
// If there were 'always' entries their preferred order has been set, so we also
// back that off to make the alternatives equivalent
if (alwaysAskList.size() > 0) {
for (ResolveInfo i : result) {
i.preferredOrder = 0;
}
result.addAll(alwaysAskList);
includeBrowser = true;
}
if (includeBrowser) {
// Also add browsers (all of them or only the default one)
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.v(TAG, " ...including browsers in candidate set");
}
if ((matchFlags & MATCH_ALL) != 0) {
result.addAll(matchAllList);
} else {
// Browser/generic handling case. If there's a default browser, go straight
// to that (but only if there is no other higher-priority match).
final String defaultBrowserPackageName =
mPermissionManager.getDefaultBrowser(userId);
int maxMatchPrio = 0;
ResolveInfo defaultBrowserMatch = null;
final int numCandidates = matchAllList.size();
for (int n = 0; n < numCandidates; n++) {
ResolveInfo info = matchAllList.get(n);
// track the highest overall match priority...
if (info.priority > maxMatchPrio) {
maxMatchPrio = info.priority;
}
// ...and the highest-priority default browser match
if (info.activityInfo.packageName.equals(defaultBrowserPackageName)) {
if (defaultBrowserMatch == null
|| (defaultBrowserMatch.priority < info.priority)) {
if (debug) {
Slog.v(TAG, "Considering default browser match " + info);
}
defaultBrowserMatch = info;
}
}
}
if (defaultBrowserMatch != null
&& defaultBrowserMatch.priority >= maxMatchPrio
&& !TextUtils.isEmpty(defaultBrowserPackageName))
{
if (debug) {
Slog.v(TAG, "Default browser match " + defaultBrowserMatch);
}
result.add(defaultBrowserMatch);
} else {
result.addAll(matchAllList);
}
}
// If there is nothing selected, add all candidates and remove the ones that the user
// has explicitly put into the INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER state
if (result.size() == 0) {
result.addAll(candidates);
result.removeAll(neverList);
}
}
}
if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) {
Slog.v(TAG, "Filtered results with preferred activities. New candidates count: " +
result.size());
for (ResolveInfo info : result) {
Slog.v(TAG, " + " + info.activityInfo);
}
}
return result;
}
// Returns a packed value as a long:
//
// high 'int'-sized word: link status: undefined/ask/never/always.
// low 'int'-sized word: relative priority among 'always' results.
private long getDomainVerificationStatusLPr(PackageSetting ps, int userId) {
long result = ps.getDomainVerificationStatusForUser(userId);
// if none available, get the master status
if (result >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
if (ps.getIntentFilterVerificationInfo() != null) {
result = ((long)ps.getIntentFilterVerificationInfo().getStatus()) << 32;
}
}
return result;
}
private ResolveInfo querySkipCurrentProfileIntents(
List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
int flags, int sourceUserId) {
if (matchingFilters != null) {
int size = matchingFilters.size();
for (int i = 0; i < size; i ++) {
CrossProfileIntentFilter filter = matchingFilters.get(i);
if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0) {
// Checking if there are activities in the target user that can handle the
// intent.
ResolveInfo resolveInfo = createForwardingResolveInfo(filter, intent,
resolvedType, flags, sourceUserId);
if (resolveInfo != null) {
return resolveInfo;
}
}
}
}
return null;
}
// Return matching ResolveInfo in target user if any.
private ResolveInfo queryCrossProfileIntents(
List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
int flags, int sourceUserId, boolean matchInCurrentProfile) {
if (matchingFilters != null) {
// Two {@link CrossProfileIntentFilter}s can have the same targetUserId and
// match the same intent. For performance reasons, it is better not to
// run queryIntent twice for the same userId
SparseBooleanArray alreadyTriedUserIds = new SparseBooleanArray();
int size = matchingFilters.size();
for (int i = 0; i < size; i++) {
CrossProfileIntentFilter filter = matchingFilters.get(i);
int targetUserId = filter.getTargetUserId();
boolean skipCurrentProfile =
(filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0;
boolean skipCurrentProfileIfNoMatchFound =
(filter.getFlags() & PackageManager.ONLY_IF_NO_MATCH_FOUND) != 0;
if (!skipCurrentProfile && !alreadyTriedUserIds.get(targetUserId)
&& (!skipCurrentProfileIfNoMatchFound || !matchInCurrentProfile)) {
// Checking if there are activities in the target user that can handle the
// intent.
ResolveInfo resolveInfo = createForwardingResolveInfo(filter, intent,
resolvedType, flags, sourceUserId);
if (resolveInfo != null) return resolveInfo;
alreadyTriedUserIds.put(targetUserId, true);
}
}
}
return null;
}
/**
* If the filter's target user can handle the intent and is enabled: returns a ResolveInfo that
* will forward the intent to the filter's target user.
* Otherwise, returns null.
*/
private ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter filter, Intent intent,
String resolvedType, int flags, int sourceUserId) {
int targetUserId = filter.getTargetUserId();
List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
resolvedType, flags, targetUserId);
if (resultTargetUser != null && isUserEnabled(targetUserId)) {
// If all the matches in the target profile are suspended, return null.
for (int i = resultTargetUser.size() - 1; i >= 0; i--) {
if ((resultTargetUser.get(i).activityInfo.applicationInfo.flags
& ApplicationInfo.FLAG_SUSPENDED) == 0) {
return createForwardingResolveInfoUnchecked(filter, sourceUserId,
targetUserId);
}
}
}
return null;
}
private ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter,
int sourceUserId, int targetUserId) {
ResolveInfo forwardingResolveInfo = new ResolveInfo();
long ident = Binder.clearCallingIdentity();
boolean targetIsProfile;
try {
targetIsProfile = mUserManager.getUserInfo(targetUserId).isManagedProfile();
} finally {
Binder.restoreCallingIdentity(ident);
}
String className;
if (targetIsProfile) {
className = FORWARD_INTENT_TO_MANAGED_PROFILE;
} else {
className = FORWARD_INTENT_TO_PARENT;
}
ComponentName forwardingActivityComponentName = new ComponentName(
mAndroidApplication.packageName, className);
ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0,
sourceUserId);
if (!targetIsProfile) {
forwardingActivityInfo.showUserIcon = targetUserId;
forwardingResolveInfo.noResourceId = true;
}
forwardingResolveInfo.activityInfo = forwardingActivityInfo;
forwardingResolveInfo.priority = 0;
forwardingResolveInfo.preferredOrder = 0;
forwardingResolveInfo.match = 0;
forwardingResolveInfo.isDefault = true;
forwardingResolveInfo.filter = filter;
forwardingResolveInfo.targetUserId = targetUserId;
return forwardingResolveInfo;
}
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent,
String resolvedType, int flags, int userId) {
return new ParceledListSlice<>(queryIntentActivityOptionsInternal(caller, specifics,
specificTypes, intent, resolvedType, flags, userId));
}
private @NonNull List<ResolveInfo> queryIntentActivityOptionsInternal(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent,
String resolvedType, int flags, int userId) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
flags));
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent activity options");
final String resultsAction = intent.getAction();
final List<ResolveInfo> results = queryIntentActivitiesInternal(intent, resolvedType, flags
| PackageManager.GET_RESOLVED_FILTER, userId);
if (DEBUG_INTENT_MATCHING) {
Log.v(TAG, "Query " + intent + ": " + results);
}
int specificsPos = 0;
int N;
// todo: note that the algorithm used here is O(N^2). This
// isn't a problem in our current environment, but if we start running
// into situations where we have more than 5 or 10 matches then this
// should probably be changed to something smarter...
// First we go through and resolve each of the specific items
// that were supplied, taking care of removing any corresponding
// duplicate items in the generic resolve list.
if (specifics != null) {
for (int i=0; i<specifics.length; i++) {
final Intent sintent = specifics[i];
if (sintent == null) {
continue;
}
if (DEBUG_INTENT_MATCHING) {
Log.v(TAG, "Specific #" + i + ": " + sintent);
}
String action = sintent.getAction();
if (resultsAction != null && resultsAction.equals(action)) {
// If this action was explicitly requested, then don't
// remove things that have it.
action = null;
}
ResolveInfo ri = null;
ActivityInfo ai = null;
ComponentName comp = sintent.getComponent();
if (comp == null) {
ri = resolveIntent(
sintent,
specificTypes != null ? specificTypes[i] : null,
flags, userId);
if (ri == null) {
continue;
}
if (ri == mResolveInfo) {
// ACK! Must do something better with this.
}
ai = ri.activityInfo;
comp = new ComponentName(ai.applicationInfo.packageName,
ai.name);
} else {
ai = getActivityInfo(comp, flags, userId);
if (ai == null) {
continue;
}
}
// Look for any generic query activities that are duplicates
// of this specific one, and remove them from the results.
if (DEBUG_INTENT_MATCHING) Log.v(TAG, "Specific #" + i + ": " + ai);
N = results.size();
int j;
for (j=specificsPos; j<N; j++) {
ResolveInfo sri = results.get(j);
if ((sri.activityInfo.name.equals(comp.getClassName())
&& sri.activityInfo.applicationInfo.packageName.equals(
comp.getPackageName()))
|| (action != null && sri.filter.matchAction(action))) {
results.remove(j);
if (DEBUG_INTENT_MATCHING) Log.v(
TAG, "Removing duplicate item from " + j
+ " due to specific " + specificsPos);
if (ri == null) {
ri = sri;
}
j--;
N--;
}
}
// Add this specific item to its proper place.
if (ri == null) {
ri = new ResolveInfo();
ri.activityInfo = ai;
}
results.add(specificsPos, ri);
ri.specificIndex = i;
specificsPos++;
}
}
// Now we go through the remaining generic results and remove any
// duplicate actions that are found here.
N = results.size();
for (int i=specificsPos; i<N-1; i++) {
final ResolveInfo rii = results.get(i);
if (rii.filter == null) {
continue;
}
// Iterate over all of the actions of this result's intent
// filter... typically this should be just one.
final Iterator<String> it = rii.filter.actionsIterator();
if (it == null) {
continue;
}
while (it.hasNext()) {
final String action = it.next();
if (resultsAction != null && resultsAction.equals(action)) {
// If this action was explicitly requested, then don't
// remove things that have it.
continue;
}
for (int j=i+1; j<N; j++) {
final ResolveInfo rij = results.get(j);
if (rij.filter != null && rij.filter.hasAction(action)) {
results.remove(j);
if (DEBUG_INTENT_MATCHING) Log.v(
TAG, "Removing duplicate item from " + j
+ " due to action " + action + " at " + i);
j--;
N--;
}
}
}
// If the caller didn't request filter information, drop it now
// so we don't have to marshall/unmarshall it.
if ((flags&PackageManager.GET_RESOLVED_FILTER) == 0) {
rii.filter = null;
}
}
// Filter out the caller activity if so requested.
if (caller != null) {
N = results.size();
for (int i=0; i<N; i++) {
ActivityInfo ainfo = results.get(i).activityInfo;
if (caller.getPackageName().equals(ainfo.applicationInfo.packageName)
&& caller.getClassName().equals(ainfo.name)) {
results.remove(i);
break;
}
}
}
// If the caller didn't request filter information,
// drop them now so we don't have to
// marshall/unmarshall it.
if ((flags&PackageManager.GET_RESOLVED_FILTER) == 0) {
N = results.size();
for (int i=0; i<N; i++) {
results.get(i).filter = null;
}
}
if (DEBUG_INTENT_MATCHING) Log.v(TAG, "Result: " + results);
return results;
}
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
String resolvedType, int flags, int userId) {
return new ParceledListSlice<>(
queryIntentReceiversInternal(intent, resolvedType, flags, userId,
false /*allowDynamicSplits*/));
}
private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
String resolvedType, int flags, int userId, boolean allowDynamicSplits) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
flags));
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<>(1);
final ActivityInfo ai = getReceiverInfo(comp, flags, userId);
if (ai != null) {
// When specifying an explicit component, we prevent the activity from being
// used when either 1) the calling package is normal and the activity is within
// an instant application or 2) the calling package is ephemeral and the
// activity is not visible to instant applications.
final boolean matchInstantApp =
(flags & PackageManager.MATCH_INSTANT) != 0;
final boolean matchVisibleToInstantAppOnly =
(flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
final boolean matchExplicitlyVisibleOnly =
(flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
final boolean isCallerInstantApp =
instantAppPkgName != null;
final boolean isTargetSameInstantApp =
comp.getPackageName().equals(instantAppPkgName);
final boolean isTargetInstantApp =
(ai.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
final boolean isTargetVisibleToInstantApp =
(ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
final boolean isTargetExplicitlyVisibleToInstantApp =
isTargetVisibleToInstantApp
&& (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
final boolean isTargetHiddenFromInstantApp =
!isTargetVisibleToInstantApp
|| (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp);
final boolean blockResolution =
!isTargetSameInstantApp
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
if (!blockResolution) {
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
}
return applyPostResolutionFilter(
list, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
intent);
}
// reader
synchronized (mLock) {
String pkgName = intent.getPackage();
if (pkgName == null) {
final List<ResolveInfo> result =
mComponentResolver.queryReceivers(intent, resolvedType, flags, userId);
if (result == null) {
return Collections.emptyList();
}
return applyPostResolutionFilter(
result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
intent);
}
final AndroidPackage pkg = mPackages.get(pkgName);
if (pkg != null) {
final List<ResolveInfo> result = mComponentResolver.queryReceivers(
intent, resolvedType, flags, pkg.getReceivers(), userId);
if (result == null) {
return Collections.emptyList();
}
return applyPostResolutionFilter(
result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
intent);
}
return Collections.emptyList();
}
}
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {
final int callingUid = Binder.getCallingUid();
return resolveServiceInternal(intent, resolvedType, flags, userId, callingUid);
}
private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags,
int userId, int callingUid) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
List<ResolveInfo> query = queryIntentServicesInternal(
intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
if (query != null) {
if (query.size() >= 1) {
// If there is more than one service with the same priority,
// just arbitrarily pick the first one.
return query.get(0);
}
}
return null;
}
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentServices(Intent intent,
String resolvedType, int flags, int userId) {
final int callingUid = Binder.getCallingUid();
return new ParceledListSlice<>(queryIntentServicesInternal(
intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/));
}
private @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
String resolvedType, int flags, int userId, int callingUid,
boolean includeInstantApps) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
mPermissionManager.enforceCrossUserOrProfilePermission(callingUid,
userId,
false /*requireFullPermission*/,
false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<>(1);
final ServiceInfo si = getServiceInfo(comp, flags, userId);
if (si != null) {
// When specifying an explicit component, we prevent the service from being
// used when either 1) the service is in an instant application and the
// caller is not the same instant application or 2) the calling package is
// ephemeral and the activity is not visible to ephemeral applications.
final boolean matchInstantApp =
(flags & PackageManager.MATCH_INSTANT) != 0;
final boolean matchVisibleToInstantAppOnly =
(flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
final boolean isCallerInstantApp =
instantAppPkgName != null;
final boolean isTargetSameInstantApp =
comp.getPackageName().equals(instantAppPkgName);
final boolean isTargetInstantApp =
(si.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
final boolean isTargetHiddenFromInstantApp =
(si.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0;
final boolean blockInstantResolution =
!isTargetSameInstantApp
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
&& shouldFilterApplicationLocked(
getPackageSettingInternal(si.applicationInfo.packageName,
Process.SYSTEM_UID), callingUid, userId);
if (!blockInstantResolution && !blockNormalResolution) {
final ResolveInfo ri = new ResolveInfo();
ri.serviceInfo = si;
list.add(ri);
}
}
return list;
}
// reader
synchronized (mLock) {
String pkgName = intent.getPackage();
if (pkgName == null) {
final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
resolvedType, flags, userId);
if (resolveInfos == null) {
return Collections.emptyList();
}
return applyPostServiceResolutionFilter(
resolveInfos, instantAppPkgName, userId, callingUid);
}
final AndroidPackage pkg = mPackages.get(pkgName);
if (pkg != null) {
final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
resolvedType, flags, pkg.getServices(),
userId);
if (resolveInfos == null) {
return Collections.emptyList();
}
return applyPostServiceResolutionFilter(
resolveInfos, instantAppPkgName, userId, callingUid);
}
return Collections.emptyList();
}
}
private List<ResolveInfo> applyPostServiceResolutionFilter(List<ResolveInfo> resolveInfos,
String instantAppPkgName, @UserIdInt int userId, int filterCallingUid) {
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
if (instantAppPkgName == null) {
SettingBase callingSetting =
mSettings.getSettingLPr(UserHandle.getAppId(filterCallingUid));
PackageSetting resolvedSetting =
getPackageSettingInternal(info.serviceInfo.packageName, 0);
if (!mAppsFilter.shouldFilterApplication(
filterCallingUid, callingSetting, resolvedSetting, userId)) {
continue;
}
}
final boolean isEphemeralApp = info.serviceInfo.applicationInfo.isInstantApp();
// allow services that are defined in the provided package
if (isEphemeralApp && instantAppPkgName.equals(info.serviceInfo.packageName)) {
if (info.serviceInfo.splitName != null
&& !ArrayUtils.contains(info.serviceInfo.applicationInfo.splitNames,
info.serviceInfo.splitName)) {
// requested service is defined in a split that hasn't been installed yet.
// add the installer to the resolve list
if (DEBUG_INSTANT) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
final ResolveInfo installerInfo = new ResolveInfo(
mInstantAppInstallerInfo);
installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
null /* installFailureActivity */,
info.serviceInfo.packageName,
info.serviceInfo.applicationInfo.longVersionCode,
info.serviceInfo.splitName);
// add a non-generic filter
installerInfo.filter = new IntentFilter();
// load resources from the correct package
installerInfo.resolvePackageName = info.getComponentInfo().packageName;
resolveInfos.set(i, installerInfo);
}
continue;
}
// allow services that have been explicitly exposed to ephemeral apps
if (!isEphemeralApp
&& ((info.serviceInfo.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) {
continue;
}
resolveInfos.remove(i);
}
return resolveInfos;
}
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentContentProviders(Intent intent,
String resolvedType, int flags, int userId) {
return new ParceledListSlice<>(
queryIntentContentProvidersInternal(intent, resolvedType, flags, userId));
}
private @NonNull List<ResolveInfo> queryIntentContentProvidersInternal(
Intent intent, String resolvedType, int flags, int userId) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<>(1);
final ProviderInfo pi = getProviderInfo(comp, flags, userId);
if (pi != null) {
// When specifying an explicit component, we prevent the provider from being
// used when either 1) the provider is in an instant application and the
// caller is not the same instant application or 2) the calling package is an
// instant application and the provider is not visible to instant applications.
final boolean matchInstantApp =
(flags & PackageManager.MATCH_INSTANT) != 0;
final boolean matchVisibleToInstantAppOnly =
(flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
final boolean isCallerInstantApp =
instantAppPkgName != null;
final boolean isTargetSameInstantApp =
comp.getPackageName().equals(instantAppPkgName);
final boolean isTargetInstantApp =
(pi.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
final boolean isTargetHiddenFromInstantApp =
(pi.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0;
final boolean blockResolution =
!isTargetSameInstantApp
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
&& shouldFilterApplicationLocked(
getPackageSettingInternal(pi.applicationInfo.packageName,
Process.SYSTEM_UID), callingUid, userId);
if (!blockResolution && !blockNormalResolution) {
final ResolveInfo ri = new ResolveInfo();
ri.providerInfo = pi;
list.add(ri);
}
}
return list;
}
// reader
synchronized (mLock) {
String pkgName = intent.getPackage();
if (pkgName == null) {
final List<ResolveInfo> resolveInfos = mComponentResolver.queryProviders(intent,
resolvedType, flags, userId);
if (resolveInfos == null) {
return Collections.emptyList();
}
return applyPostContentProviderResolutionFilter(
resolveInfos, instantAppPkgName, userId, callingUid);
}
final AndroidPackage pkg = mPackages.get(pkgName);
if (pkg != null) {
final List<ResolveInfo> resolveInfos = mComponentResolver.queryProviders(intent,
resolvedType, flags,
pkg.getProviders(), userId);
if (resolveInfos == null) {
return Collections.emptyList();
}
return applyPostContentProviderResolutionFilter(
resolveInfos, instantAppPkgName, userId, callingUid);
}
return Collections.emptyList();
}
}
private List<ResolveInfo> applyPostContentProviderResolutionFilter(
List<ResolveInfo> resolveInfos, String instantAppPkgName,
@UserIdInt int userId, int callingUid) {
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
if (instantAppPkgName == null) {
SettingBase callingSetting =
mSettings.getSettingLPr(UserHandle.getAppId(callingUid));
PackageSetting resolvedSetting =
getPackageSettingInternal(info.providerInfo.packageName, 0);
if (!mAppsFilter.shouldFilterApplication(
callingUid, callingSetting, resolvedSetting, userId)) {
continue;
}
}
final boolean isEphemeralApp = info.providerInfo.applicationInfo.isInstantApp();
// allow providers that are defined in the provided package
if (isEphemeralApp && instantAppPkgName.equals(info.providerInfo.packageName)) {
if (info.providerInfo.splitName != null
&& !ArrayUtils.contains(info.providerInfo.applicationInfo.splitNames,
info.providerInfo.splitName)) {
// requested provider is defined in a split that hasn't been installed yet.
// add the installer to the resolve list
if (DEBUG_INSTANT) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
final ResolveInfo installerInfo = new ResolveInfo(
mInstantAppInstallerInfo);
installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
null /*failureActivity*/,
info.providerInfo.packageName,
info.providerInfo.applicationInfo.longVersionCode,
info.providerInfo.splitName);
// add a non-generic filter
installerInfo.filter = new IntentFilter();
// load resources from the correct package
installerInfo.resolvePackageName = info.getComponentInfo().packageName;
resolveInfos.set(i, installerInfo);
}
continue;
}
// allow providers that have been explicitly exposed to instant applications
if (!isEphemeralApp
&& ((info.providerInfo.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) {
continue;
}
resolveInfos.remove(i);
}
return resolveInfos;
}
@Override
public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return ParceledListSlice.emptyList();
}
if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForPackage(flags, userId);
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
final boolean listApex = (flags & MATCH_APEX) != 0;
final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0;
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */,
"get installed packages");
// writer
synchronized (mLock) {
ArrayList<PackageInfo> list;
if (listUninstalled) {
list = new ArrayList<>(mSettings.mPackages.size());
for (PackageSetting ps : mSettings.mPackages.values()) {
if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
continue;
}
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
continue;
}
final PackageInfo pi = generatePackageInfo(ps, flags, userId);
if (pi != null) {
list.add(pi);
}
}
} else {
list = new ArrayList<>(mPackages.size());
for (AndroidPackage p : mPackages.values()) {
final PackageSetting ps = getPackageSetting(p.getPackageName());
if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
continue;
}
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
continue;
}
final PackageInfo pi = generatePackageInfo(ps, flags, userId);
if (pi != null) {
list.add(pi);
}
}
}
if (listApex) {
if (listFactory) {
list.addAll(mApexManager.getFactoryPackages());
} else {
list.addAll(mApexManager.getActivePackages());
}
if (listUninstalled) {
list.addAll(mApexManager.getInactivePackages());
}
}
return new ParceledListSlice<>(list);
}
}
private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageSetting ps,
String[] permissions, boolean[] tmp, int flags, int userId) {
int numMatch = 0;
for (int i=0; i<permissions.length; i++) {
final String permission = permissions[i];
if (checkPermission(permission, ps.name, userId) == PERMISSION_GRANTED) {
tmp[i] = true;
numMatch++;
} else {
tmp[i] = false;
}
}
if (numMatch == 0) {
return;
}
final PackageInfo pi = generatePackageInfo(ps, flags, userId);
// The above might return null in cases of uninstalled apps or install-state
// skew across users/profiles.
if (pi != null) {
if ((flags&PackageManager.GET_PERMISSIONS) == 0) {
if (numMatch == permissions.length) {
pi.requestedPermissions = permissions;
} else {
pi.requestedPermissions = new String[numMatch];
numMatch = 0;
for (int i=0; i<permissions.length; i++) {
if (tmp[i]) {
pi.requestedPermissions[numMatch] = permissions[i];
numMatch++;
}
}
}
}
list.add(pi);
}
}
@Override
public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
String[] permissions, int flags, int userId) {
if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForPackage(flags, userId);
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"get packages holding permissions");
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
// writer
synchronized (mLock) {
ArrayList<PackageInfo> list = new ArrayList<>();
boolean[] tmpBools = new boolean[permissions.length];
if (listUninstalled) {
for (PackageSetting ps : mSettings.mPackages.values()) {
addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags,
userId);
}
} else {
for (AndroidPackage pkg : mPackages.values()) {
PackageSetting ps = getPackageSetting(pkg.getPackageName());
if (ps != null) {
addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags,
userId);
}
}
}
return new ParceledListSlice<>(list);
}
}
@Override
public ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags, int userId) {
final int callingUid = Binder.getCallingUid();
return new ParceledListSlice<>(
getInstalledApplicationsListInternal(flags, userId, callingUid));
}
private List<ApplicationInfo> getInstalledApplicationsListInternal(int flags, int userId,
int callingUid) {
if (getInstantAppPackageName(callingUid) != null) {
return Collections.emptyList();
}
if (!mUserManager.exists(userId)) return Collections.emptyList();
flags = updateFlagsForApplication(flags, userId);
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
mPermissionManager.enforceCrossUserPermission(
callingUid,
userId,
false /* requireFullPermission */,
false /* checkShell */,
"get installed application info");
// writer
synchronized (mLock) {
ArrayList<ApplicationInfo> list;
if (listUninstalled) {
list = new ArrayList<>(mSettings.mPackages.size());
for (PackageSetting ps : mSettings.mPackages.values()) {
ApplicationInfo ai;
int effectiveFlags = flags;
if (ps.isSystem()) {
effectiveFlags |= PackageManager.MATCH_ANY_USER;
}
if (ps.pkg != null) {
if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
continue;
}
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
continue;
}
ai = PackageInfoUtils.generateApplicationInfo(ps.pkg, effectiveFlags,
ps.readUserState(userId), userId, ps);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
}
} else {
// Shared lib filtering done in generateApplicationInfoFromSettingsLPw
// and already converts to externally visible package name
ai = generateApplicationInfoFromSettingsLPw(ps.name,
effectiveFlags, callingUid, userId);
}
if (ai != null) {
list.add(ai);
}
}
} else {
list = new ArrayList<>(mPackages.size());
for (AndroidPackage p : mPackages.values()) {
final PackageSetting ps = getPackageSetting(p.getPackageName());
if (ps != null) {
if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
continue;
}
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
continue;
}
ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(p, flags,
ps.readUserState(userId), userId, ps);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(p);
list.add(ai);
}
}
}
}
return list;
}
}
@Override
public ParceledListSlice<InstantAppInfo> getInstantApps(int userId) {
if (HIDE_EPHEMERAL_APIS) {
return null;
}
if (!canViewInstantApps(Binder.getCallingUid(), userId)) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
"getEphemeralApplications");
}
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"getEphemeralApplications");
synchronized (mLock) {
List<InstantAppInfo> instantApps = mInstantAppRegistry
.getInstantAppsLPr(userId);
if (instantApps != null) {
return new ParceledListSlice<>(instantApps);
}
}
return null;
}
@Override
public boolean isInstantApp(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"isInstantApp");
return isInstantAppInternal(packageName, userId, callingUid);
}
private boolean isInstantAppInternal(String packageName, @UserIdInt int userId,
int callingUid) {
if (HIDE_EPHEMERAL_APIS) {
return false;
}
synchronized (mLock) {
if (Process.isIsolated(callingUid)) {
callingUid = mIsolatedOwners.get(callingUid);
}
final PackageSetting ps = mSettings.mPackages.get(packageName);
final boolean returnAllowed =
ps != null
&& (isCallerSameApp(packageName, callingUid)
|| canViewInstantApps(callingUid, userId)
|| mInstantAppRegistry.isInstantAccessGranted(
userId, UserHandle.getAppId(callingUid), ps.appId));
if (returnAllowed) {
return ps.getInstantApp(userId);
}
}
return false;
}
@Override
public byte[] getInstantAppCookie(String packageName, int userId) {
if (HIDE_EPHEMERAL_APIS) {
return null;
}
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"getInstantAppCookie");
if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
return null;
}
synchronized (mLock) {
return mInstantAppRegistry.getInstantAppCookieLPw(
packageName, userId);
}
}
@Override
public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) {
if (HIDE_EPHEMERAL_APIS) {
return true;
}
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, true /* checkShell */,
"setInstantAppCookie");
if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
return false;
}
synchronized (mLock) {
return mInstantAppRegistry.setInstantAppCookieLPw(
packageName, cookie, userId);
}
}
@Override
public Bitmap getInstantAppIcon(String packageName, int userId) {
if (HIDE_EPHEMERAL_APIS) {
return null;
}
if (!canViewInstantApps(Binder.getCallingUid(), userId)) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
"getInstantAppIcon");
}
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"getInstantAppIcon");
synchronized (mLock) {
return mInstantAppRegistry.getInstantAppIconLPw(
packageName, userId);
}
}
private boolean isCallerSameApp(String packageName, int uid) {
AndroidPackage pkg = mPackages.get(packageName);
return pkg != null
&& UserHandle.getAppId(uid) == pkg.getUid();
}
@Override
public @NonNull ParceledListSlice<ApplicationInfo> getPersistentApplications(int flags) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return ParceledListSlice.emptyList();
}
return new ParceledListSlice<>(getPersistentApplicationsInternal(flags));
}
private @NonNull List<ApplicationInfo> getPersistentApplicationsInternal(int flags) {
final ArrayList<ApplicationInfo> finalList = new ArrayList<>();
// reader
synchronized (mLock) {
final Iterator<AndroidPackage> i = mPackages.values().iterator();
final int userId = UserHandle.getCallingUserId();
while (i.hasNext()) {
final AndroidPackage p = i.next();
final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0)
&& !p.isDirectBootAware();
final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0)
&& p.isDirectBootAware();
if (p.isPersistent()
&& (!mSafeMode || p.isSystem())
&& (matchesUnaware || matchesAware)) {
PackageSetting ps = mSettings.mPackages.get(p.getPackageName());
if (ps != null) {
ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(p, flags,
ps.readUserState(userId), userId, ps);
if (ai != null) {
finalList.add(ai);
}
}
}
}
}
return finalList;
}
@Override
public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
return resolveContentProviderInternal(name, flags, userId);
}
private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId);
boolean checkedGrants = false;
if (providerInfo != null) {
// Looking for cross-user grants before enforcing the typical cross-users permissions
if (userId != UserHandle.getUserId(callingUid)) {
final UriGrantsManagerInternal mUgmInternal =
LocalServices.getService(UriGrantsManagerInternal.class);
checkedGrants =
mUgmInternal.checkAuthorityGrants(callingUid, providerInfo, userId, true);
}
}
if (!checkedGrants) {
mPermissionManager.enforceCrossUserPermission(
callingUid, userId, false, false, "resolveContentProvider");
}
if (providerInfo == null) {
return null;
}
synchronized (mLock) {
if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) {
return null;
}
final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName);
final ComponentName component =
new ComponentName(providerInfo.packageName, providerInfo.name);
if (shouldFilterApplicationLocked(ps, callingUid, component, TYPE_PROVIDER, userId)) {
return null;
}
return providerInfo;
}
}
/**
* @deprecated
*/
@Deprecated
public void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return;
}
mComponentResolver.querySyncProviders(
outNames, outInfo, mSafeMode, UserHandle.getCallingUserId());
}
@Override
public @NonNull ParceledListSlice<ProviderInfo> queryContentProviders(String processName,
int uid, int flags, String metaDataKey) {
final int callingUid = Binder.getCallingUid();
final int userId = processName != null ? UserHandle.getUserId(uid)
: UserHandle.getCallingUserId();
if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForComponent(flags, userId);
ArrayList<ProviderInfo> finalList = null;
final List<ProviderInfo> matchList =
mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId);
final int listSize = (matchList == null ? 0 : matchList.size());
synchronized (mLock) {
for (int i = 0; i < listSize; i++) {
final ProviderInfo providerInfo = matchList.get(i);
if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) {
continue;
}
final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName);
final ComponentName component =
new ComponentName(providerInfo.packageName, providerInfo.name);
if (shouldFilterApplicationLocked(
ps, callingUid, component, TYPE_PROVIDER, userId)) {
continue;
}
if (finalList == null) {
finalList = new ArrayList<>(listSize - i);
}
finalList.add(providerInfo);
}
}
if (finalList != null) {
finalList.sort(sProviderInitOrderSorter);
return new ParceledListSlice<>(finalList);
}
return ParceledListSlice.emptyList();
}
@Override
public InstrumentationInfo getInstrumentationInfo(ComponentName component, int flags) {
// reader
synchronized (mLock) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
String packageName = component.getPackageName();
final PackageSetting ps = mSettings.mPackages.get(packageName);
AndroidPackage pkg = mPackages.get(packageName);
if (ps == null || pkg == null) return null;
if (shouldFilterApplicationLocked(
ps, callingUid, component, TYPE_UNKNOWN, callingUserId)) {
return null;
}
final ParsedInstrumentation i = mInstrumentation.get(component);
return PackageInfoUtils.generateInstrumentationInfo(i, pkg, flags, callingUserId, ps);
}
}
@Override
public @NonNull ParceledListSlice<InstrumentationInfo> queryInstrumentation(
String targetPackage, int flags) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
synchronized (mLock) {
final PackageSetting ps = mSettings.getPackageLPr(targetPackage);
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return ParceledListSlice.emptyList();
}
}
return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags,
callingUserId));
}
private @NonNull List<InstrumentationInfo> queryInstrumentationInternal(String targetPackage,
int flags, int userId) {
ArrayList<InstrumentationInfo> finalList = new ArrayList<>();
// reader
synchronized (mLock) {
final Iterator<ParsedInstrumentation> i = mInstrumentation.values().iterator();
while (i.hasNext()) {
final ParsedInstrumentation p = i.next();
if (targetPackage == null
|| targetPackage.equals(p.getTargetPackage())) {
String packageName = p.getPackageName();
AndroidPackage pkg = mPackages.get(packageName);
PackageSetting pkgSetting = getPackageSetting(packageName);
if (pkg != null) {
InstrumentationInfo ii = PackageInfoUtils.generateInstrumentationInfo(p,
pkg, flags, userId, pkgSetting);
if (ii != null) {
finalList.add(ii);
}
}
}
}
}
return finalList;
}
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
PackageParser2 packageParser, ExecutorService executorService) {
final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + scanDir);
return;
}
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
+ " flags=0x" + Integer.toHexString(parseFlags));
}
ParallelPackageParser parallelPackageParser =
new ParallelPackageParser(packageParser, executorService);
// Submit files for parsing in parallel
int fileCount = 0;
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
// Process results one by one
for (; fileCount > 0; fileCount--) {
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
if (throwable == null) {
// TODO(toddke): move lower in the scan chain
// Static shared libraries have synthetic package names
if (parseResult.parsedPackage.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(parseResult.parsedPackage);
}
try {
addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
currentTime, null);
} catch (PackageManagerException e) {
errorCode = e.error;
Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
}
} else if (throwable instanceof PackageParserException) {
PackageParserException e = (PackageParserException)
throwable;
errorCode = e.error;
Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());
} else {
throw new IllegalStateException("Unexpected exception occurred while parsing "
+ parseResult.scanFile, throwable);
}
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath());
}
// Delete invalid userdata apps
if ((scanFlags & SCAN_AS_SYSTEM) == 0
&& errorCode != PackageManager.INSTALL_SUCCEEDED) {
logCriticalInfo(Log.WARN,
"Deleting invalid package at " + parseResult.scanFile);
removeCodePathLI(parseResult.scanFile);
}
}
}
public static void reportSettingsProblem(int priority, String msg) {
logCriticalInfo(priority, msg);
}
private void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
boolean forceCollect, boolean skipVerify) throws PackageManagerException {
// When upgrading from pre-N MR1, verify the package time stamp using the package
// directory and not the APK file.
final long lastModifiedTime = mIsPreNMR1Upgrade
? new File(parsedPackage.getCodePath()).lastModified()
: getLastModifiedTime(parsedPackage);
final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(parsedPackage);
if (ps != null && !forceCollect
&& ps.codePathString.equals(parsedPackage.getCodePath())
&& ps.timeStamp == lastModifiedTime
&& !isCompatSignatureUpdateNeeded(settingsVersionForPackage)
&& !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) {
if (ps.signatures.mSigningDetails.signatures != null
&& ps.signatures.mSigningDetails.signatures.length != 0
&& ps.signatures.mSigningDetails.signatureSchemeVersion
!= SignatureSchemeVersion.UNKNOWN) {
// Optimization: reuse the existing cached signing data
// if the package appears to be unchanged.
parsedPackage.setSigningDetails(
new PackageParser.SigningDetails(ps.signatures.mSigningDetails));
return;
}
Slog.w(TAG, "PackageSetting for " + ps.name
+ " is missing signatures. Collecting certs again to recover them.");
} else {
Slog.i(TAG, parsedPackage.getCodePath() + " changed; collecting certs" +
(forceCollect ? " (forced)" : ""));
}
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
parsedPackage.setSigningDetails(
ParsingPackageUtils.getSigningDetails(parsedPackage, skipVerify));
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
/**
* Clear the package profile if this was an upgrade and the package
* version was updated.
*/
private void maybeClearProfilesForUpgradesLI(
@Nullable PackageSetting originalPkgSetting,
@NonNull AndroidPackage pkg) {
if (originalPkgSetting == null || !isDeviceUpgrading()) {
return;
}
if (originalPkgSetting.versionCode == pkg.getVersionCode()) {
return;
}
clearAppProfilesLIF(pkg, UserHandle.USER_ALL);
if (DEBUG_INSTALL) {
Slog.d(TAG, originalPkgSetting.name
+ " clear profile due to version change "
+ originalPkgSetting.versionCode + " != "
+ pkg.getVersionCode());
}
}
/**
* Traces a package scan.
* @see #scanPackageLI(File, int, int, long, UserHandle)
*/
@GuardedBy({"mInstallLock", "mLock"})
private AndroidPackage scanPackageTracedLI(File scanFile, final int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
try {
return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
/**
* Scans a package and returns the newly parsed package.
* Returns {@code null} in case of errors and the error code is stored in mLastScanError
*/
@GuardedBy({"mInstallLock", "mLock"})
private AndroidPackage scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final ParsedPackage parsedPackage;
try (PackageParser2 pp = new PackageParser2(mSeparateProcesses, mOnlyCore, mMetrics, null,
mPackageParserCallback)) {
parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
// Static shared libraries have synthetic package names
if (parsedPackage.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(parsedPackage);
}
return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
}
/**
* Returns if forced apk verification can be skipped for the whole package, including splits.
*/
private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
if (!canSkipForcedApkVerification(pkg.getBaseCodePath())) {
return false;
}
// TODO: Allow base and splits to be verified individually.
String[] splitCodePaths = pkg.getSplitCodePaths();
if (!ArrayUtils.isEmpty(splitCodePaths)) {
for (int i = 0; i < splitCodePaths.length; i++) {
if (!canSkipForcedApkVerification(splitCodePaths[i])) {
return false;
}
}
}
return true;
}
/**
* Returns if forced apk verification can be skipped, depending on current FSVerity setup and
* whether the apk contains signed root hash. Note that the signer's certificate still needs to
* match one in a trusted source, and should be done separately.
*/
private boolean canSkipForcedApkVerification(String apkPath) {
if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
return VerityUtils.hasFsverity(apkPath);
}
try {
final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
if (rootHashObserved == null) {
return false; // APK does not contain Merkle tree root hash.
}
synchronized (mInstallLock) {
// Returns whether the observed root hash matches what kernel has.
mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved);
return true;
}
} catch (InstallerException | IOException | DigestException |
NoSuchAlgorithmException e) {
Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
}
return false;
}
/**
* Adds a new package to the internal data structures during platform initialization.
* <p>After adding, the package is known to the system and available for querying.
* <p>For packages located on the device ROM [eg. packages located in /system, /vendor,
* etc...], additional checks are performed. Basic verification [such as ensuring
* matching signatures, checking version codes, etc...] occurs if the package is
* identical to a previously known package. If the package fails a signature check,
* the version installed on /data will be removed. If the version of the new package
* is less than or equal than the version on /data, it will be ignored.
* <p>Regardless of the package location, the results are applied to the internal
* structures and the package is made available to the rest of the system.
* <p>NOTE: The return value should be removed. It's the passed in package object.
*/
@GuardedBy({"mInstallLock", "mLock"})
private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
@ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
throws PackageManagerException {
final boolean scanSystemPartition = (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0;
final String renamedPkgName;
final PackageSetting disabledPkgSetting;
final boolean isSystemPkgUpdated;
final boolean pkgAlreadyExists;
PackageSetting pkgSetting;
synchronized (mLock) {
renamedPkgName = mSettings.getRenamedPackageLPr(parsedPackage.getRealPackage());
final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);
if (realPkgName != null) {
ensurePackageRenamed(parsedPackage, renamedPkgName);
}
final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
renamedPkgName);
final PackageSetting installedPkgSetting = mSettings.getPackageLPr(
parsedPackage.getPackageName());
pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
pkgAlreadyExists = pkgSetting != null;
final String disabledPkgName = pkgAlreadyExists
? pkgSetting.name : parsedPackage.getPackageName();
if (scanSystemPartition && !pkgAlreadyExists
&& mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
// The updated-package data for /system apk remains inconsistently
// after the package data for /data apk is lost accidentally.
// To recover it, enable /system apk and install it as non-updated system app.
Slog.w(TAG, "Inconsistent package setting of updated system app for "
+ disabledPkgName + ". To recover it, enable the system app"
+ "and install it as non-updated system app.");
mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
}
disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName);
isSystemPkgUpdated = disabledPkgSetting != null;
if (DEBUG_INSTALL && isSystemPkgUpdated) {
Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
}
final SharedUserSetting sharedUserSetting = (parsedPackage.getSharedUserId() != null)
? mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
: null;
if (DEBUG_PACKAGE_SCANNING
&& (parseFlags & PackageParser.PARSE_CHATTY) != 0
&& sharedUserSetting != null) {
Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
+ " (uid=" + sharedUserSetting.userId + "):"
+ " packages=" + sharedUserSetting.packages);
}
if (scanSystemPartition) {
if (isSystemPkgUpdated) {
// we're updating the disabled package, so, scan it as the package setting
boolean isPlatformPackage = mPlatformPackage != null
&& Objects.equals(mPlatformPackage.getPackageName(),
parsedPackage.getPackageName());
final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
null, disabledPkgSetting /* pkgSetting */,
null /* disabledPkgSetting */, null /* originalPkgSetting */,
null, parseFlags, scanFlags, isPlatformPackage, user, null);
applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage, true);
final ScanResult scanResult =
scanPackageOnlyLI(request, mInjector, mFactoryTest, -1L);
if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) {
scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting);
}
}
}
}
final boolean newPkgChangedPaths =
pkgAlreadyExists && !pkgSetting.codePathString.equals(parsedPackage.getCodePath());
final boolean newPkgVersionGreater =
pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode;
final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
&& newPkgChangedPaths && newPkgVersionGreater;
if (isSystemPkgBetter) {
// The version of the application on /system is greater than the version on
// /data. Switch back to the application on /system.
// It's safe to assume the application on /system will correctly scan. If not,
// there won't be a working copy of the application.
synchronized (mLock) {
// just remove the loaded entries from package lists
mPackages.remove(pkgSetting.name);
}
logCriticalInfo(Log.WARN,
"System package updated;"
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> " + parsedPackage.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> " + parsedPackage.getCodePath());
final InstallArgs args = createInstallArgsForExisting(
pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(
pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
args.cleanUpResourcesLI();
synchronized (mLock) {
mSettings.enableSystemPackageLPw(pkgSetting.name);
}
}
if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
// The version of the application on the /system partition is less than or
// equal to the version on the /data partition. Throw an exception and use
// the application already installed on the /data partition.
throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
+ " at " + parsedPackage.getCodePath() + " ignored: updated version "
+ pkgSetting.versionCode + " better than this "
+ parsedPackage.getLongVersionCode());
}
// Verify certificates against what was last scanned. Force re-collecting certificate in two
// special cases:
// 1) when scanning system, force re-collect only if system is upgrading.
// 2) when scannning /data, force re-collect only if the app is privileged (updated from
// preinstall, or treated as privileged, e.g. due to shared user ID).
final boolean forceCollect = scanSystemPartition ? mIsUpgrade
: PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
if (DEBUG_VERIFY && forceCollect) {
Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
}
// Full APK verification can be skipped during certificate collection, only if the file is
// in verified partition, or can be verified on access (when apk verity is enabled). In both
// cases, only data in Signing Block is verified instead of the whole file.
// TODO(b/136132412): skip for Incremental installation
final boolean skipVerify = scanSystemPartition
|| (forceCollect && canSkipForcedPackageVerification(parsedPackage));
collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify);
// Reset profile if the application version is changed
maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
/*
* A new system app appeared, but we already had a non-system one of the
* same name installed earlier.
*/
boolean shouldHideSystemApp = false;
// A new application appeared on /system, but, we already have a copy of
// the application installed on /data.
if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
&& !pkgSetting.isSystem()) {
if (!parsedPackage.getSigningDetails()
.checkCapability(pkgSetting.signatures.mSigningDetails,
PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
&& !pkgSetting.signatures.mSigningDetails.checkCapability(
parsedPackage.getSigningDetails(),
PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
logCriticalInfo(Log.WARN,
"System package signature mismatch;"
+ " name: " + pkgSetting.name);
try (@SuppressWarnings("unused") PackageFreezer freezer = freezePackage(
parsedPackage.getPackageName(),
"scanPackageInternalLI")) {
deletePackageLIF(parsedPackage.getPackageName(), null, true, null, 0, null,
false, null);
}
pkgSetting = null;
} else if (newPkgVersionGreater) {
// The application on /system is newer than the application on /data.
// Simply remove the application on /data [keeping application data]
// and replace it with the version on /system.
logCriticalInfo(Log.WARN,
"System package enabled;"
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> "
+ parsedPackage.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> "
+ parsedPackage.getCodePath());
InstallArgs args = createInstallArgsForExisting(
pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(
pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
} else {
// The application on /system is older than the application on /data. Hide
// the application on /system and the version on /data will be scanned later
// and re-added like an update.
shouldHideSystemApp = true;
logCriticalInfo(Log.INFO,
"System package disabled;"
+ " name: " + pkgSetting.name
+ "; old: " + pkgSetting.codePathString + " @ "
+ pkgSetting.versionCode
+ "; new: " + parsedPackage.getCodePath() + " @ "
+ parsedPackage.getCodePath());
}
}
final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user, null);
if (scanResult.success) {
synchronized (mLock) {
boolean appIdCreated = false;
try {
final String pkgName = scanResult.pkgSetting.name;
final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked(
new ReconcileRequest(
Collections.singletonMap(pkgName, scanResult),
mSharedLibraries,
mPackages,
Collections.singletonMap(
pkgName, getSettingsVersionForPackage(parsedPackage)),
Collections.singletonMap(pkgName,
getSharedLibLatestVersionSetting(scanResult))),
mSettings.mKeySetManagerService);
appIdCreated = optimisticallyRegisterAppId(scanResult);
commitReconciledScanResultLocked(
reconcileResult.get(pkgName), mUserManager.getUserIds());
} catch (PackageManagerException e) {
if (appIdCreated) {
cleanUpAppIdCreation(scanResult);
}
throw e;
}
}
}
if (shouldHideSystemApp) {
synchronized (mLock) {
mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
}
}
return scanResult.pkgSetting.pkg;
}
// TODO:(b/135203078): Move to parsing
private static void renameStaticSharedLibraryPackage(ParsedPackage parsedPackage) {
// Derive the new package synthetic package name
parsedPackage.setPackageName(toStaticSharedLibraryPackageName(
parsedPackage.getPackageName(), parsedPackage.getStaticSharedLibVersion()));
}
private static String toStaticSharedLibraryPackageName(
String packageName, long libraryVersion) {
return packageName + STATIC_SHARED_LIB_DELIMITER + libraryVersion;
}
static String fixProcessName(String defProcessName, String processName) {
if (processName == null) {
return defProcessName;
}
return processName;
}
/**
* Enforces that only the system UID or root's UID can call a method exposed
* via Binder.
*
* @param message used as message if SecurityException is thrown
* @throws SecurityException if the caller is not system or root
*/
private static void enforceSystemOrRoot(String message) {
final int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) {
throw new SecurityException(message);
}
}
/**
* Enforces that only the system UID or root's UID or shell's UID can call
* a method exposed via Binder.
*
* @param message used as message if SecurityException is thrown
* @throws SecurityException if the caller is not system or shell
*/
private static void enforceSystemOrRootOrShell(String message) {
final int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID && uid != Process.SHELL_UID) {
throw new SecurityException(message);
}
}
@Override
public void performFstrimIfNeeded() {
enforceSystemOrRoot("Only the system can request fstrim");
// Before everything else, see whether we need to fstrim.
try {
IStorageManager sm = PackageHelper.getStorageManager();
if (sm != null) {
boolean doTrim = false;
final long interval = android.provider.Settings.Global.getLong(
mContext.getContentResolver(),
android.provider.Settings.Global.FSTRIM_MANDATORY_INTERVAL,
DEFAULT_MANDATORY_FSTRIM_INTERVAL);
if (interval > 0) {
final long timeSinceLast = System.currentTimeMillis() - sm.lastMaintenance();
if (timeSinceLast > interval) {
doTrim = true;
Slog.w(TAG, "No disk maintenance in " + timeSinceLast
+ "; running immediately");
}
}
if (doTrim) {
final boolean dexOptDialogShown;
synchronized (mLock) {
dexOptDialogShown = mDexOptDialogShown;
}
if (!isFirstBoot() && dexOptDialogShown) {
try {
ActivityManager.getService().showBootMessage(
mContext.getResources().getString(
R.string.android_upgrading_fstrim), true);
} catch (RemoteException e) {
}
}
sm.runMaintenance();
}
} else {
Slog.e(TAG, "storageManager service unavailable!");
}
} catch (RemoteException e) {
// Can't happen; StorageManagerService is local
}
}
@Override
public void updatePackagesIfNeeded() {
enforceSystemOrRoot("Only the system can request package update");
// We need to re-extract after an OTA.
boolean causeUpgrade = isDeviceUpgrading();
// First boot or factory reset.
// Note: we also handle devices that are upgrading to N right now as if it is their
// first boot, as they do not have profile data.
boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade;
// We need to re-extract after a pruned cache, as AoT-ed files will be out of date.
boolean causePrunedCache = VMRuntime.didPruneDalvikCache();
if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) {
return;
}
List<PackageSetting> pkgSettings;
synchronized (mLock) {
pkgSettings = PackageManagerServiceUtils.getPackagesForDexopt(
mSettings.mPackages.values(), this);
}
List<AndroidPackage> pkgs = new ArrayList<>(pkgSettings.size());
for (int index = 0; index < pkgSettings.size(); index++) {
pkgs.add(pkgSettings.get(index).pkg);
}
final long startTime = System.nanoTime();
final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
false /* bootComplete */);
final int elapsedTimeSeconds =
(int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
MetricsLogger.histogram(mContext, "opt_dialog_num_dexopted", stats[0]);
MetricsLogger.histogram(mContext, "opt_dialog_num_skipped", stats[1]);
MetricsLogger.histogram(mContext, "opt_dialog_num_failed", stats[2]);
MetricsLogger.histogram(mContext, "opt_dialog_num_total", getOptimizablePackages().size());
MetricsLogger.histogram(mContext, "opt_dialog_time_s", elapsedTimeSeconds);
}
/*
* Return the prebuilt profile path given a package base code path.
*/
private static String getPrebuildProfilePath(AndroidPackage pkg) {
return pkg.getBaseCodePath() + ".prof";
}
/**
* Performs dexopt on the set of packages in {@code packages} and returns an int array
* containing statistics about the invocation. The array consists of three elements,
* which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped}
* and {@code numberOfPackagesFailed}.
*/
private int[] performDexOptUpgrade(List<AndroidPackage> pkgs, boolean showDialog,
final int compilationReason, boolean bootComplete) {
int numberOfPackagesVisited = 0;
int numberOfPackagesOptimized = 0;
int numberOfPackagesSkipped = 0;
int numberOfPackagesFailed = 0;
final int numberOfPackagesToDexopt = pkgs.size();
for (AndroidPackage pkg : pkgs) {
numberOfPackagesVisited++;
boolean useProfileForDexopt = false;
if ((isFirstBoot() || isDeviceUpgrading()) && pkg.isSystem()) {
// Copy over initial preopt profiles since we won't get any JIT samples for methods
// that are already compiled.
File profileFile = new File(getPrebuildProfilePath(pkg));
// Copy profile if it exists.
if (profileFile.exists()) {
try {
// We could also do this lazily before calling dexopt in
// PackageDexOptimizer to prevent this happening on first boot. The issue
// is that we don't have a good way to say "do this only once".
if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
pkg.getUid(), pkg.getPackageName(),
ArtManager.getProfileName(null))) {
Log.e(TAG, "Installer failed to copy system profile!");
} else {
// Disabled as this causes speed-profile compilation during first boot
// even if things are already compiled.
// useProfileForDexopt = true;
}
} catch (Exception e) {
Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ",
e);
}
} else {
PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(
pkg.getPackageName());
// Handle compressed APKs in this path. Only do this for stubs with profiles to
// minimize the number off apps being speed-profile compiled during first boot.
// The other paths will not change the filter.
if (disabledPs != null && disabledPs.pkg.isStub()) {
// The package is the stub one, remove the stub suffix to get the normal
// package and APK names.
String systemProfilePath =
getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, "");
profileFile = new File(systemProfilePath);
// If we have a profile for a compressed APK, copy it to the reference
// location.
// Note that copying the profile here will cause it to override the
// reference profile every OTA even though the existing reference profile
// may have more data. We can't copy during decompression since the
// directories are not set up at that point.
if (profileFile.exists()) {
try {
// We could also do this lazily before calling dexopt in
// PackageDexOptimizer to prevent this happening on first boot. The
// issue is that we don't have a good way to say "do this only
// once".
if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
pkg.getUid(), pkg.getPackageName(),
ArtManager.getProfileName(null))) {
Log.e(TAG, "Failed to copy system profile for stub package!");
} else {
useProfileForDexopt = true;
}
} catch (Exception e) {
Log.e(TAG, "Failed to copy profile " +
profileFile.getAbsolutePath() + " ", e);
}
}
}
}
}
if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
if (DEBUG_DEXOPT) {
Log.i(TAG, "Skipping update of non-optimizable app " + pkg.getPackageName());
}
numberOfPackagesSkipped++;
continue;
}
if (DEBUG_DEXOPT) {
Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " +
numberOfPackagesToDexopt + ": " + pkg.getPackageName());
}
if (showDialog) {
try {
ActivityManager.getService().showBootMessage(
mContext.getResources().getString(R.string.android_upgrading_apk,
numberOfPackagesVisited, numberOfPackagesToDexopt), true);
} catch (RemoteException e) {
}
synchronized (mLock) {
mDexOptDialogShown = true;
}
}
int pkgCompilationReason = compilationReason;
if (useProfileForDexopt) {
// Use background dexopt mode to try and use the profile. Note that this does not
// guarantee usage of the profile.
pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
}
if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
mArtManagerService.compileLayouts(pkg);
}
// checkProfiles is false to avoid merging profiles during boot which
// might interfere with background compilation (b/28612421).
// Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
// behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
// trade-off worth doing to save boot time work.
int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
if (compilationReason == REASON_FIRST_BOOT) {
// TODO: This doesn't cover the upgrade case, we should check for this too.
dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
}
int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
pkg.getPackageName(),
pkgCompilationReason,
dexoptFlags));
switch (primaryDexOptStaus) {
case PackageDexOptimizer.DEX_OPT_PERFORMED:
numberOfPackagesOptimized++;
break;
case PackageDexOptimizer.DEX_OPT_SKIPPED:
numberOfPackagesSkipped++;
break;
case PackageDexOptimizer.DEX_OPT_FAILED:
numberOfPackagesFailed++;
break;
default:
Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus);
break;
}
}
return new int[] { numberOfPackagesOptimized, numberOfPackagesSkipped,
numberOfPackagesFailed };
}
@Override
public void notifyPackageUse(String packageName, int reason) {
synchronized (mLock) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
if (getInstantAppPackageName(callingUid) != null) {
if (!isCallerSameApp(packageName, callingUid)) {
return;
}
} else {
if (isInstantApp(packageName, callingUserId)) {
return;
}
}
notifyPackageUseLocked(packageName, reason);
}
}
@GuardedBy("mLock")
private void notifyPackageUseLocked(String packageName, int reason) {
final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
if (pkgSetting == null) {
return;
}
pkgSetting.getPkgState().setLastPackageUsageTimeInMills(reason, System.currentTimeMillis());
}
@Override
public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap,
String loaderIsa) {
if (PLATFORM_PACKAGE_NAME.equals(loadingPackageName)
&& Binder.getCallingUid() != Process.SYSTEM_UID) {
Slog.w(TAG, "Non System Server process reporting dex loads as system server. uid="
+ Binder.getCallingUid());
// Do not record dex loads from processes pretending to be system server.
// Only the system server should be assigned the package "android", so reject calls
// that don't satisfy the constraint.
//
// notifyDexLoad is a PM API callable from the app process. So in theory, apps could
// craft calls to this API and pretend to be system server. Doing so poses no particular
// danger for dex load reporting or later dexopt, however it is a sensible check to do
// in order to verify the expectations.
return;
}
int userId = UserHandle.getCallingUserId();
ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
if (ai == null) {
Slog.w(TAG, "Loading a package that does not exist for the calling user. package="
+ loadingPackageName + ", user=" + userId);
return;
}
mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId);
}
@Override
public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule,
IDexModuleRegisterCallback callback) {
int userId = UserHandle.getCallingUserId();
ApplicationInfo ai = getApplicationInfo(packageName, /*flags*/ 0, userId);
DexManager.RegisterDexModuleResult result;
if (ai == null) {
Slog.w(TAG, "Registering a dex module for a package that does not exist for the" +
" calling user. package=" + packageName + ", user=" + userId);
result = new DexManager.RegisterDexModuleResult(false, "Package not installed");
} else {
result = mDexManager.registerDexModule(ai, dexModulePath, isSharedModule, userId);
}
if (callback != null) {
mHandler.post(() -> {
try {
callback.onDexModuleRegistered(dexModulePath, result.success, result.message);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to callback after module registration " + dexModulePath, e);
}
});
}
}
/**
* Ask the package manager to perform a dex-opt with the given compiler filter.
*
* Note: exposed only for the shell command to allow moving packages explicitly to a
* definite state.
*/
@Override
public boolean performDexOptMode(String packageName,
boolean checkProfiles, String targetCompilerFilter, boolean force,
boolean bootComplete, String splitName) {
int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) |
(force ? DexoptOptions.DEXOPT_FORCE : 0) |
(bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0);
return performDexOpt(new DexoptOptions(packageName, REASON_UNKNOWN,
targetCompilerFilter, splitName, flags));
}
/**
* Ask the package manager to perform a dex-opt with the given compiler filter on the
* secondary dex files belonging to the given package.
*
* Note: exposed only for the shell command to allow moving packages explicitly to a
* definite state.
*/
@Override
public boolean performDexOptSecondary(String packageName, String compilerFilter,
boolean force) {
int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES |
DexoptOptions.DEXOPT_BOOT_COMPLETE |
(force ? DexoptOptions.DEXOPT_FORCE : 0);
return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags));
}
/**
* Ask the package manager to compile layouts in the given package.
*/
@Override
public boolean compileLayouts(String packageName) {
AndroidPackage pkg;
synchronized (mLock) {
pkg = mPackages.get(packageName);
if (pkg == null) {
return false;
}
}
return mViewCompiler.compileLayouts(pkg);
}
/*package*/ boolean performDexOpt(DexoptOptions options) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return false;
} else if (isInstantApp(options.getPackageName(), UserHandle.getCallingUserId())) {
return false;
}
if (options.isDexoptOnlySecondaryDex()) {
return mDexManager.dexoptSecondaryDex(options);
} else {
int dexoptStatus = performDexOptWithStatus(options);
return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
}
}
/**
* Perform dexopt on the given package and return one of following result:
* {@link PackageDexOptimizer#DEX_OPT_SKIPPED}
* {@link PackageDexOptimizer#DEX_OPT_PERFORMED}
* {@link PackageDexOptimizer#DEX_OPT_FAILED}
*/
/* package */ int performDexOptWithStatus(DexoptOptions options) {
return performDexOptTraced(options);
}
private int performDexOptTraced(DexoptOptions options) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
try {
return performDexOptInternal(options);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
// Run dexopt on a given package. Returns true if dexopt did not fail, i.e.
// if the package can now be considered up to date for the given filter.
private int performDexOptInternal(DexoptOptions options) {
AndroidPackage p;
PackageSetting pkgSetting;
synchronized (mLock) {
p = mPackages.get(options.getPackageName());
pkgSetting = mSettings.getPackageLPr(options.getPackageName());
if (p == null || pkgSetting == null) {
// Package could not be found. Report failure.
return PackageDexOptimizer.DEX_OPT_FAILED;
}
mPackageUsage.maybeWriteAsync(mSettings.mPackages);
mCompilerStats.maybeWriteAsync();
}
long callingId = Binder.clearCallingIdentity();
try {
synchronized (mInstallLock) {
return performDexOptInternalWithDependenciesLI(p, pkgSetting, options);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
public ArraySet<String> getOptimizablePackages() {
ArraySet<String> pkgs = new ArraySet<>();
synchronized (mLock) {
for (AndroidPackage p : mPackages.values()) {
if (PackageDexOptimizer.canOptimizePackage(p)) {
pkgs.add(p.getPackageName());
}
}
}
return pkgs;
}
private int performDexOptInternalWithDependenciesLI(AndroidPackage p,
@NonNull PackageSetting pkgSetting, DexoptOptions options) {
// System server gets a special path.
if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) {
return mDexManager.dexoptSystemServer(options);
}
// Select the dex optimizer based on the force parameter.
// Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
// allocate an object here.
PackageDexOptimizer pdo = options.isForce()
? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer)
: mPackageDexOptimizer;
// Dexopt all dependencies first. Note: we ignore the return value and march on
// on errors.
// Note that we are going to call performDexOpt on those libraries as many times as
// they are referenced in packages. When we do a batch of performDexOpt (for example
// at boot, or background job), the passed 'targetCompilerFilter' stays the same,
// and the first package that uses the library will dexopt it. The
// others will see that the compiled code for the library is up to date.
Collection<SharedLibraryInfo> deps = findSharedLibraries(pkgSetting);
final String[] instructionSets = getAppDexInstructionSets(
AndroidPackageUtils.getPrimaryCpuAbi(p, pkgSetting),
AndroidPackageUtils.getSecondaryCpuAbi(p, pkgSetting));
if (!deps.isEmpty()) {
DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
options.getCompilationReason(), options.getCompilerFilter(),
options.getSplitName(),
options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
for (SharedLibraryInfo info : deps) {
AndroidPackage depPackage = null;
PackageSetting depPackageSetting = null;
synchronized (mLock) {
depPackage = mPackages.get(info.getPackageName());
depPackageSetting = mSettings.getPackageLPr(info.getPackageName());
}
if (depPackage != null && depPackageSetting != null) {
// TODO: Analyze and investigate if we (should) profile libraries.
pdo.performDexOpt(depPackage, depPackageSetting, instructionSets,
getOrCreateCompilerPackageStats(depPackage),
mDexManager.getPackageUseInfoOrDefault(depPackage.getPackageName()),
libraryOptions);
} else {
// TODO(ngeoffray): Support dexopting system shared libraries.
}
}
}
return pdo.performDexOpt(p, pkgSetting, instructionSets,
getOrCreateCompilerPackageStats(p),
mDexManager.getPackageUseInfoOrDefault(p.getPackageName()), options);
}
/**
* Reconcile the information we have about the secondary dex files belonging to
* {@code packageName} and the actual dex files. For all dex files that were
* deleted, update the internal records and delete the generated oat files.
*/
@Override
public void reconcileSecondaryDexFiles(String packageName) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return;
} else if (isInstantApp(packageName, UserHandle.getCallingUserId())) {
return;
}
mDexManager.reconcileSecondaryDexFiles(packageName);
}
// TODO(calin): this is only needed for BackgroundDexOptService. Find a cleaner way to inject
// a reference there.
/*package*/ DexManager getDexManager() {
return mDexManager;
}
/**
* Execute the background dexopt job immediately.
*/
@Override
public boolean runBackgroundDexoptJob(@Nullable List<String> packageNames) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return false;
}
enforceSystemOrRootOrShell("runBackgroundDexoptJob");
final long identity = Binder.clearCallingIdentity();
try {
return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
private static List<SharedLibraryInfo> findSharedLibraries(PackageSetting pkgSetting) {
if (!pkgSetting.getPkgState().getUsesLibraryInfos().isEmpty()) {
ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
Set<String> collectedNames = new HashSet<>();
for (SharedLibraryInfo info : pkgSetting.getPkgState().getUsesLibraryInfos()) {
findSharedLibrariesRecursive(info, retValue, collectedNames);
}
return retValue;
} else {
return Collections.emptyList();
}
}
private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
if (!collectedNames.contains(info.getName())) {
collectedNames.add(info.getName());
collected.add(info);
if (info.getDependencies() != null) {
for (SharedLibraryInfo dep : info.getDependencies()) {
findSharedLibrariesRecursive(dep, collected, collectedNames);
}
}
}
}
List<PackageSetting> findSharedNonSystemLibraries(PackageSetting pkgSetting) {
List<SharedLibraryInfo> deps = findSharedLibraries(pkgSetting);
if (!deps.isEmpty()) {
List<PackageSetting> retValue = new ArrayList<>();
synchronized (mLock) {
for (SharedLibraryInfo info : deps) {
PackageSetting depPackageSetting =
mSettings.getPackageLPr(info.getPackageName());
if (depPackageSetting != null && depPackageSetting.pkg != null) {
retValue.add(depPackageSetting);
}
}
}
return retValue;
} else {
return Collections.emptyList();
}
}
@Nullable
private SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
return getSharedLibraryInfo(name, version, mSharedLibraries, null);
}
@Nullable
private static SharedLibraryInfo getSharedLibraryInfo(String name, long version,
Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries,
@Nullable Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries) {
if (newLibraries != null) {
final LongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name);
SharedLibraryInfo info = null;
if (versionedLib != null) {
info = versionedLib.get(version);
}
if (info != null) {
return info;
}
}
final LongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name);
if (versionedLib == null) {
return null;
}
return versionedLib.get(version);
}
private SharedLibraryInfo getLatestSharedLibraVersionLPr(AndroidPackage pkg) {
LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
pkg.getStaticSharedLibName());
if (versionedLib == null) {
return null;
}
long previousLibVersion = -1;
final int versionCount = versionedLib.size();
for (int i = 0; i < versionCount; i++) {
final long libVersion = versionedLib.keyAt(i);
if (libVersion < pkg.getStaticSharedLibVersion()) {
previousLibVersion = Math.max(previousLibVersion, libVersion);
}
}
if (previousLibVersion >= 0) {
return versionedLib.get(previousLibVersion);
}
return null;
}
@Nullable
private PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) {
PackageSetting sharedLibPackage = null;
synchronized (mLock) {
final SharedLibraryInfo latestSharedLibraVersionLPr =
getLatestSharedLibraVersionLPr(scanResult.request.parsedPackage);
if (latestSharedLibraVersionLPr != null) {
sharedLibPackage = mSettings.getPackageLPr(
latestSharedLibraVersionLPr.getPackageName());
}
}
return sharedLibPackage;
}
public void shutdown() {
mPackageUsage.writeNow(mSettings.mPackages);
mCompilerStats.writeNow();
mDexManager.writePackageDexUsageNow();
PackageWatchdog.getInstance(mContext).writeNow();
// This is the last chance to write out pending restriction settings
synchronized (mLock) {
if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
for (int userId : mDirtyUsers) {
mSettings.writePackageRestrictionsLPr(userId);
}
mDirtyUsers.clear();
}
}
}
@Override
public void dumpProfiles(String packageName) {
AndroidPackage pkg;
synchronized (mLock) {
pkg = mPackages.get(packageName);
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
}
/* Only the shell, root, or the app user should be able to dump profiles. */
int callingUid = Binder.getCallingUid();
if (callingUid != Process.SHELL_UID &&
callingUid != Process.ROOT_UID &&
callingUid != pkg.getUid()) {
throw new SecurityException("dumpProfiles");
}
synchronized (mInstallLock) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles");
mArtManagerService.dumpProfiles(pkg);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
@Override
public void forceDexOpt(String packageName) {
enforceSystemOrRoot("forceDexOpt");
AndroidPackage pkg;
PackageSetting pkgSetting;
synchronized (mLock) {
pkg = mPackages.get(packageName);
pkgSetting = mSettings.getPackageLPr(packageName);
if (pkg == null || pkgSetting == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
}
synchronized (mInstallLock) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
// Whoever is calling forceDexOpt wants a compiled package.
// Don't use profiles since that may cause compilation to be skipped.
final int res = performDexOptInternalWithDependenciesLI(pkg, pkgSetting,
new DexoptOptions(packageName,
getDefaultCompilerFilter(),
DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
throw new IllegalStateException("Failed to dexopt: " + res);
}
}
}
@GuardedBy("mLock")
private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) {
if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
Slog.w(TAG, "Unable to update from " + oldPkg.name
+ " to " + newPkg.getPackageName()
+ ": old package not in system partition");
return false;
} else if (mPackages.get(oldPkg.name) != null) {
Slog.w(TAG, "Unable to update from " + oldPkg.name
+ " to " + newPkg.getPackageName()
+ ": old package still exists");
return false;
}
return true;
}
@GuardedBy("mInstallLock")
void removeCodePathLI(File codePath) {
if (codePath.isDirectory()) {
File codePathParent = codePath.getParentFile();
try {
mInstaller.rmPackageDir(codePath.getAbsolutePath());
if (codePathParent.getName().startsWith(RANDOM_DIR_PREFIX)) {
mInstaller.rmPackageDir(codePathParent.getAbsolutePath());
}
} catch (InstallerException e) {
Slog.w(TAG, "Failed to remove code path", e);
}
} else {
codePath.delete();
}
}
private int[] resolveUserIds(int userId) {
return (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[] { userId };
}
private void clearAppDataLIF(AndroidPackage pkg, int userId, int flags) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
clearAppDataLeafLIF(pkg, userId, flags);
if ((flags & Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES) == 0) {
clearAppProfilesLIF(pkg, UserHandle.USER_ALL);
}
}
private void clearAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) {
final PackageSetting ps;
synchronized (mLock) {
ps = mSettings.mPackages.get(pkg.getPackageName());
}
for (int realUserId : resolveUserIds(userId)) {
final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0;
try {
mInstaller.clearAppData(pkg.getVolumeUuid(), pkg.getPackageName(), realUserId,
flags, ceDataInode);
} catch (InstallerException e) {
Slog.w(TAG, String.valueOf(e));
}
}
}
private void destroyAppDataLIF(AndroidPackage pkg, int userId, int flags) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
destroyAppDataLeafLIF(pkg, userId, flags);
}
private void destroyAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) {
final PackageSetting ps;
synchronized (mLock) {
ps = mSettings.mPackages.get(pkg.getPackageName());
}
for (int realUserId : resolveUserIds(userId)) {
final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0;
try {
mInstaller.destroyAppData(pkg.getVolumeUuid(), pkg.getPackageName(), realUserId,
flags, ceDataInode);
} catch (InstallerException e) {
Slog.w(TAG, String.valueOf(e));
}
mDexManager.notifyPackageDataDestroyed(pkg.getPackageName(), userId);
}
}
private void destroyAppProfilesLIF(AndroidPackage pkg) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
destroyAppProfilesLeafLIF(pkg);
}
private void destroyAppProfilesLeafLIF(AndroidPackage pkg) {
try {
mInstaller.destroyAppProfiles(pkg.getPackageName());
} catch (InstallerException e) {
Slog.w(TAG, String.valueOf(e));
}
}
private void clearAppProfilesLIF(AndroidPackage pkg, int userId) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
mArtManagerService.clearAppProfiles(pkg);
}
@GuardedBy("mLock")
private void applyDefiningSharedLibraryUpdateLocked(
AndroidPackage pkg, SharedLibraryInfo libInfo,
BiConsumer<SharedLibraryInfo, SharedLibraryInfo> action) {
// Note that libraries defined by this package may be null if:
// - Package manager was unable to create the shared library. The package still
// gets installed, but the shared library does not get created.
// Or:
// - Package manager is in a state where package isn't scanned yet. This will
// get called again after scanning to fix the dependencies.
if (AndroidPackageUtils.isLibrary(pkg)) {
if (pkg.getStaticSharedLibName() != null) {
SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr(
pkg.getStaticSharedLibName(), pkg.getStaticSharedLibVersion());
if (definedLibrary != null) {
action.accept(definedLibrary, libInfo);
}
} else {
for (String libraryName : pkg.getLibraryNames()) {
SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr(
libraryName, SharedLibraryInfo.VERSION_UNDEFINED);
if (definedLibrary != null) {
action.accept(definedLibrary, libInfo);
}
}
}
}
}
@GuardedBy("mLock")
private void addSharedLibraryLPr(AndroidPackage pkg, Set<String> usesLibraryFiles,
SharedLibraryInfo libInfo, @Nullable AndroidPackage changingLib,
@Nullable PackageSetting changingLibSetting) {
if (libInfo.getPath() != null) {
usesLibraryFiles.add(libInfo.getPath());
return;
}
AndroidPackage pkgForCodePaths = mPackages.get(libInfo.getPackageName());
PackageSetting pkgSetting = mSettings.getPackageLPr(libInfo.getPackageName());
if (changingLib != null && changingLib.getPackageName().equals(libInfo.getPackageName())) {
// If we are doing this while in the middle of updating a library apk,
// then we need to make sure to use that new apk for determining the
// dependencies here. (We haven't yet finished committing the new apk
// to the package manager state.)
if (pkgForCodePaths == null
|| pkgForCodePaths.getPackageName().equals(changingLib.getPackageName())) {
pkgForCodePaths = changingLib;
pkgSetting = changingLibSetting;
}
}
if (pkgForCodePaths != null) {
usesLibraryFiles.addAll(AndroidPackageUtils.getAllCodePaths(pkgForCodePaths));
// If the package provides libraries, add the dependency to them.
applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, SharedLibraryInfo::addDependency);
if (pkgSetting != null) {
usesLibraryFiles.addAll(pkgSetting.getPkgState().getUsesLibraryFiles());
}
}
}
@GuardedBy("mLock")
private void updateSharedLibrariesLocked(AndroidPackage pkg, PackageSetting pkgSetting,
@Nullable AndroidPackage changingLib, @Nullable PackageSetting changingLibSetting,
Map<String, AndroidPackage> availablePackages)
throws PackageManagerException {
final ArrayList<SharedLibraryInfo> sharedLibraryInfos = collectSharedLibraryInfos(
pkgSetting.pkg, availablePackages, mSharedLibraries, null);
executeSharedLibrariesUpdateLPr(pkg, pkgSetting, changingLib, changingLibSetting,
sharedLibraryInfos, mUserManager.getUserIds());
}
private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(AndroidPackage pkg,
Map<String, AndroidPackage> availablePackages,
@NonNull final Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries,
@Nullable final Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries)
throws PackageManagerException {
if (pkg == null) {
return null;
}
// The collection used here must maintain the order of addition (so
// that libraries are searched in the correct order) and must have no
// duplicates.
ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
if (!pkg.getUsesLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null,
pkg.getPackageName(), true, pkg.getTargetSdkVersion(), null,
availablePackages, existingLibraries, newLibraries);
}
if (!pkg.getUsesStaticLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
pkg.getPackageName(), true, pkg.getTargetSdkVersion(), usesLibraryInfos,
availablePackages, existingLibraries, newLibraries);
}
if (!pkg.getUsesOptionalLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(),
null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
}
return usesLibraryInfos;
}
private void executeSharedLibrariesUpdateLPr(AndroidPackage pkg,
@NonNull PackageSetting pkgSetting, @Nullable AndroidPackage changingLib,
@Nullable PackageSetting changingLibSetting,
ArrayList<SharedLibraryInfo> usesLibraryInfos, int[] allUsers) {
// If the package provides libraries, clear their old dependencies.
// This method will set them up again.
applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> {
definingLibrary.clearDependencies();
});
if (usesLibraryInfos != null) {
pkgSetting.getPkgState().setUsesLibraryInfos(usesLibraryInfos);
// Use LinkedHashSet to preserve the order of files added to
// usesLibraryFiles while eliminating duplicates.
Set<String> usesLibraryFiles = new LinkedHashSet<>();
for (SharedLibraryInfo libInfo : usesLibraryInfos) {
addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib,
changingLibSetting);
}
pkgSetting.getPkgState().setUsesLibraryFiles(new ArrayList<>(usesLibraryFiles));
// let's make sure we mark all static shared libraries as installed for the same users
// that its dependent packages are installed for.
int[] installedUsers = new int[allUsers.length];
int installedUserCount = 0;
for (int u = 0; u < allUsers.length; u++) {
if (pkgSetting.getInstalled(allUsers[u])) {
installedUsers[installedUserCount++] = allUsers[u];
}
}
for (SharedLibraryInfo sharedLibraryInfo : usesLibraryInfos) {
if (!sharedLibraryInfo.isStatic()) {
continue;
}
final PackageSetting staticLibPkgSetting = getPackageSetting(
toStaticSharedLibraryPackageName(sharedLibraryInfo.getPackageName(),
sharedLibraryInfo.getLongVersion()));
if (staticLibPkgSetting == null) {
Slog.wtf(TAG, "Shared lib without setting: " + sharedLibraryInfo);
continue;
}
for (int u = 0; u < installedUserCount; u++) {
staticLibPkgSetting.setInstalled(true, installedUsers[u]);
}
}
} else {
pkgSetting.getPkgState().setUsesLibraryInfos(Collections.emptyList())
.setUsesLibraryFiles(Collections.emptyList());
}
}
@GuardedBy("mLock")
private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
@NonNull List<String> requestedLibraries,
@Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
@NonNull String packageName, boolean required, int targetSdk,
@Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
@NonNull final Map<String, AndroidPackage> availablePackages,
@NonNull final Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries,
@Nullable final Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries)
throws PackageManagerException {
final int libCount = requestedLibraries.size();
for (int i = 0; i < libCount; i++) {
final String libName = requestedLibraries.get(i);
final long libVersion = requiredVersions != null ? requiredVersions[i]
: SharedLibraryInfo.VERSION_UNDEFINED;
final SharedLibraryInfo libraryInfo = getSharedLibraryInfo(libName, libVersion,
existingLibraries, newLibraries);
if (libraryInfo == null) {
if (required) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
"Package " + packageName + " requires unavailable shared library "
+ libName + "; failing!");
} else if (DEBUG_SHARED_LIBRARIES) {
Slog.i(TAG, "Package " + packageName
+ " desires unavailable shared library "
+ libName + "; ignoring!");
}
} else {
if (requiredVersions != null && requiredCertDigests != null) {
if (libraryInfo.getLongVersion() != requiredVersions[i]) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
"Package " + packageName + " requires unavailable static shared"
+ " library " + libName + " version "
+ libraryInfo.getLongVersion() + "; failing!");
}
AndroidPackage pkg = availablePackages.get(libraryInfo.getPackageName());
SigningDetails libPkg = pkg == null ? null : pkg.getSigningDetails();
if (libPkg == null) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
"Package " + packageName + " requires unavailable static shared"
+ " library; failing!");
}
final String[] expectedCertDigests = requiredCertDigests[i];
if (expectedCertDigests.length > 1) {
// For apps targeting O MR1 we require explicit enumeration of all certs.
final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1)
? PackageUtils.computeSignaturesSha256Digests(
libPkg.signatures)
: PackageUtils.computeSignaturesSha256Digests(
new Signature[]{libPkg.signatures[0]});
// Take a shortcut if sizes don't match. Note that if an app doesn't
// target O we don't parse the "additional-certificate" tags similarly
// how we only consider all certs only for apps targeting O (see above).
// Therefore, the size check is safe to make.
if (expectedCertDigests.length != libCertDigests.length) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
"Package " + packageName + " requires differently signed" +
" static shared library; failing!");
}
// Use a predictable order as signature order may vary
Arrays.sort(libCertDigests);
Arrays.sort(expectedCertDigests);
final int certCount = libCertDigests.length;
for (int j = 0; j < certCount; j++) {
if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) {
throw new PackageManagerException(
INSTALL_FAILED_MISSING_SHARED_LIBRARY,
"Package " + packageName + " requires differently signed" +
" static shared library; failing!");
}
}
} else {
// lib signing cert could have rotated beyond the one expected, check to see
// if the new one has been blessed by the old
byte[] digestBytes = HexEncoding.decode(
expectedCertDigests[0], false /* allowSingleChar */);
if (!libPkg.hasSha256Certificate(digestBytes)) {
throw new PackageManagerException(
INSTALL_FAILED_MISSING_SHARED_LIBRARY,
"Package " + packageName + " requires differently signed" +
" static shared library; failing!");
}
}
}
if (outUsedLibraries == null) {
outUsedLibraries = new ArrayList<>();
}
outUsedLibraries.add(libraryInfo);
}
}
return outUsedLibraries;
}
private static boolean hasString(List<String> list, List<String> which) {
if (list == null || which == null) {
return false;
}
for (int i=list.size()-1; i>=0; i--) {
for (int j=which.size()-1; j>=0; j--) {
if (which.get(j).equals(list.get(i))) {
return true;
}
}
}
return false;
}
@GuardedBy("mLock")
private ArrayList<AndroidPackage> updateAllSharedLibrariesLocked(
@Nullable AndroidPackage updatedPkg, @Nullable PackageSetting updatedPkgSetting,
Map<String, AndroidPackage> availablePackages) {
ArrayList<AndroidPackage> resultList = null;
// Set of all descendants of a library; used to eliminate cycles
ArraySet<String> descendants = null;
// The current list of packages that need updating
List<Pair<AndroidPackage, PackageSetting>> needsUpdating = null;
if (updatedPkg != null && updatedPkgSetting != null) {
needsUpdating = new ArrayList<>(1);
needsUpdating.add(Pair.create(updatedPkg, updatedPkgSetting));
}
do {
final Pair<AndroidPackage, PackageSetting> changingPkgPair =
(needsUpdating == null) ? null : needsUpdating.remove(0);
final AndroidPackage changingPkg = changingPkgPair != null
? changingPkgPair.first : null;
final PackageSetting changingPkgSetting = changingPkgPair != null
? changingPkgPair.second : null;
for (int i = mPackages.size() - 1; i >= 0; --i) {
final AndroidPackage pkg = mPackages.valueAt(i);
final PackageSetting pkgSetting = mSettings.getPackageLPr(pkg.getPackageName());
if (changingPkg != null
&& !hasString(pkg.getUsesLibraries(), changingPkg.getLibraryNames())
&& !hasString(pkg.getUsesOptionalLibraries(), changingPkg.getLibraryNames())
&& !ArrayUtils.contains(pkg.getUsesStaticLibraries(),
changingPkg.getStaticSharedLibName())) {
continue;
}
if (resultList == null) {
resultList = new ArrayList<>();
}
resultList.add(pkg);
// if we're updating a shared library, all of its descendants must be updated
if (changingPkg != null) {
if (descendants == null) {
descendants = new ArraySet<>();
}
if (!descendants.contains(pkg.getPackageName())) {
descendants.add(pkg.getPackageName());
needsUpdating.add(Pair.create(pkg, pkgSetting));
}
}
try {
updateSharedLibrariesLocked(pkg, pkgSetting, changingPkg,
changingPkgSetting, availablePackages);
} catch (PackageManagerException e) {
// If a system app update or an app and a required lib missing we
// delete the package and for updated system apps keep the data as
// it is better for the user to reinstall than to be in an limbo
// state. Also libs disappearing under an app should never happen
// - just in case.
if (!pkg.isSystem() || pkgSetting.getPkgState().isUpdatedSystemApp()) {
final int flags = pkgSetting.getPkgState().isUpdatedSystemApp()
? PackageManager.DELETE_KEEP_DATA : 0;
deletePackageLIF(pkg.getPackageName(), null, true,
mUserManager.getUserIds(), flags, null,
true, null);
}
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
}
}
} while (needsUpdating != null && needsUpdating.size() > 0);
return resultList;
}
private int getVendorPartitionVersion() {
final String version = SystemProperties.get("ro.vndk.version");
if (!version.isEmpty()) {
try {
return Integer.parseInt(version);
} catch (NumberFormatException ignore) {
if (ArrayUtils.contains(Build.VERSION.ACTIVE_CODENAMES, version)) {
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
}
}
return Build.VERSION_CODES.P;
}
@GuardedBy({"mInstallLock", "mLock"})
private ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
try {
return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
cpuAbiOverride);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
/** The result of a package scan. */
@VisibleForTesting
static class ScanResult {
/** The request that initiated the scan that produced this result. */
public final ScanRequest request;
/** Whether or not the package scan was successful */
public final boolean success;
/**
* Whether or not the original PackageSetting needs to be updated with this result on
* commit.
*/
public final boolean existingSettingCopied;
/**
* The final package settings. This may be the same object passed in
* the {@link ScanRequest}, but, with modified values.
*/
@Nullable public final PackageSetting pkgSetting;
/** ABI code paths that have changed in the package scan */
@Nullable public final List<String> changedAbiCodePath;
public final SharedLibraryInfo staticSharedLibraryInfo;
public final List<SharedLibraryInfo> dynamicSharedLibraryInfos;
public ScanResult(
ScanRequest request, boolean success,
@Nullable PackageSetting pkgSetting,
@Nullable List<String> changedAbiCodePath, boolean existingSettingCopied,
SharedLibraryInfo staticSharedLibraryInfo,
List<SharedLibraryInfo> dynamicSharedLibraryInfos) {
this.request = request;
this.success = success;
this.pkgSetting = pkgSetting;
this.changedAbiCodePath = changedAbiCodePath;
this.existingSettingCopied = existingSettingCopied;
this.staticSharedLibraryInfo = staticSharedLibraryInfo;
this.dynamicSharedLibraryInfos = dynamicSharedLibraryInfos;
}
}
/** A package to be scanned */
@VisibleForTesting
static class ScanRequest {
/** The parsed package */
@NonNull public final ParsedPackage parsedPackage;
/** The package this package replaces */
@Nullable public final AndroidPackage oldPkg;
/** Shared user settings, if the package has a shared user */
@Nullable public final SharedUserSetting sharedUserSetting;
/**
* Package settings of the currently installed version.
* <p><em>IMPORTANT:</em> The contents of this object may be modified
* during scan.
*/
@Nullable public final PackageSetting pkgSetting;
/** A copy of the settings for the currently installed version */
@Nullable public final PackageSetting oldPkgSetting;
/** Package settings for the disabled version on the /system partition */
@Nullable public final PackageSetting disabledPkgSetting;
/** Package settings for the installed version under its original package name */
@Nullable public final PackageSetting originalPkgSetting;
/** The real package name of a renamed application */
@Nullable public final String realPkgName;
public final @ParseFlags int parseFlags;
public final @ScanFlags int scanFlags;
/** The user for which the package is being scanned */
@Nullable public final UserHandle user;
/** Whether or not the platform package is being scanned */
public final boolean isPlatformPackage;
/** Override value for package ABI if set during install */
@Nullable
public final String cpuAbiOverride;
public ScanRequest(
@NonNull ParsedPackage parsedPackage,
@Nullable SharedUserSetting sharedUserSetting,
@Nullable AndroidPackage oldPkg,
@Nullable PackageSetting pkgSetting,
@Nullable PackageSetting disabledPkgSetting,
@Nullable PackageSetting originalPkgSetting,
@Nullable String realPkgName,
@ParseFlags int parseFlags,
@ScanFlags int scanFlags,
boolean isPlatformPackage,
@Nullable UserHandle user,
@Nullable String cpuAbiOverride) {
this.parsedPackage = parsedPackage;
this.oldPkg = oldPkg;
this.pkgSetting = pkgSetting;
this.sharedUserSetting = sharedUserSetting;
this.oldPkgSetting = pkgSetting == null ? null : new PackageSetting(pkgSetting);
this.disabledPkgSetting = disabledPkgSetting;
this.originalPkgSetting = originalPkgSetting;
this.realPkgName = realPkgName;
this.parseFlags = parseFlags;
this.scanFlags = scanFlags;
this.isPlatformPackage = isPlatformPackage;
this.user = user;
this.cpuAbiOverride = cpuAbiOverride;
}
}
/**
* Returns the actual scan flags depending upon the state of the other settings.
* <p>Updated system applications will not have the following flags set
* by default and need to be adjusted after the fact:
* <ul>
* <li>{@link #SCAN_AS_SYSTEM}</li>
* <li>{@link #SCAN_AS_PRIVILEGED}</li>
* <li>{@link #SCAN_AS_OEM}</li>
* <li>{@link #SCAN_AS_VENDOR}</li>
* <li>{@link #SCAN_AS_PRODUCT}</li>
* <li>{@link #SCAN_AS_SYSTEM_EXT}</li>
* <li>{@link #SCAN_AS_INSTANT_APP}</li>
* <li>{@link #SCAN_AS_VIRTUAL_PRELOAD}</li>
* <li>{@link #SCAN_AS_ODM}</li>
* </ul>
*/
private @ScanFlags int adjustScanFlags(@ScanFlags int scanFlags,
PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
AndroidPackage pkg) {
// TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain
// the correct isSystem value now that we don't disable system packages before scan.
final PackageSetting systemPkgSetting =
(scanFlags & SCAN_NEW_INSTALL) != 0 && disabledPkgSetting == null
&& pkgSetting != null && pkgSetting.isSystem()
? pkgSetting
: disabledPkgSetting;
if (systemPkgSetting != null) {
// updated system application, must at least have SCAN_AS_SYSTEM
scanFlags |= SCAN_AS_SYSTEM;
if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
scanFlags |= SCAN_AS_OEM;
}
if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
scanFlags |= SCAN_AS_VENDOR;
}
if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
scanFlags |= SCAN_AS_PRODUCT;
}
if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0) {
scanFlags |= SCAN_AS_SYSTEM_EXT;
}
if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_ODM) != 0) {
scanFlags |= SCAN_AS_ODM;
}
}
if (pkgSetting != null) {
final int userId = ((user == null) ? 0 : user.getIdentifier());
if (pkgSetting.getInstantApp(userId)) {
scanFlags |= SCAN_AS_INSTANT_APP;
}
if (pkgSetting.getVirtulalPreload(userId)) {
scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
}
}
// Scan as privileged apps that share a user with a priv-app.
final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0)
&& getVendorPartitionVersion() < 28;
if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
&& !pkg.isPrivileged()
&& (pkg.getSharedUserId() != null)
&& !skipVendorPrivilegeScan) {
SharedUserSetting sharedUserSetting = null;
try {
sharedUserSetting = mSettings.getSharedUserLPw(pkg.getSharedUserId(), 0,
0, false);
} catch (PackageManagerException ignore) {
}
if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
// Exempt SharedUsers signed with the platform key.
// TODO(b/72378145) Fix this exemption. Force signature apps
// to whitelist their privileged permissions just like other
// priv-apps.
synchronized (mLock) {
PackageSetting platformPkgSetting = mSettings.mPackages.get("android");
if ((compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures,
pkg.getSigningDetails().signatures)
!= PackageManager.SIGNATURE_MATCH)) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
}
}
}
return scanFlags;
}
// TODO: scanPackageNewLI() and scanPackageOnly() should be merged. But, first, commiting
// the results / removing app data needs to be moved up a level to the callers of this
// method. Also, we need to solve the problem of potentially creating a new shared user
// setting. That can probably be done later and patch things up after the fact.
@GuardedBy({"mInstallLock", "mLock"})
private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
final String renamedPkgName = mSettings.getRenamedPackageLPr(
parsedPackage.getRealPackage());
final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);
if (realPkgName != null) {
ensurePackageRenamed(parsedPackage, renamedPkgName);
}
final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
renamedPkgName);
final PackageSetting pkgSetting = mSettings.getPackageLPr(parsedPackage.getPackageName());
final PackageSetting disabledPkgSetting =
mSettings.getDisabledSystemPkgLPr(parsedPackage.getPackageName());
if (mTransferredPackages.contains(parsedPackage.getPackageName())) {
Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ " was transferred to another, but its .apk remains");
}
scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, parsedPackage);
synchronized (mLock) {
boolean isUpdatedSystemApp;
if (pkgSetting != null) {
isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
} else {
isUpdatedSystemApp = disabledPkgSetting != null;
}
applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage, isUpdatedSystemApp);
assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
SharedUserSetting sharedUserSetting = null;
if (parsedPackage.getSharedUserId() != null) {
// SIDE EFFECTS; may potentially allocate a new shared user
sharedUserSetting = mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
if (DEBUG_PACKAGE_SCANNING) {
if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
+ " (uid=" + sharedUserSetting.userId + "):"
+ " packages=" + sharedUserSetting.packages);
}
}
String platformPackageName = mPlatformPackage == null
? null : mPlatformPackage.getPackageName();
final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
originalPkgSetting, realPkgName, parseFlags, scanFlags,
Objects.equals(parsedPackage.getPackageName(), platformPackageName), user,
cpuAbiOverride);
return scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime);
}
}
/**
* Prepares the system to commit a {@link ScanResult} in a way that will not fail by registering
* the app ID required for reconcile.
* @return {@code true} if a new app ID was registered and will need to be cleaned up on
* failure.
*/
private boolean optimisticallyRegisterAppId(@NonNull ScanResult result)
throws PackageManagerException {
if (!result.existingSettingCopied) {
// THROWS: when we can't allocate a user id. add call to check if there's
// enough space to ensure we won't throw; otherwise, don't modify state
return mSettings.registerAppIdLPw(result.pkgSetting);
}
return false;
}
/**
* Reverts any app ID creation that were made by
* {@link #optimisticallyRegisterAppId(ScanResult)}. Note: this is only necessary if the
* referenced method returned true.
*/
private void cleanUpAppIdCreation(@NonNull ScanResult result) {
// iff we've acquired an app ID for a new package setting, remove it so that it can be
// acquired by another request.
if (result.pkgSetting.appId > 0) {
mSettings.removeAppIdLPw(result.pkgSetting.appId);
}
}
/**
* Commits the package scan and modifies system state.
* <p><em>WARNING:</em> The method may throw an excpetion in the middle
* of committing the package, leaving the system in an inconsistent state.
* This needs to be fixed so, once we get to this point, no errors are
* possible and the system is not left in an inconsistent state.
*/
@GuardedBy({"mLock", "mInstallLock"})
private AndroidPackage commitReconciledScanResultLocked(
@NonNull ReconciledPackage reconciledPkg, int[] allUsers) {
final ScanResult result = reconciledPkg.scanResult;
final ScanRequest request = result.request;
// TODO(b/135203078): Move this even further away
ParsedPackage parsedPackage = request.parsedPackage;
if ("android".equals(parsedPackage.getPackageName())) {
// TODO(b/135203078): Move this to initial parse
parsedPackage.setVersionCode(mSdkVersion)
.setVersionCodeMajor(0);
}
final AndroidPackage oldPkg = request.oldPkg;
final @ParseFlags int parseFlags = request.parseFlags;
final @ScanFlags int scanFlags = request.scanFlags;
final PackageSetting oldPkgSetting = request.oldPkgSetting;
final PackageSetting originalPkgSetting = request.originalPkgSetting;
final UserHandle user = request.user;
final String realPkgName = request.realPkgName;
final List<String> changedAbiCodePath = result.changedAbiCodePath;
final PackageSetting pkgSetting;
if (request.pkgSetting != null && request.pkgSetting.sharedUser != null
&& request.pkgSetting.sharedUser != result.pkgSetting.sharedUser) {
// shared user changed, remove from old shared user
request.pkgSetting.sharedUser.removePackage(request.pkgSetting);
}
if (result.existingSettingCopied) {
pkgSetting = request.pkgSetting;
pkgSetting.updateFrom(result.pkgSetting);
} else {
pkgSetting = result.pkgSetting;
if (originalPkgSetting != null) {
mSettings.addRenamedPackageLPw(parsedPackage.getRealPackage(),
originalPkgSetting.name);
mTransferredPackages.add(originalPkgSetting.name);
} else {
mSettings.removeRenamedPackageLPw(parsedPackage.getPackageName());
}
}
if (pkgSetting.sharedUser != null) {
pkgSetting.sharedUser.addPackage(pkgSetting);
}
if (reconciledPkg.installArgs != null && reconciledPkg.installArgs.forceQueryableOverride) {
pkgSetting.forceQueryableOverride = true;
}
// If this is part of a standard install, set the initiating package name, else rely on
// previous device state.
if (reconciledPkg.installArgs != null) {
InstallSource installSource = reconciledPkg.installArgs.installSource;
if (installSource.initiatingPackageName != null) {
final PackageSetting ips = mSettings.mPackages.get(
installSource.initiatingPackageName);
if (ips != null) {
installSource = installSource.setInitiatingPackageSignatures(
ips.signatures);
}
}
pkgSetting.setInstallSource(installSource);
}
// TODO(toddke): Consider a method specifically for modifying the Package object
// post scan; or, moving this stuff out of the Package object since it has nothing
// to do with the package on disk.
// We need to have this here because addUserToSettingLPw() is sometimes responsible
// for creating the application ID. If we did this earlier, we would be saving the
// correct ID.
parsedPackage.setUid(pkgSetting.appId);
final AndroidPackage pkg = parsedPackage.hideAsFinal();
mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
if (realPkgName != null) {
mTransferredPackages.add(pkg.getPackageName());
}
if (reconciledPkg.collectedSharedLibraryInfos != null) {
executeSharedLibrariesUpdateLPr(pkg, pkgSetting, null, null,
reconciledPkg.collectedSharedLibraryInfos, allUsers);
}
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
if (reconciledPkg.removeAppKeySetData) {
ksms.removeAppKeySetDataLPw(pkg.getPackageName());
}
if (reconciledPkg.sharedUserSignaturesChanged) {
pkgSetting.sharedUser.signaturesChanged = Boolean.TRUE;
pkgSetting.sharedUser.signatures.mSigningDetails = reconciledPkg.signingDetails;
}
pkgSetting.signatures.mSigningDetails = reconciledPkg.signingDetails;
if (!pkg.getAdoptPermissions().isEmpty()) {
// This package wants to adopt ownership of permissions from
// another package.
for (int i = pkg.getAdoptPermissions().size() - 1; i >= 0; i--) {
final String origName = pkg.getAdoptPermissions().get(i);
final PackageSetting orig = mSettings.getPackageLPr(origName);
if (orig != null) {
if (verifyPackageUpdateLPr(orig, pkg)) {
Slog.i(TAG, "Adopting permissions from " + origName + " to "
+ pkg.getPackageName());
mSettings.mPermissions.transferPermissions(origName, pkg.getPackageName());
}
}
}
}
if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
final String codePathString = changedAbiCodePath.get(i);
try {
mInstaller.rmdex(codePathString,
getDexCodeInstructionSet(getPreferredInstructionSet()));
} catch (InstallerException ignored) {
}
}
}
final int userId = user == null ? 0 : user.getIdentifier();
// Modify state for the given package setting
commitPackageSettings(pkg, oldPkg, pkgSetting, scanFlags,
(parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
if (pkgSetting.getInstantApp(userId)) {
mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
}
return pkg;
}
/**
* Returns the "real" name of the package.
* <p>This may differ from the package's actual name if the application has already
* been installed under one of this package's original names.
*/
private static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
@Nullable String renamedPkgName) {
if (isPackageRenamed(pkg, renamedPkgName)) {
return pkg.getRealPackage();
}
return null;
}
/** Returns {@code true} if the package has been renamed. Otherwise, {@code false}. */
private static boolean isPackageRenamed(@NonNull AndroidPackage pkg,
@Nullable String renamedPkgName) {
return pkg.getOriginalPackages().contains(renamedPkgName);
}
/**
* Returns the original package setting.
* <p>A package can migrate its name during an update. In this scenario, a package
* designates a set of names that it considers as one of its original names.
* <p>An original package must be signed identically and it must have the same
* shared user [if any].
*/
@GuardedBy("mLock")
private @Nullable PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
@Nullable String renamedPkgName) {
if (isPackageRenamed(pkg, renamedPkgName)) {
return null;
}
for (int i = ArrayUtils.size(pkg.getOriginalPackages()) - 1; i >= 0; --i) {
final PackageSetting originalPs =
mSettings.getPackageLPr(pkg.getOriginalPackages().get(i));
if (originalPs != null) {
// the package is already installed under its original name...
// but, should we use it?
if (!verifyPackageUpdateLPr(originalPs, pkg)) {
// the new package is incompatible with the original
continue;
} else if (originalPs.sharedUser != null) {
if (!originalPs.sharedUser.name.equals(pkg.getSharedUserId())) {
// the shared user id is incompatible with the original
Slog.w(TAG, "Unable to migrate data from " + originalPs.name
+ " to " + pkg.getPackageName() + ": old uid "
+ originalPs.sharedUser.name
+ " differs from " + pkg.getSharedUserId());
continue;
}
// TODO: Add case when shared user id is added [b/28144775]
} else {
if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
+ pkg.getPackageName() + " to old name " + originalPs.name);
}
return originalPs;
}
}
return null;
}
/**
* Renames the package if it was installed under a different name.
* <p>When we've already installed the package under an original name, update
* the new package so we can continue to have the old name.
*/
private static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
@NonNull String renamedPackageName) {
if (!parsedPackage.getOriginalPackages().contains(renamedPackageName)
|| parsedPackage.getPackageName().equals(renamedPackageName)) {
return;
}
parsedPackage.setPackageName(renamedPackageName);
}
/**
* Applies the adjusted ABI calculated by
* {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, AndroidPackage)} to all
* relevant packages and settings.
* @param sharedUserSetting The {@code SharedUserSetting} to adjust
* @param scannedPackage the package being scanned or null
* @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper}
* @return the list of code paths that belong to packages that had their ABIs adjusted.
*/
private static List<String> applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting,
ParsedPackage scannedPackage, String adjustedAbi) {
if (scannedPackage != null) {
scannedPackage.setPrimaryCpuAbi(adjustedAbi);
}
List<String> changedAbiCodePath = null;
for (PackageSetting ps : sharedUserSetting.packages) {
if (scannedPackage == null || !scannedPackage.getPackageName().equals(ps.name)) {
if (ps.primaryCpuAbiString != null) {
continue;
}
ps.primaryCpuAbiString = adjustedAbi;
if (ps.pkg != null) {
if (!TextUtils.equals(adjustedAbi,
AndroidPackageUtils.getRawPrimaryCpuAbi(ps.pkg))) {
if (DEBUG_ABI_SELECTION) {
Slog.i(TAG,
"Adjusting ABI for " + ps.name + " to " + adjustedAbi
+ " (scannedPackage="
+ (scannedPackage != null ? scannedPackage : "null")
+ ")");
}
if (changedAbiCodePath == null) {
changedAbiCodePath = new ArrayList<>();
}
changedAbiCodePath.add(ps.codePathString);
}
}
}
}
return changedAbiCodePath;
}
/**
* Sets the enabled state of components configured through {@link SystemConfig}.
* This modifies the {@link PackageSetting} object.
*
* TODO(b/135203078): Move this to package parsing
**/
static void configurePackageComponents(AndroidPackage pkg) {
final ArrayMap<String, Boolean> componentsEnabledStates = SystemConfig.getInstance()
.getComponentsEnabledStates(pkg.getPackageName());
if (componentsEnabledStates == null) {
return;
}
for (int i = ArrayUtils.size(pkg.getActivities()) - 1; i >= 0; i--) {
final ParsedActivity component = pkg.getActivities().get(i);
final Boolean enabled = componentsEnabledStates.get(component.getName());
if (enabled != null) {
component.setEnabled(enabled);
}
}
for (int i = ArrayUtils.size(pkg.getReceivers()) - 1; i >= 0; i--) {
final ParsedActivity component = pkg.getReceivers().get(i);
final Boolean enabled = componentsEnabledStates.get(component.getName());
if (enabled != null) {
component.setEnabled(enabled);
}
}
for (int i = ArrayUtils.size(pkg.getProviders()) - 1; i >= 0; i--) {
final ParsedProvider component = pkg.getProviders().get(i);
final Boolean enabled = componentsEnabledStates.get(component.getName());
if (enabled != null) {
component.setEnabled(enabled);
}
}
for (int i = ArrayUtils.size(pkg.getServices()) - 1; i >= 0; i--) {
final ParsedService component = pkg.getServices().get(i);
final Boolean enabled = componentsEnabledStates.get(component.getName());
if (enabled != null) {
component.setEnabled(enabled);
}
}
}
/**
* Just scans the package without any side effects.
* <p>Not entirely true at the moment. There is still one side effect -- this
* method potentially modifies a live {@link PackageSetting} object representing
* the package being scanned. This will be resolved in the future.
*
* @param injector injector for acquiring dependencies
* @param request Information about the package to be scanned
* @param isUnderFactoryTest Whether or not the device is under factory test
* @param currentTime The current time, in millis
* @return The results of the scan
*/
@GuardedBy("mInstallLock")
@VisibleForTesting
@NonNull
static ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
Injector injector,
boolean isUnderFactoryTest, long currentTime)
throws PackageManagerException {
final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
final UserManagerInternal userManager = injector.getUserManagerInternal();
ParsedPackage parsedPackage = request.parsedPackage;
PackageSetting pkgSetting = request.pkgSetting;
final PackageSetting disabledPkgSetting = request.disabledPkgSetting;
final PackageSetting originalPkgSetting = request.originalPkgSetting;
final @ParseFlags int parseFlags = request.parseFlags;
final @ScanFlags int scanFlags = request.scanFlags;
final String realPkgName = request.realPkgName;
final SharedUserSetting sharedUserSetting = request.sharedUserSetting;
final UserHandle user = request.user;
final boolean isPlatformPackage = request.isPlatformPackage;
List<String> changedAbiCodePath = null;
if (DEBUG_PACKAGE_SCANNING) {
if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());
}
// Initialize package source and resource directories
final File destCodeFile = new File(parsedPackage.getCodePath());
final File destResourceFile = new File(parsedPackage.getCodePath());
// We keep references to the derived CPU Abis from settings in oder to reuse
// them in the case where we're not upgrading or booting for the first time.
String primaryCpuAbiFromSettings = null;
String secondaryCpuAbiFromSettings = null;
boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
if (!needToDeriveAbi) {
if (pkgSetting != null) {
// TODO(b/154610922): if it is not first boot or upgrade, we should directly use
// API info from existing package setting. However, stub packages currently do not
// preserve ABI info, thus the special condition check here. Remove the special
// check after we fix the stub generation.
if (pkgSetting.pkg != null && pkgSetting.pkg.isStub()) {
needToDeriveAbi = true;
} else {
primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString;
secondaryCpuAbiFromSettings = pkgSetting.secondaryCpuAbiString;
}
} else {
// Re-scanning a system package after uninstalling updates; need to derive ABI
needToDeriveAbi = true;
}
}
if (pkgSetting != null && pkgSetting.sharedUser != sharedUserSetting) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Package " + parsedPackage.getPackageName() + " shared user changed from "
+ (pkgSetting.sharedUser != null
? pkgSetting.sharedUser.name : "<nothing>")
+ " to "
+ (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
+ "; replacing with new");
pkgSetting = null;
}
String[] usesStaticLibraries = null;
if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {
usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);
}
// TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans
// to avoid adding something that's unsupported due to lack of state, since it's called
// with null.
final boolean createNewPackage = (pkgSetting == null);
if (createNewPackage) {
final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
// Flags contain system values stored in the server variant of AndroidPackage,
// and so the server-side PackageInfoUtils is still called, even without a
// PackageSetting to pass in.
int pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, null);
int pkgPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(parsedPackage, null);
// REMOVE SharedUserSetting from method; update in a separate call
pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
destCodeFile, destResourceFile, parsedPackage.getNativeLibraryRootDir(),
AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
parsedPackage.getVersionCode(), pkgFlags, pkgPrivateFlags, user,
true /*allowInstall*/, instantApp, virtualPreload,
UserManagerService.getInstance(), usesStaticLibraries,
parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups());
} else {
// make a deep copy to avoid modifying any existing system state.
pkgSetting = new PackageSetting(pkgSetting);
pkgSetting.pkg = parsedPackage;
// REMOVE SharedUserSetting from method; update in a separate call.
//
// TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
// secondaryCpuAbi are not known at this point so we always update them
// to null here, only to reset them at a later point.
Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
destCodeFile, destResourceFile, parsedPackage.getNativeLibraryDir(),
AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
UserManagerService.getInstance(),
usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
parsedPackage.getMimeGroups());
}
if (createNewPackage && originalPkgSetting != null) {
// This is the initial transition from the original package, so,
// fix up the new package's name now. We must do this after looking
// up the package under its new name, so getPackageLP takes care of
// fiddling things correctly.
parsedPackage.setPackageName(originalPkgSetting.name);
// File a report about this.
String msg = "New package " + pkgSetting.realName
+ " renamed to replace old package " + pkgSetting.name;
reportSettingsProblem(Log.WARN, msg);
}
final int userId = (user == null ? UserHandle.USER_SYSTEM : user.getIdentifier());
// for existing packages, change the install state; but, only if it's explicitly specified
if (!createNewPackage) {
final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
setInstantAppForUser(injector, pkgSetting, userId, instantApp, fullApp);
}
// TODO(patb): see if we can do away with disabled check here.
if (disabledPkgSetting != null
|| (0 != (scanFlags & SCAN_NEW_INSTALL)
&& pkgSetting != null && pkgSetting.isSystem())) {
pkgSetting.getPkgState().setUpdatedSystemApp(true);
}
parsedPackage
.setSeInfo(SELinuxMMAC.getSeInfo(parsedPackage, sharedUserSetting,
injector.getCompatibility()))
.setSeInfoUser(SELinuxUtil.assignSeinfoUser(pkgSetting.readUserState(
userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId)));
if (parsedPackage.isSystem()) {
configurePackageComponents(parsedPackage);
}
final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride, pkgSetting);
if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
if (needToDeriveAbi) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
final boolean extractNativeLibs = !AndroidPackageUtils.isLibrary(parsedPackage);
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
packageAbiHelper.derivePackageAbi(parsedPackage,
pkgSetting.getPkgState().isUpdatedSystemApp(), cpuAbiOverride,
extractNativeLibs);
derivedAbi.first.applyTo(parsedPackage);
derivedAbi.second.applyTo(parsedPackage);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// Some system apps still use directory structure for native libraries
// in which case we might end up not detecting abi solely based on apk
// structure. Try to detect abi based on directory structure.
String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
if (parsedPackage.isSystem() && !pkgSetting.getPkgState().isUpdatedSystemApp() &&
pkgRawPrimaryCpuAbi == null) {
final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
parsedPackage);
abis.applyTo(parsedPackage);
abis.applyTo(pkgSetting);
final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
packageAbiHelper.getNativeLibraryPaths(parsedPackage, pkgSetting,
sAppLib32InstallDir);
nativeLibraryPaths.applyTo(parsedPackage);
}
} else {
// This is not a first boot or an upgrade, don't bother deriving the
// ABI during the scan. Instead, trust the value that was stored in the
// package setting.
parsedPackage.setPrimaryCpuAbi(primaryCpuAbiFromSettings)
.setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
packageAbiHelper.getNativeLibraryPaths(parsedPackage,
pkgSetting, sAppLib32InstallDir);
nativeLibraryPaths.applyTo(parsedPackage);
if (DEBUG_ABI_SELECTION) {
Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
parsedPackage.getPackageName() + " " +
AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)
+ ", "
+ AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
}
}
} else {
if ((scanFlags & SCAN_MOVE) != 0) {
// We haven't run dex-opt for this move (since we've moved the compiled output too)
// but we already have this packages package info in the PackageSetting. We just
// use that and derive the native library path based on the new codepath.
parsedPackage.setPrimaryCpuAbi(pkgSetting.primaryCpuAbiString)
.setSecondaryCpuAbi(pkgSetting.secondaryCpuAbiString);
}
// Set native library paths again. For moves, the path will be updated based on the
// ABIs we've determined above. For non-moves, the path will be updated based on the
// ABIs we determined during compilation, but the path will depend on the final
// package path (after the rename away from the stage path).
final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
packageAbiHelper.getNativeLibraryPaths(parsedPackage, pkgSetting,
sAppLib32InstallDir);
nativeLibraryPaths.applyTo(parsedPackage);
}
// This is a special case for the "system" package, where the ABI is
// dictated by the zygote configuration (and init.rc). We should keep track
// of this ABI so that we can deal with "normal" applications that run under
// the same UID correctly.
if (isPlatformPackage) {
parsedPackage.setPrimaryCpuAbi(VMRuntime.getRuntime().is64Bit() ?
Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]);
}
// If there's a mismatch between the abi-override in the package setting
// and the abiOverride specified for the install. Warn about this because we
// would've already compiled the app without taking the package setting into
// account.
if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
if (cpuAbiOverride == null) {
Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
" for package " + parsedPackage.getPackageName());
}
}
pkgSetting.primaryCpuAbiString = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
pkgSetting.secondaryCpuAbiString = AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage);
pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
if (DEBUG_ABI_SELECTION) {
Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
+ " to root=" + parsedPackage.getNativeLibraryRootDir() + ", isa="
+ parsedPackage.isNativeLibraryRootRequiresIsa());
}
// Push the derived path down into PackageSettings so we know what to
// clean up at uninstall time.
pkgSetting.legacyNativeLibraryPathString = parsedPackage.getNativeLibraryRootDir();
if (DEBUG_ABI_SELECTION) {
Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are" +
" primary=" + AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage) +
" secondary=" + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
}
if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
// We don't do this here during boot because we can do it all
// at once after scanning all existing packages.
//
// We also do this *before* we perform dexopt on this package, so that
// we can avoid redundant dexopts, and also to make sure we've got the
// code and package path correct.
changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.sharedUser, parsedPackage,
packageAbiHelper.getAdjustedAbiForSharedUser(
pkgSetting.sharedUser.packages, parsedPackage));
}
parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
.contains(android.Manifest.permission.FACTORY_TEST));
if (parsedPackage.isSystem()) {
pkgSetting.setIsOrphaned(true);
}
// Take care of first install / last update times.
final long scanFileTime = getLastModifiedTime(parsedPackage);
if (currentTime != 0) {
if (pkgSetting.firstInstallTime == 0) {
pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
} else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
pkgSetting.lastUpdateTime = currentTime;
}
} else if (pkgSetting.firstInstallTime == 0) {
// We need *something*. Take time time stamp of the file.
pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
} else if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
if (scanFileTime != pkgSetting.timeStamp) {
// A package on the system image has changed; consider this
// to be an update.
pkgSetting.lastUpdateTime = scanFileTime;
}
}
pkgSetting.setTimeStamp(scanFileTime);
// TODO(b/135203078): Remove, move to constructor
pkgSetting.pkg = parsedPackage;
pkgSetting.pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting);
pkgSetting.pkgPrivateFlags =
PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting);
if (parsedPackage.getLongVersionCode() != pkgSetting.versionCode) {
pkgSetting.versionCode = parsedPackage.getLongVersionCode();
}
// Update volume if needed
final String volumeUuid = parsedPackage.getVolumeUuid();
if (!Objects.equals(volumeUuid, pkgSetting.volumeUuid)) {
Slog.i(PackageManagerService.TAG,
"Update" + (pkgSetting.isSystem() ? " system" : "")
+ " package " + parsedPackage.getPackageName()
+ " volume from " + pkgSetting.volumeUuid
+ " to " + volumeUuid);
pkgSetting.volumeUuid = volumeUuid;
}
SharedLibraryInfo staticSharedLibraryInfo = null;
if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
staticSharedLibraryInfo =
AndroidPackageUtils.createSharedLibraryForStatic(parsedPackage);
}
List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
if (!ArrayUtils.isEmpty(parsedPackage.getLibraryNames())) {
dynamicSharedLibraryInfos = new ArrayList<>(parsedPackage.getLibraryNames().size());
for (String name : parsedPackage.getLibraryNames()) {
dynamicSharedLibraryInfos.add(
AndroidPackageUtils.createSharedLibraryForDynamic(parsedPackage, name));
}
}
return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
!createNewPackage /* existingSettingCopied */, staticSharedLibraryInfo,
dynamicSharedLibraryInfos);
}
/**
* Returns {@code true} if the given file contains code. Otherwise {@code false}.
*/
private static boolean apkHasCode(String fileName) {
StrictJarFile jarFile = null;
try {
jarFile = new StrictJarFile(fileName,
false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
return jarFile.findEntry("classes.dex") != null;
} catch (IOException ignore) {
} finally {
try {
if (jarFile != null) {
jarFile.close();
}
} catch (IOException ignore) {}
}
return false;
}
/**
* Enforces code policy for the package. This ensures that if an APK has
* declared hasCode="true" in its manifest that the APK actually contains
* code.
*
* @throws PackageManagerException If bytecode could not be found when it should exist
*/
private static void assertCodePolicy(AndroidPackage pkg)
throws PackageManagerException {
final boolean shouldHaveCode = pkg.isHasCode();
if (shouldHaveCode && !apkHasCode(pkg.getBaseCodePath())) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Package " + pkg.getBaseCodePath() + " code is missing");
}
if (!ArrayUtils.isEmpty(pkg.getSplitCodePaths())) {
for (int i = 0; i < pkg.getSplitCodePaths().length; i++) {
final boolean splitShouldHaveCode =
(pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0;
if (splitShouldHaveCode && !apkHasCode(pkg.getSplitCodePaths()[i])) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Package " + pkg.getSplitCodePaths()[i] + " code is missing");
}
}
}
}
/**
* Applies policy to the parsed package based upon the given policy flags.
* Ensures the package is in a good state.
* <p>
* Implementation detail: This method must NOT have any side effect. It would
* ideally be static, but, it requires locks to read system state.
*/
private static void applyPolicy(ParsedPackage parsedPackage, final @ParseFlags int parseFlags,
final @ScanFlags int scanFlags, AndroidPackage platformPkg,
boolean isUpdatedSystemApp) {
if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
parsedPackage.setSystem(true);
// TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag
// is set during parse.
if (parsedPackage.isDirectBootAware()) {
parsedPackage.setAllComponentsDirectBootAware(true);
}
if (compressedFileExists(parsedPackage.getCodePath())) {
parsedPackage.setStub(true);
}
} else {
parsedPackage
// Non system apps cannot mark any broadcast as protected
.clearProtectedBroadcasts()
// non system apps can't be flagged as core
.setCoreApp(false)
// clear flags not applicable to regular apps
.setPersistent(false)
.setDefaultToDeviceProtectedStorage(false)
.setDirectBootAware(false)
// non system apps can't have permission priority
.capPermissionPriorities();
}
if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
parsedPackage
.markNotActivitiesAsNotExportedIfSingleUser();
}
parsedPackage.setPrivileged((scanFlags & SCAN_AS_PRIVILEGED) != 0)
.setOem((scanFlags & SCAN_AS_OEM) != 0)
.setVendor((scanFlags & SCAN_AS_VENDOR) != 0)
.setProduct((scanFlags & SCAN_AS_PRODUCT) != 0)
.setSystemExt((scanFlags & SCAN_AS_SYSTEM_EXT) != 0)
.setOdm((scanFlags & SCAN_AS_ODM) != 0);
// Check if the package is signed with the same key as the platform package.
parsedPackage.setSignedWithPlatformKey(
(PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
|| (platformPkg != null && compareSignatures(
platformPkg.getSigningDetails().signatures,
parsedPackage.getSigningDetails().signatures
) == PackageManager.SIGNATURE_MATCH))
);
if (!parsedPackage.isSystem()) {
// Only system apps can use these features.
parsedPackage.clearOriginalPackages()
.setRealPackage(null)
.clearAdoptPermissions();
}
PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isUpdatedSystemApp);
}
private static @NonNull <T> T assertNotNull(@Nullable T object, String message)
throws PackageManagerException {
if (object == null) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, message);
}
return object;
}
private <T extends ParsedMainComponent>
void assertPackageProcesses(AndroidPackage pkg, List<T> components,
Map<String, ParsedProcess> procs, String compName)
throws PackageManagerException {
if (components == null) {
return;
}
for (int i = components.size() - 1; i >= 0; i--) {
final ParsedMainComponent component = components.get(i);
if (!procs.containsKey(component.getProcessName())) {
throw new PackageManagerException(
INSTALL_FAILED_PROCESS_NOT_DEFINED,
"Can't install because " + compName + " " + component.getClassName()
+ "'s process attribute " + component.getProcessName()
+ " (in package " + pkg.getPackageName()
+ ") is not included in the <processes> list");
}
}
}
/**
* Asserts the parsed package is valid according to the given policy. If the
* package is invalid, for whatever reason, throws {@link PackageManagerException}.
* <p>
* Implementation detail: This method must NOT have any side effects. It would
* ideally be static, but, it requires locks to read system state.
*
* @throws PackageManagerException If the package fails any of the validation checks
*/
private void assertPackageIsValid(AndroidPackage pkg, final @ParseFlags int parseFlags,
final @ScanFlags int scanFlags)
throws PackageManagerException {
if ((parseFlags & PackageParser.PARSE_ENFORCE_CODE) != 0) {
assertCodePolicy(pkg);
}
if (pkg.getCodePath() == null) {
// Bail out. The resource and code paths haven't been set.
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Code and resource paths haven't been set correctly");
}
// Check that there is an APEX package with the same name only during install/first boot
// after OTA.
final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
if ((isUserInstall || isFirstBootOrUpgrade)
&& mApexManager.isApexPackage(pkg.getPackageName())) {
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
pkg.getPackageName()
+ " is an APEX package and can't be installed as an APK.");
}
// Make sure we're not adding any bogus keyset info
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
ksms.assertScannedPackageValid(pkg);
synchronized (mLock) {
// The special "android" package can only be defined once
if (pkg.getPackageName().equals("android")) {
if (mAndroidApplication != null) {
Slog.w(TAG, "*************************************************");
Slog.w(TAG, "Core android package being redefined. Skipping.");
Slog.w(TAG, " codePath=" + pkg.getCodePath());
Slog.w(TAG, "*************************************************");
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
"Core android package being redefined. Skipping.");
}
}
// A package name must be unique; don't allow duplicates
if ((scanFlags & SCAN_NEW_INSTALL) == 0
&& mPackages.containsKey(pkg.getPackageName())) {
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
"Application package " + pkg.getPackageName()
+ " already installed. Skipping duplicate.");
}
if (pkg.isStaticSharedLibrary()) {
// Static libs have a synthetic package name containing the version
// but we still want the base name to be unique.
if ((scanFlags & SCAN_NEW_INSTALL) == 0
&& mPackages.containsKey(pkg.getManifestPackageName())) {
throw new PackageManagerException(
"Duplicate static shared lib provider package");
}
// Static shared libraries should have at least O target SDK
if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
throw new PackageManagerException(
"Packages declaring static-shared libs must target O SDK or higher");
}
// Package declaring static a shared lib cannot be instant apps
if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
throw new PackageManagerException(
"Packages declaring static-shared libs cannot be instant apps");
}
// Package declaring static a shared lib cannot be renamed since the package
// name is synthetic and apps can't code around package manager internals.
if (!ArrayUtils.isEmpty(pkg.getOriginalPackages())) {
throw new PackageManagerException(
"Packages declaring static-shared libs cannot be renamed");
}
// Package declaring static a shared lib cannot declare dynamic libs
if (!ArrayUtils.isEmpty(pkg.getLibraryNames())) {
throw new PackageManagerException(
"Packages declaring static-shared libs cannot declare dynamic libs");
}
// Package declaring static a shared lib cannot declare shared users
if (pkg.getSharedUserId() != null) {
throw new PackageManagerException(
"Packages declaring static-shared libs cannot declare shared users");
}
// Static shared libs cannot declare activities
if (!pkg.getActivities().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare activities");
}
// Static shared libs cannot declare services
if (!pkg.getServices().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare services");
}
// Static shared libs cannot declare providers
if (!pkg.getProviders().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare content providers");
}
// Static shared libs cannot declare receivers
if (!pkg.getReceivers().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare broadcast receivers");
}
// Static shared libs cannot declare permission groups
if (!pkg.getPermissionGroups().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare permission groups");
}
// Static shared libs cannot declare attributions
if (!pkg.getAttributions().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare features");
}
// Static shared libs cannot declare permissions
if (!pkg.getPermissions().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare permissions");
}
// Static shared libs cannot declare protected broadcasts
if (!pkg.getProtectedBroadcasts().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare protected broadcasts");
}
// Static shared libs cannot be overlay targets
if (pkg.getOverlayTarget() != null) {
throw new PackageManagerException(
"Static shared libs cannot be overlay targets");
}
// The version codes must be ordered as lib versions
long minVersionCode = Long.MIN_VALUE;
long maxVersionCode = Long.MAX_VALUE;
LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
pkg.getStaticSharedLibName());
if (versionedLib != null) {
final int versionCount = versionedLib.size();
for (int i = 0; i < versionCount; i++) {
SharedLibraryInfo libInfo = versionedLib.valueAt(i);
final long libVersionCode = libInfo.getDeclaringPackage()
.getLongVersionCode();
if (libInfo.getLongVersion() < pkg.getStaticSharedLibVersion()) {
minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
} else if (libInfo.getLongVersion()
> pkg.getStaticSharedLibVersion()) {
maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
} else {
minVersionCode = maxVersionCode = libVersionCode;
break;
}
}
}
if (pkg.getLongVersionCode() < minVersionCode
|| pkg.getLongVersionCode() > maxVersionCode) {
throw new PackageManagerException("Static shared"
+ " lib version codes must be ordered as lib versions");
}
}
// If we're only installing presumed-existing packages, require that the
// scanned APK is both already known and at the path previously established
// for it. Previously unknown packages we pick up normally, but if we have an
// a priori expectation about this package's install presence, enforce it.
// With a singular exception for new system packages. When an OTA contains
// a new system package, we allow the codepath to change from a system location
// to the user-installed location. If we don't allow this change, any newer,
// user-installed version of the application will be ignored.
if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
if (mExpectingBetter.containsKey(pkg.getPackageName())) {
logCriticalInfo(Log.WARN,
"Relax SCAN_REQUIRE_KNOWN requirement for package "
+ pkg.getPackageName());
} else {
PackageSetting known = mSettings.getPackageLPr(pkg.getPackageName());
if (known != null) {
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Examining " + pkg.getCodePath()
+ " and requiring known paths " + known.codePathString
+ " & " + known.resourcePathString);
}
if (!pkg.getCodePath().equals(known.codePathString)
|| !pkg.getCodePath().equals(known.resourcePathString)) {
throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
"Application package " + pkg.getPackageName()
+ " found at " + pkg.getCodePath()
+ " but expected at " + known.codePathString
+ "; ignoring.");
}
} else {
throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Application package " + pkg.getPackageName()
+ " not found; ignoring.");
}
}
}
// Verify that this new package doesn't have any content providers
// that conflict with existing packages. Only do this if the
// package isn't already installed, since we don't want to break
// things that are installed.
if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
mComponentResolver.assertProvidersNotDefined(pkg);
}
// If this package has defined explicit processes, then ensure that these are
// the only processes used by its components.
final Map<String, ParsedProcess> procs = pkg.getProcesses();
if (!procs.isEmpty()) {
if (!procs.containsKey(pkg.getProcessName())) {
throw new PackageManagerException(
INSTALL_FAILED_PROCESS_NOT_DEFINED,
"Can't install because application tag's process attribute "
+ pkg.getProcessName()
+ " (in package " + pkg.getPackageName()
+ ") is not included in the <processes> list");
}
assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity");
assertPackageProcesses(pkg, pkg.getServices(), procs, "service");
assertPackageProcesses(pkg, pkg.getReceivers(), procs, "receiver");
assertPackageProcesses(pkg, pkg.getProviders(), procs, "provider");
}
// Verify that packages sharing a user with a privileged app are marked as privileged.
if (!pkg.isPrivileged() && (pkg.getSharedUserId() != null)) {
SharedUserSetting sharedUserSetting = null;
try {
sharedUserSetting = mSettings.getSharedUserLPw(pkg.getSharedUserId(),
0, 0, false);
} catch (PackageManagerException ignore) {
}
if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
// Exempt SharedUsers signed with the platform key.
PackageSetting platformPkgSetting = mSettings.mPackages.get("android");
if ((platformPkgSetting.signatures.mSigningDetails
!= PackageParser.SigningDetails.UNKNOWN)
&& (compareSignatures(
platformPkgSetting.signatures.mSigningDetails.signatures,
pkg.getSigningDetails().signatures)
!= PackageManager.SIGNATURE_MATCH)) {
throw new PackageManagerException("Apps that share a user with a " +
"privileged app must themselves be marked as privileged. " +
pkg.getPackageName() + " shares privileged user " +
pkg.getSharedUserId() + ".");
}
}
}
// Apply policies specific for runtime resource overlays (RROs).
if (pkg.getOverlayTarget() != null) {
// System overlays have some restrictions on their use of the 'static' state.
if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
// We are scanning a system overlay. This can be the first scan of the
// system/vendor/oem partition, or an update to the system overlay.
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
// This must be an update to a system overlay. Immutable overlays cannot be
// upgraded.
Objects.requireNonNull(mOverlayConfig,
"Parsing non-system dir before overlay configs are initialized");
if (!mOverlayConfig.isMutable(pkg.getPackageName())) {
throw new PackageManagerException("Overlay "
+ pkg.getPackageName()
+ " is static and cannot be upgraded.");
}
} else {
if ((scanFlags & SCAN_AS_VENDOR) != 0) {
if (pkg.getTargetSdkVersion() < getVendorPartitionVersion()) {
Slog.w(TAG, "System overlay " + pkg.getPackageName()
+ " targets an SDK below the required SDK level of vendor"
+ " overlays (" + getVendorPartitionVersion() + ")."
+ " This will become an install error in a future release");
}
} else if (pkg.getTargetSdkVersion() < Build.VERSION.SDK_INT) {
Slog.w(TAG, "System overlay " + pkg.getPackageName()
+ " targets an SDK below the required SDK level of system"
+ " overlays (" + Build.VERSION.SDK_INT + ")."
+ " This will become an install error in a future release");
}
}
} else {
// A non-preloaded overlay packages must have targetSdkVersion >= Q, or be
// signed with the platform certificate. Check this in increasing order of
// computational cost.
if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.Q) {
final PackageSetting platformPkgSetting =
mSettings.getPackageLPr("android");
if ((platformPkgSetting.signatures.mSigningDetails
!= PackageParser.SigningDetails.UNKNOWN)
&& (compareSignatures(
platformPkgSetting.signatures.mSigningDetails.signatures,
pkg.getSigningDetails().signatures)
!= PackageManager.SIGNATURE_MATCH)) {
throw new PackageManagerException("Overlay "
+ pkg.getPackageName()
+ " must target Q or later, "
+ "or be signed with the platform certificate");
}
}
// A non-preloaded overlay package, without <overlay android:targetName>, will
// only be used if it is signed with the same certificate as its target. If the
// target is already installed, check this here to augment the last line of
// defence which is OMS.
if (pkg.getOverlayTargetName() == null) {
final PackageSetting targetPkgSetting =
mSettings.getPackageLPr(pkg.getOverlayTarget());
if (targetPkgSetting != null) {
if ((targetPkgSetting.signatures.mSigningDetails
!= PackageParser.SigningDetails.UNKNOWN)
&& (compareSignatures(
targetPkgSetting.signatures.mSigningDetails.signatures,
pkg.getSigningDetails().signatures)
!= PackageManager.SIGNATURE_MATCH)) {
throw new PackageManagerException("Overlay "
+ pkg.getPackageName() + " and target "
+ pkg.getOverlayTarget() + " signed with"
+ " different certificates, and the overlay lacks"
+ " <overlay android:targetName>");
}
}
}
}
}
// If the package is not on a system partition ensure it is signed with at least the
// minimum signature scheme version required for its target SDK.
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
int minSignatureSchemeVersion =
ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
pkg.getTargetSdkVersion());
if (pkg.getSigningDetails().signatureSchemeVersion < minSignatureSchemeVersion) {
throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
+ " or newer for package " + pkg.getPackageName());
}
}
}
}
@GuardedBy("mLock")
private boolean addBuiltInSharedLibraryLocked(String path, String name) {
if (nonStaticSharedLibExistsLocked(name)) {
return false;
}
SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, null, null, name,
(long) SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_BUILTIN,
new VersionedPackage(PLATFORM_PACKAGE_NAME, (long) 0),
null, null);
commitSharedLibraryInfoLocked(libraryInfo);
return true;
}
@GuardedBy("mLock")
private boolean nonStaticSharedLibExistsLocked(String name) {
return sharedLibExists(name, SharedLibraryInfo.VERSION_UNDEFINED, mSharedLibraries);
}
private static boolean sharedLibExists(final String name, final long version,
Map<String, LongSparseArray<SharedLibraryInfo>> librarySource) {
LongSparseArray<SharedLibraryInfo> versionedLib = librarySource.get(name);
if (versionedLib != null && versionedLib.indexOfKey(version) >= 0) {
return true;
}
return false;
}
@GuardedBy("mLock")
private void commitSharedLibraryInfoLocked(SharedLibraryInfo libraryInfo) {
final String name = libraryInfo.getName();
LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
if (versionedLib == null) {
versionedLib = new LongSparseArray<>();
mSharedLibraries.put(name, versionedLib);
}
final String declaringPackageName = libraryInfo.getDeclaringPackage().getPackageName();
if (libraryInfo.getType() == SharedLibraryInfo.TYPE_STATIC) {
mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib);
}
versionedLib.put(libraryInfo.getLongVersion(), libraryInfo);
}
private boolean removeSharedLibraryLPw(String name, long version) {
LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
if (versionedLib == null) {
return false;
}
final int libIdx = versionedLib.indexOfKey(version);
if (libIdx < 0) {
return false;
}
SharedLibraryInfo libraryInfo = versionedLib.valueAt(libIdx);
// Remove the shared library overlays from its dependent packages.
for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
final List<VersionedPackage> dependents = getPackagesUsingSharedLibraryLPr(
libraryInfo, 0, currentUserId);
if (dependents == null) {
continue;
}
for (VersionedPackage dependentPackage : dependents) {
final PackageSetting ps = mSettings.mPackages.get(
dependentPackage.getPackageName());
if (ps != null) {
ps.setOverlayPathsForLibrary(libraryInfo.getName(), null, currentUserId);
}
}
}
versionedLib.remove(version);
if (versionedLib.size() <= 0) {
mSharedLibraries.remove(name);
if (libraryInfo.getType() == SharedLibraryInfo.TYPE_STATIC) {
mStaticLibsByDeclaringPackage.remove(libraryInfo.getDeclaringPackage()
.getPackageName());
}
}
return true;
}
/**
* Adds a scanned package to the system. When this method is finished, the package will
* be available for query, resolution, etc...
*/
private void commitPackageSettings(AndroidPackage pkg,
@Nullable AndroidPackage oldPkg, PackageSetting pkgSetting,
final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) {
final String pkgName = pkg.getPackageName();
if (mCustomResolverComponentName != null &&
mCustomResolverComponentName.getPackageName().equals(pkg.getPackageName())) {
setUpCustomResolverActivity(pkg, pkgSetting);
}
if (pkg.getPackageName().equals("android")) {
synchronized (mLock) {
// Set up information for our fall-back user intent resolution activity.
mPlatformPackage = pkg;
// The instance stored in PackageManagerService is special cased to be non-user
// specific, so initialize all the needed fields here.
mAndroidApplication = pkg.toAppInfoWithoutState();
mAndroidApplication.flags = PackageInfoUtils.appInfoFlags(pkg, pkgSetting);
mAndroidApplication.privateFlags =
PackageInfoUtils.appInfoPrivateFlags(pkg, pkgSetting);
mAndroidApplication.initForUser(UserHandle.USER_SYSTEM);
if (!mResolverReplaced) {
mResolveActivity.applicationInfo = mAndroidApplication;
mResolveActivity.name = ResolverActivity.class.getName();
mResolveActivity.packageName = mAndroidApplication.packageName;
mResolveActivity.processName = "system:ui";
mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert;
mResolveActivity.exported = true;
mResolveActivity.enabled = true;
mResolveActivity.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
mResolveActivity.configChanges = ActivityInfo.CONFIG_SCREEN_SIZE
| ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE
| ActivityInfo.CONFIG_SCREEN_LAYOUT
| ActivityInfo.CONFIG_ORIENTATION
| ActivityInfo.CONFIG_KEYBOARD
| ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
mResolveInfo.activityInfo = mResolveActivity;
mResolveInfo.priority = 0;
mResolveInfo.preferredOrder = 0;
mResolveInfo.match = 0;
mResolveComponentName = new ComponentName(
mAndroidApplication.packageName, mResolveActivity.name);
}
}
}
ArrayList<AndroidPackage> clientLibPkgs = null;
// writer
synchronized (mLock) {
if (!ArrayUtils.isEmpty(reconciledPkg.allowedSharedLibraryInfos)) {
for (SharedLibraryInfo info : reconciledPkg.allowedSharedLibraryInfos) {
commitSharedLibraryInfoLocked(info);
}
final Map<String, AndroidPackage> combinedSigningDetails =
reconciledPkg.getCombinedAvailablePackages();
try {
// Shared libraries for the package need to be updated.
updateSharedLibrariesLocked(pkg, pkgSetting, null, null,
combinedSigningDetails);
} catch (PackageManagerException e) {
Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
}
// Update all applications that use this library. Skip when booting
// since this will be done after all packages are scaned.
if ((scanFlags & SCAN_BOOTING) == 0) {
clientLibPkgs = updateAllSharedLibrariesLocked(pkg, pkgSetting,
combinedSigningDetails);
}
}
}
if (reconciledPkg.installResult != null) {
reconciledPkg.installResult.libraryConsumers = clientLibPkgs;
}
if ((scanFlags & SCAN_BOOTING) != 0) {
// No apps can run during boot scan, so they don't need to be frozen
} else if ((scanFlags & SCAN_DONT_KILL_APP) != 0) {
// Caller asked to not kill app, so it's probably not frozen
} else if ((scanFlags & SCAN_IGNORE_FROZEN) != 0) {
// Caller asked us to ignore frozen check for some reason; they
// probably didn't know the package name
} else {
// We're doing major surgery on this package, so it better be frozen
// right now to keep it from launching
checkPackageFrozen(pkgName);
}
// Also need to kill any apps that are dependent on the library.
if (clientLibPkgs != null) {
for (int i=0; i<clientLibPkgs.size(); i++) {
AndroidPackage clientPkg = clientLibPkgs.get(i);
killApplication(clientPkg.getPackageName(),
clientPkg.getUid(), "update lib");
}
}
// writer
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
synchronized (mLock) {
// We don't expect installation to fail beyond this point
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the new setting to mPackages
mPackages.put(pkg.getPackageName(), pkg);
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
mApexManager.registerApkInApex(pkg);
}
// Add the package's KeySets to the global KeySetManagerService
KeySetManagerService ksms = mSettings.mKeySetManagerService;
ksms.addScannedPackageLPw(pkg);
mComponentResolver.addAllComponents(pkg, chatty);
final boolean isReplace =
reconciledPkg.prepareResult != null && reconciledPkg.prepareResult.replace;
mAppsFilter.addPackage(pkgSetting, isReplace);
// Don't allow ephemeral applications to define new permissions groups.
if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
Slog.w(TAG, "Permission groups from package " + pkg.getPackageName()
+ " ignored: instant apps cannot define new permission groups.");
} else {
mPermissionManager.addAllPermissionGroups(pkg, chatty);
}
// If a permission has had its defining app changed, or it has had its protection
// upgraded, we need to revoke apps that hold it
final List<String> permissionsWithChangedDefinition;
// Don't allow ephemeral applications to define new permissions.
if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
permissionsWithChangedDefinition = null;
Slog.w(TAG, "Permissions from package " + pkg.getPackageName()
+ " ignored: instant apps cannot define new permissions.");
} else {
permissionsWithChangedDefinition =
mPermissionManager.addAllPermissions(pkg, chatty);
}
int collectionSize = ArrayUtils.size(pkg.getInstrumentations());
StringBuilder r = null;
int i;
for (i = 0; i < collectionSize; i++) {
ParsedInstrumentation a = pkg.getInstrumentations().get(i);
a.setPackageName(pkg.getPackageName());
mInstrumentation.put(a.getComponentName(), a);
if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(a.getName());
}
}
if (r != null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Instrumentation: " + r);
}
if (!pkg.getProtectedBroadcasts().isEmpty()) {
synchronized (mProtectedBroadcasts) {
mProtectedBroadcasts.addAll(pkg.getProtectedBroadcasts());
}
}
boolean hasOldPkg = oldPkg != null;
boolean hasPermissionDefinitionChanges =
!CollectionUtils.isEmpty(permissionsWithChangedDefinition);
if (hasOldPkg || hasPermissionDefinitionChanges) {
// We need to call revokeRuntimePermissionsIfGroupChanged async as permission
// revoke callbacks from this method might need to kill apps which need the
// mPackages lock on a different thread. This would dead lock.
//
// Hence create a copy of all package names and pass it into
// revokeRuntimePermissionsIfGroupChanged. Only for those permissions might get
// revoked. If a new package is added before the async code runs the permission
// won't be granted yet, hence new packages are no problem.
final ArrayList<String> allPackageNames = new ArrayList<>(mPackages.keySet());
AsyncTask.execute(() -> {
if (hasOldPkg) {
mPermissionManager.revokeRuntimePermissionsIfGroupChanged(pkg, oldPkg,
allPackageNames);
}
if (hasPermissionDefinitionChanges) {
mPermissionManager.revokeRuntimePermissionsIfPermissionDefinitionChanged(
permissionsWithChangedDefinition, allPackageNames);
}
});
}
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
private void setUpCustomResolverActivity(AndroidPackage pkg, PackageSetting pkgSetting) {
synchronized (mLock) {
mResolverReplaced = true;
// The instance created in PackageManagerService is special cased to be non-user
// specific, so initialize all the needed fields here.
ApplicationInfo appInfo = pkg.toAppInfoWithoutState();
appInfo.flags = PackageInfoUtils.appInfoFlags(pkg, pkgSetting);
appInfo.privateFlags =
PackageInfoUtils.appInfoPrivateFlags(pkg, pkgSetting);
appInfo.initForUser(UserHandle.USER_SYSTEM);
// Set up information for custom user intent resolution activity.
mResolveActivity.applicationInfo = appInfo;
mResolveActivity.name = mCustomResolverComponentName.getClassName();
mResolveActivity.packageName = pkg.getPackageName();
mResolveActivity.processName = pkg.getProcessName();
mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS |
ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
mResolveActivity.theme = 0;
mResolveActivity.exported = true;
mResolveActivity.enabled = true;
mResolveInfo.activityInfo = mResolveActivity;
mResolveInfo.priority = 0;
mResolveInfo.preferredOrder = 0;
mResolveInfo.match = 0;
mResolveComponentName = mCustomResolverComponentName;
Slog.i(TAG, "Replacing default ResolverActivity with custom activity: " +
mResolveComponentName);
}
}
private void setUpInstantAppInstallerActivityLP(ActivityInfo installerActivity) {
if (installerActivity == null) {
if (DEBUG_INSTANT) {
Slog.d(TAG, "Clear ephemeral installer activity");
}
mInstantAppInstallerActivity = null;
return;
}
if (DEBUG_INSTANT) {
Slog.d(TAG, "Set ephemeral installer activity: "
+ installerActivity.getComponentName());
}
// Set up information for ephemeral installer activity
mInstantAppInstallerActivity = installerActivity;
mInstantAppInstallerActivity.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
| ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
mInstantAppInstallerActivity.exported = true;
mInstantAppInstallerActivity.enabled = true;
mInstantAppInstallerInfo.activityInfo = mInstantAppInstallerActivity;
mInstantAppInstallerInfo.priority = 1;
mInstantAppInstallerInfo.preferredOrder = 1;
mInstantAppInstallerInfo.isDefault = true;
mInstantAppInstallerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
}
private void killApplication(String pkgName, @AppIdInt int appId, String reason) {
killApplication(pkgName, appId, UserHandle.USER_ALL, reason);
}
private void killApplication(String pkgName, @AppIdInt int appId,
@UserIdInt int userId, String reason) {
// Request the ActivityManager to kill the process(only for existing packages)
// so that we do not end up in a confused state while the user is still using the older
// version of the application while the new one gets installed.
final long token = Binder.clearCallingIdentity();
try {
IActivityManager am = ActivityManager.getService();
if (am != null) {
try {
am.killApplication(pkgName, appId, userId, reason);
} catch (RemoteException e) {
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
private void removePackageLI(AndroidPackage pkg, boolean chatty) {
// Remove the parent package setting
PackageSetting ps = getPackageSetting(pkg.getPackageName());
if (ps != null) {
removePackageLI(ps.name, chatty);
} else if (DEBUG_REMOVE && chatty) {
Log.d(TAG, "Not removing package " + pkg.getPackageName() + "; mExtras == null");
}
}
void removePackageLI(String packageName, boolean chatty) {
if (DEBUG_INSTALL) {
if (chatty)
Log.d(TAG, "Removing package " + packageName);
}
// writer
synchronized (mLock) {
final AndroidPackage removedPackage = mPackages.remove(packageName);
if (removedPackage != null) {
cleanPackageDataStructuresLILPw(removedPackage, chatty);
}
}
}
void cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean chatty) {
mComponentResolver.removeAllComponents(pkg, chatty);
mPermissionManager.removeAllPermissions(pkg, chatty);
final int instrumentationSize = ArrayUtils.size(pkg.getInstrumentations());
StringBuilder r = null;
int i;
for (i = 0; i < instrumentationSize; i++) {
ParsedInstrumentation a = pkg.getInstrumentations().get(i);
mInstrumentation.remove(a.getComponentName());
if (DEBUG_REMOVE && chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(a.getName());
}
}
if (r != null) {
if (DEBUG_REMOVE) Log.d(TAG, " Instrumentation: " + r);
}
r = null;
if (pkg.isSystem()) {
// Only system apps can hold shared libraries.
final int libraryNamesSize = pkg.getLibraryNames().size();
for (i = 0; i < libraryNamesSize; i++) {
String name = pkg.getLibraryNames().get(i);
if (removeSharedLibraryLPw(name, 0)) {
if (DEBUG_REMOVE && chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(name);
}
}
}
}
r = null;
// Any package can hold static shared libraries.
if (pkg.getStaticSharedLibName() != null) {
if (removeSharedLibraryLPw(pkg.getStaticSharedLibName(),
pkg.getStaticSharedLibVersion())) {
if (DEBUG_REMOVE && chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(pkg.getStaticSharedLibName());
}
}
}
if (r != null) {
if (DEBUG_REMOVE) Log.d(TAG, " Libraries: " + r);
}
}
@Override
public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
final int[] userIds, int[] instantUserIds,
@Nullable SparseArray<int[]> broadcastWhitelist) {
mHandler.post(() -> {
try {
final IActivityManager am = ActivityManager.getService();
if (am == null) return;
final int[] resolvedUserIds;
if (userIds == null) {
resolvedUserIds = am.getRunningUserIds();
} else {
resolvedUserIds = userIds;
}
doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
resolvedUserIds, false, broadcastWhitelist);
if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) {
doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
instantUserIds, true, null);
}
} catch (RemoteException ex) {
}
});
}
@Override
public void notifyPackageAdded(String packageName, int uid) {
final PackageListObserver[] observers;
synchronized (mLock) {
if (mPackageListObservers.size() == 0) {
return;
}
final PackageListObserver[] observerArray =
new PackageListObserver[mPackageListObservers.size()];
observers = mPackageListObservers.toArray(observerArray);
}
for (int i = observers.length - 1; i >= 0; --i) {
observers[i].onPackageAdded(packageName, uid);
}
}
@Override
public void notifyPackageChanged(String packageName, int uid) {
final PackageListObserver[] observers;
synchronized (mLock) {
if (mPackageListObservers.size() == 0) {
return;
}
final PackageListObserver[] observerArray =
new PackageListObserver[mPackageListObservers.size()];
observers = mPackageListObservers.toArray(observerArray);
}
for (int i = observers.length - 1; i >= 0; --i) {
observers[i].onPackageChanged(packageName, uid);
}
}
private static final Comparator<ProviderInfo> sProviderInitOrderSorter = (p1, p2) -> {
final int v1 = p1.initOrder;
final int v2 = p2.initOrder;
return (v1 > v2) ? -1 : ((v1 < v2) ? 1 : 0);
};
@Override
public void notifyPackageRemoved(String packageName, int uid) {
final PackageListObserver[] observers;
synchronized (mLock) {
if (mPackageListObservers.size() == 0) {
return;
}
final PackageListObserver[] observerArray =
new PackageListObserver[mPackageListObservers.size()];
observers = mPackageListObservers.toArray(observerArray);
}
for (int i = observers.length - 1; i >= 0; --i) {
observers[i].onPackageRemoved(packageName, uid);
}
}
/**
* Sends a broadcast for the given action.
* <p>If {@code isInstantApp} is {@code true}, then the broadcast is protected with
* the {@link android.Manifest.permission#ACCESS_INSTANT_APPS} permission. This allows
* the system and applications allowed to see instant applications to receive package
* lifecycle events for instant applications.
*/
private void doSendBroadcast(IActivityManager am, String action, String pkg, Bundle extras,
int flags, String targetPkg, IIntentReceiver finishedReceiver,
int[] userIds, boolean isInstantApp, @Nullable SparseArray<int[]> broadcastWhitelist) {
for (int id : userIds) {
final Intent intent = new Intent(action,
pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
final String[] requiredPermissions =
isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null;
if (extras != null) {
intent.putExtras(extras);
}
if (targetPkg != null) {
intent.setPackage(targetPkg);
}
// Modify the UID when posting to other users
int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
if (uid > 0 && UserHandle.getUserId(uid) != id) {
uid = UserHandle.getUid(id, UserHandle.getAppId(uid));
intent.putExtra(Intent.EXTRA_UID, uid);
}
intent.putExtra(Intent.EXTRA_USER_HANDLE, id);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
if (DEBUG_BROADCASTS) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Slog.d(TAG, "Sending to user " + id + ": "
+ intent.toShortString(false, true, false, false)
+ " " + intent.getExtras(), here);
}
mInjector.getActivityManagerInternal().broadcastIntent(
intent, finishedReceiver, requiredPermissions,
finishedReceiver != null, id,
broadcastWhitelist == null ? null : broadcastWhitelist.get(id));
}
}
/**
* Check if the external storage media is available. This is true if there
* is a mounted external storage medium or if the external storage is
* emulated.
*/
private boolean isExternalMediaAvailable() {
return mMediaMounted || Environment.isExternalStorageEmulated();
}
/**
* Ensure that the install reason matches what we know about the package installer (e.g. whether
* it is acting on behalf on an enterprise or the user).
*
* Note that the ordering of the conditionals in this method is important. The checks we perform
* are as follows, in this order:
*
* 1) If the install is being performed by a system app, we can trust the app to have set the
* install reason correctly. Thus, we pass through the install reason unchanged, no matter
* what it is.
* 2) If the install is being performed by a device or profile owner app, the install reason
* should be enterprise policy. However, we cannot be sure that the device or profile owner
* set the install reason correctly. If the app targets an older SDK version where install
* reasons did not exist yet, or if the app author simply forgot, the install reason may be
* unset or wrong. Thus, we force the install reason to be enterprise policy.
* 3) In all other cases, the install is being performed by a regular app that is neither part
* of the system nor a device or profile owner. We have no reason to believe that this app is
* acting on behalf of the enterprise admin. Thus, we check whether the install reason was
* set to enterprise policy and if so, change it to unknown instead.
*/
private int fixUpInstallReason(String installerPackageName, int installerUid,
int installReason) {
if (checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
== PERMISSION_GRANTED) {
// If the install is being performed by a system app, we trust that app to have set the
// install reason correctly.
return installReason;
}
final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(
UserHandle.getUserId(installerUid));
if (ownerPackage != null && ownerPackage.equals(installerPackageName)) {
// If the install is being performed by a device or profile owner, the install
// reason should be enterprise policy.
return PackageManager.INSTALL_REASON_POLICY;
}
if (installReason == PackageManager.INSTALL_REASON_POLICY) {
// If the install is being performed by a regular app (i.e. neither system app nor
// device or profile owner), we have no reason to believe that the app is acting on
// behalf of an enterprise. If the app set the install reason to enterprise policy,
// change it to unknown instead.
return PackageManager.INSTALL_REASON_UNKNOWN;
}
// If the install is being performed by a regular app and the install reason was set to any
// value but enterprise policy, leave the install reason unchanged.
return installReason;
}
void installStage(ActiveInstallSession activeInstallSession) {
if (DEBUG_INSTANT) {
if ((activeInstallSession.getSessionParams().installFlags
& PackageManager.INSTALL_INSTANT_APP) != 0) {
Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName());
}
}
final Message msg = mHandler.obtainMessage(INIT_COPY);
final InstallParams params = new InstallParams(activeInstallSession);
params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
System.identityHashCode(msg.obj));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(msg.obj));
mHandler.sendMessage(msg);
}
void installStage(List<ActiveInstallSession> children)
throws PackageManagerException {
final Message msg = mHandler.obtainMessage(INIT_COPY);
final MultiPackageInstallParams params =
new MultiPackageInstallParams(UserHandle.ALL, children);
params.setTraceMethod("installStageMultiPackage")
.setTraceCookie(System.identityHashCode(params));
msg.obj = params;
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStageMultiPackage",
System.identityHashCode(msg.obj));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(msg.obj));
mHandler.sendMessage(msg);
}
private void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting,
int userId, int dataLoaderType) {
final boolean isSystem = isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting);
final boolean isInstantApp = pkgSetting.getInstantApp(userId);
final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
sendPackageAddedForNewUsers(packageName, isSystem /*sendBootCompleted*/,
false /*startReceiver*/, pkgSetting.appId, userIds, instantUserIds,
dataLoaderType);
// Send a session commit broadcast
final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo();
info.installReason = pkgSetting.getInstallReason(userId);
info.appPackageName = packageName;
sendSessionCommitBroadcast(info, userId);
}
@Override
public void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted,
boolean includeStopped, @AppIdInt int appId, int[] userIds, int[] instantUserIds,
int dataLoaderType) {
if (ArrayUtils.isEmpty(userIds) && ArrayUtils.isEmpty(instantUserIds)) {
return;
}
Bundle extras = new Bundle(1);
// Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
final int uid = UserHandle.getUid(
(ArrayUtils.isEmpty(userIds) ? instantUserIds[0] : userIds[0]), appId);
extras.putInt(Intent.EXTRA_UID, uid);
extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
packageName, extras, 0, null, null, userIds, instantUserIds,
mAppsFilter.getVisibilityWhitelist(
getPackageSettingInternal(packageName, Process.SYSTEM_UID),
userIds, mSettings.mPackages));
if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) {
mHandler.post(() -> {
for (int userId : userIds) {
sendBootCompletedBroadcastToSystemApp(
packageName, includeStopped, userId);
}
}
);
}
}
/**
* The just-installed/enabled app is bundled on the system, so presumed to be able to run
* automatically without needing an explicit launch.
* Send it a LOCKED_BOOT_COMPLETED/BOOT_COMPLETED if it would ordinarily have gotten ones.
*/
private void sendBootCompletedBroadcastToSystemApp(
String packageName, boolean includeStopped, int userId) {
// If user is not running, the app didn't miss any broadcast
if (!mUserManager.isUserRunning(userId)) {
return;
}
final IActivityManager am = ActivityManager.getService();
try {
// Deliver LOCKED_BOOT_COMPLETED first
Intent lockedBcIntent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)
.setPackage(packageName);
if (includeStopped) {
lockedBcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
}
final String[] requiredPermissions = {Manifest.permission.RECEIVE_BOOT_COMPLETED};
am.broadcastIntentWithFeature(null, null, lockedBcIntent, null, null, 0, null, null,
requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false,
userId);
// Deliver BOOT_COMPLETED only if user is unlocked
if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED).setPackage(packageName);
if (includeStopped) {
bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
}
am.broadcastIntentWithFeature(null, null, bcIntent, null, null, 0, null, null,
requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false,
userId);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override
public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
PackageSetting pkgSetting;
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */,
"setApplicationHiddenSetting for user " + userId);
if (hidden && isPackageDeviceAdmin(packageName, userId)) {
Slog.w(TAG, "Not hiding package " + packageName + ": has active device admin");
return false;
}
long callingId = Binder.clearCallingIdentity();
try {
boolean sendAdded = false;
boolean sendRemoved = false;
// writer
synchronized (mLock) {
pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null) {
return false;
}
if (shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
return false;
}
// Do not allow "android" is being disabled
if ("android".equals(packageName)) {
Slog.w(TAG, "Cannot hide package: android");
return false;
}
// Cannot hide static shared libs as they are considered
// a part of the using app (emulating static linking). Also
// static libs are installed always on internal storage.
AndroidPackage pkg = mPackages.get(packageName);
if (pkg != null && pkg.getStaticSharedLibName() != null) {
Slog.w(TAG, "Cannot hide package: " + packageName
+ " providing static shared library: "
+ pkg.getStaticSharedLibName());
return false;
}
// Only allow protected packages to hide themselves.
if (hidden && !UserHandle.isSameApp(callingUid, pkgSetting.appId)
&& mProtectedPackages.isPackageStateProtected(userId, packageName)) {
Slog.w(TAG, "Not hiding protected package: " + packageName);
return false;
}
if (pkgSetting.getHidden(userId) != hidden) {
pkgSetting.setHidden(hidden, userId);
mSettings.writePackageRestrictionsLPr(userId);
if (hidden) {
sendRemoved = true;
} else {
sendAdded = true;
}
}
}
if (sendAdded) {
sendPackageAddedForUser(packageName, pkgSetting, userId, DataLoaderType.NONE);
return true;
}
if (sendRemoved) {
killApplication(packageName, UserHandle.getUid(userId, pkgSetting.appId),
"hiding pkg");
sendApplicationHiddenForUser(packageName, pkgSetting, userId);
return true;
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
return false;
}
@Override
public void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden) {
final int callingUid = Binder.getCallingUid();
PackageManagerServiceUtils
.enforceSystemOrPhoneCaller("setSystemAppHiddenUntilInstalled", callingUid);
synchronized (mLock) {
final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null || !pkgSetting.isSystem()) {
return;
}
pkgSetting.getPkgState().setHiddenUntilInstalled(hidden);
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(packageName);
if (disabledPs == null) {
return;
}
disabledPs.getPkgState().setHiddenUntilInstalled(hidden);
}
}
@Override
public boolean setSystemAppInstallState(String packageName, boolean installed, int userId) {
final int callingUid = Binder.getCallingUid();
PackageManagerServiceUtils
.enforceSystemOrPhoneCaller("setSystemAppInstallState", callingUid);
synchronized (mLock) {
final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
// The target app should always be in system
if (pkgSetting == null || !pkgSetting.isSystem()) {
return false;
}
// Check if the install state is the same
if (pkgSetting.getInstalled(userId) == installed) {
return false;
}
}
final long callingId = Binder.clearCallingIdentity();
try {
if (installed) {
// install the app from uninstalled state
installExistingPackageAsUser(
packageName,
userId,
PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
PackageManager.INSTALL_REASON_DEVICE_SETUP,
null);
return true;
}
// uninstall the app from installed state
deletePackageVersioned(
new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
new LegacyPackageDeleteObserver(null).getBinder(),
userId,
PackageManager.DELETE_SYSTEM_APP);
return true;
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
private void sendApplicationHiddenForUser(String packageName, PackageSetting pkgSetting,
int userId) {
final PackageRemovedInfo info = new PackageRemovedInfo(this);
info.removedPackage = packageName;
info.installerPackageName = pkgSetting.installSource.installerPackageName;
info.removedUsers = new int[] {userId};
info.broadcastUsers = new int[] {userId};
info.uid = UserHandle.getUid(userId, pkgSetting.appId);
info.sendPackageRemovedBroadcasts(true /*killApp*/);
}
private void sendDistractingPackagesChanged(String[] pkgList, int[] uidList, int userId,
int distractionFlags) {
final Bundle extras = new Bundle(3);
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
sendPackageBroadcast(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null, extras,
Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, new int[]{userId}, null, null);
}
private void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId,
boolean suspended) {
final Bundle extras = new Bundle(3);
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
sendPackageBroadcast(
suspended ? Intent.ACTION_PACKAGES_SUSPENDED
: Intent.ACTION_PACKAGES_UNSUSPENDED,
null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
new int[] {userId}, null, null);
}
/**
* Returns true if application is not found or there was an error. Otherwise it returns
* the hidden state of the package for the given user.
*/
@Override
public boolean getApplicationHiddenSettingAsUser(String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"getApplicationHidden for user " + userId);
PackageSetting ps;
long callingId = Binder.clearCallingIdentity();
try {
// writer
synchronized (mLock) {
ps = mSettings.mPackages.get(packageName);
if (ps == null) {
return true;
}
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
return true;
}
return ps.getHidden(userId);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
/**
* @hide
*/
@Override
public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
int installReason, List<String> whiteListedPermissions) {
return installExistingPackageAsUser(packageName, userId, installFlags, installReason,
whiteListedPermissions, null);
}
int installExistingPackageAsUser(@Nullable String packageName, @UserIdInt int userId,
@PackageManager.InstallFlags int installFlags,
@PackageManager.InstallReason int installReason,
@Nullable List<String> whiteListedPermissions, @Nullable IntentSender intentSender) {
if (DEBUG_INSTALL) {
Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId
+ " installFlags=" + installFlags + " installReason=" + installReason
+ " whiteListedPermissions=" + whiteListedPermissions);
}
final int callingUid = Binder.getCallingUid();
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
!= PackageManager.PERMISSION_GRANTED
&& mContext.checkCallingOrSelfPermission(
android.Manifest.permission.INSTALL_EXISTING_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Neither user " + callingUid + " nor current process has "
+ android.Manifest.permission.INSTALL_PACKAGES + ".");
}
PackageSetting pkgSetting;
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */,
"installExistingPackage for user " + userId);
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
}
long callingId = Binder.clearCallingIdentity();
try {
boolean installed = false;
final boolean instantApp =
(installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
final boolean fullApp =
(installFlags & PackageManager.INSTALL_FULL_APP) != 0;
// writer
synchronized (mLock) {
pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null) {
return PackageManager.INSTALL_FAILED_INVALID_URI;
}
if (!canViewInstantApps(callingUid, UserHandle.getUserId(callingUid))) {
// only allow the existing package to be used if it's installed as a full
// application for at least one user
boolean installAllowed = false;
for (int checkUserId : mUserManager.getUserIds()) {
installAllowed = !pkgSetting.getInstantApp(checkUserId);
if (installAllowed) {
break;
}
}
if (!installAllowed) {
return PackageManager.INSTALL_FAILED_INVALID_URI;
}
}
if (!pkgSetting.getInstalled(userId)) {
pkgSetting.setInstalled(true, userId);
pkgSetting.setHidden(false, userId);
pkgSetting.setInstallReason(installReason, userId);
pkgSetting.setUninstallReason(PackageManager.UNINSTALL_REASON_UNKNOWN, userId);
mSettings.writePackageRestrictionsLPr(userId);
mSettings.writeKernelMappingLPr(pkgSetting);
installed = true;
} else if (fullApp && pkgSetting.getInstantApp(userId)) {
// upgrade app from instant to full; we don't allow app downgrade
installed = true;
}
setInstantAppForUser(mInjector, pkgSetting, userId, instantApp, fullApp);
}
if (installed) {
if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS)
!= 0 && pkgSetting.pkg != null) {
whiteListedPermissions = pkgSetting.pkg.getRequestedPermissions();
}
mPermissionManager.setWhitelistedRestrictedPermissions(packageName,
whiteListedPermissions, FLAG_PERMISSION_WHITELIST_INSTALLER, userId);
if (pkgSetting.pkg != null) {
synchronized (mInstallLock) {
// We don't need to freeze for a brand new install
prepareAppDataAfterInstallLIF(pkgSetting.pkg);
}
}
sendPackageAddedForUser(packageName, pkgSetting, userId, DataLoaderType.NONE);
synchronized (mLock) {
updateSequenceNumberLP(pkgSetting, new int[]{ userId });
}
// start async restore with no post-install since we finish install here
PackageInstalledInfo res =
createPackageInstalledInfo(PackageManager.INSTALL_SUCCEEDED);
res.pkg = pkgSetting.pkg;
res.newUsers = new int[]{ userId };
PostInstallData postInstallData =
new PostInstallData(null, res, () -> {
clearRolesAndRestorePermissionsForNewUserInstall(packageName,
pkgSetting.getInstallReason(userId), userId);
if (intentSender != null) {
onRestoreComplete(res.returnCode, mContext, intentSender);
}
});
restoreAndPostInstall(userId, res, postInstallData);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
return PackageManager.INSTALL_SUCCEEDED;
}
static void onRestoreComplete(int returnCode, Context context, IntentSender target) {
Intent fillIn = new Intent();
fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
PackageManager.installStatusToPublicStatus(returnCode));
try {
target.sendIntent(context, 0, fillIn, null, null);
} catch (SendIntentException ignored) {
}
}
static void setInstantAppForUser(Injector injector, PackageSetting pkgSetting,
int userId, boolean instantApp, boolean fullApp) {
// no state specified; do nothing
if (!instantApp && !fullApp) {
return;
}
if (userId != UserHandle.USER_ALL) {
if (instantApp && !pkgSetting.getInstantApp(userId)) {
pkgSetting.setInstantApp(true /*instantApp*/, userId);
} else if (fullApp && pkgSetting.getInstantApp(userId)) {
pkgSetting.setInstantApp(false /*instantApp*/, userId);
}
} else {
for (int currentUserId : injector.getUserManagerInternal().getUserIds()) {
if (instantApp && !pkgSetting.getInstantApp(currentUserId)) {
pkgSetting.setInstantApp(true /*instantApp*/, currentUserId);
} else if (fullApp && pkgSetting.getInstantApp(currentUserId)) {
pkgSetting.setInstantApp(false /*instantApp*/, currentUserId);
}
}
}
}
boolean isUserRestricted(int userId, String restrictionKey) {
Bundle restrictions = mUserManager.getUserRestrictions(userId);
if (restrictions.getBoolean(restrictionKey, false)) {
Log.w(TAG, "User is restricted: " + restrictionKey);
return true;
}
return false;
}
@Override
public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames,
int restrictionFlags, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
"setDistractingPackageRestrictionsAsUser");
final int callingUid = Binder.getCallingUid();
if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID
&& UserHandle.getUserId(callingUid) != userId) {
throw new SecurityException("Calling uid " + callingUid + " cannot call for user "
+ userId);
}
Objects.requireNonNull(packageNames, "packageNames cannot be null");
if (restrictionFlags != 0 && !isSuspendAllowedForUser(userId)) {
Slog.w(TAG, "Cannot restrict packages due to restrictions on user " + userId);
return packageNames;
}
final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
final IntArray changedUids = new IntArray(packageNames.length);
final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
final boolean[] canRestrict = (restrictionFlags != 0) ? canSuspendPackageForUserInternal(
packageNames, userId) : null;
for (int i = 0; i < packageNames.length; i++) {
final String packageName = packageNames[i];
final PackageSetting pkgSetting;
synchronized (mLock) {
pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null
|| shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
Slog.w(TAG, "Could not find package setting for package: " + packageName
+ ". Skipping...");
unactionedPackages.add(packageName);
continue;
}
}
if (canRestrict != null && !canRestrict[i]) {
unactionedPackages.add(packageName);
continue;
}
synchronized (mLock) {
final int oldDistractionFlags = pkgSetting.getDistractionFlags(userId);
if (restrictionFlags != oldDistractionFlags) {
pkgSetting.setDistractionFlags(restrictionFlags, userId);
changedPackagesList.add(packageName);
changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
}
}
}
if (!changedPackagesList.isEmpty()) {
final String[] changedPackages = changedPackagesList.toArray(
new String[changedPackagesList.size()]);
sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId,
restrictionFlags);
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(userId);
}
}
return unactionedPackages.toArray(new String[0]);
}
private void enforceCanSetPackagesSuspendedAsUser(String callingPackage, int callingUid,
int userId, String callingMethod) {
if (callingUid == Process.ROOT_UID
// Need to compare app-id to allow system dialogs access on secondary users
|| UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
return;
}
final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId);
if (ownerPackage != null) {
final int ownerUid = getPackageUid(ownerPackage, 0, userId);
if (ownerUid == callingUid) {
return;
}
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
callingMethod);
final int packageUid = getPackageUid(callingPackage, 0, userId);
final boolean allowedPackageUid = packageUid == callingUid;
// TODO(b/139383163): remove special casing for shell and enforce INTERACT_ACROSS_USERS_FULL
final boolean allowedShell = callingUid == SHELL_UID
&& UserHandle.isSameApp(packageUid, callingUid);
if (!allowedShell && !allowedPackageUid) {
throw new SecurityException("Calling package " + callingPackage + " in user "
+ userId + " does not belong to calling uid " + callingUid);
}
}
@Override
public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
PersistableBundle appExtras, PersistableBundle launcherExtras,
SuspendDialogInfo dialogInfo, String callingPackage, int userId) {
final int callingUid = Binder.getCallingUid();
enforceCanSetPackagesSuspendedAsUser(callingPackage, callingUid, userId,
"setPackagesSuspendedAsUser");
if (ArrayUtils.isEmpty(packageNames)) {
return packageNames;
}
if (suspended && !isSuspendAllowedForUser(userId)) {
Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
return packageNames;
}
final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
final IntArray changedUids = new IntArray(packageNames.length);
final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
final boolean[] canSuspend = suspended ? canSuspendPackageForUserInternal(packageNames,
userId) : null;
for (int i = 0; i < packageNames.length; i++) {
final String packageName = packageNames[i];
if (callingPackage.equals(packageName)) {
Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
+ (suspended ? "" : "un") + "suspend itself. Ignoring");
unactionedPackages.add(packageName);
continue;
}
final PackageSetting pkgSetting;
synchronized (mLock) {
pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null
|| shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
Slog.w(TAG, "Could not find package setting for package: " + packageName
+ ". Skipping suspending/un-suspending.");
unactionedPackages.add(packageName);
continue;
}
}
if (canSuspend != null && !canSuspend[i]) {
unactionedPackages.add(packageName);
continue;
}
boolean packageUnsuspended;
synchronized (mLock) {
if (suspended) {
pkgSetting.addOrUpdateSuspension(callingPackage, dialogInfo, appExtras,
launcherExtras, userId);
} else {
pkgSetting.removeSuspension(callingPackage, userId);
}
packageUnsuspended = !suspended && !pkgSetting.getSuspended(userId);
}
if (suspended || packageUnsuspended) {
changedPackagesList.add(packageName);
changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
}
}
if (!changedPackagesList.isEmpty()) {
final String[] changedPackages = changedPackagesList.toArray(
new String[changedPackagesList.size()]);
sendPackagesSuspendedForUser(changedPackages, changedUids.toArray(), userId, suspended);
sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(userId);
}
}
return unactionedPackages.toArray(new String[unactionedPackages.size()]);
}
@Override
public Bundle getSuspendedPackageAppExtras(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
if (getPackageUid(packageName, 0, userId) != callingUid) {
throw new SecurityException("Calling package " + packageName
+ " does not belong to calling uid " + callingUid);
}
return getSuspendedPackageAppExtrasInternal(packageName, userId);
}
private Bundle getSuspendedPackageAppExtrasInternal(String packageName, int userId) {
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) {
throw new IllegalArgumentException("Unknown target package: " + packageName);
}
final PackageUserState pus = ps.readUserState(userId);
final Bundle allExtras = new Bundle();
if (pus.suspended) {
for (int i = 0; i < pus.suspendParams.size(); i++) {
final PackageUserState.SuspendParams params = pus.suspendParams.valueAt(i);
if (params != null && params.appExtras != null) {
allExtras.putAll(params.appExtras);
}
}
}
return (allExtras.size() > 0) ? allExtras : null;
}
}
private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended,
int userId) {
final String action = suspended
? Intent.ACTION_MY_PACKAGE_SUSPENDED
: Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
mHandler.post(() -> {
final IActivityManager am = ActivityManager.getService();
if (am == null) {
Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
+ (suspended ? "" : "UN") + "SUSPENDED broadcasts");
return;
}
final int[] targetUserIds = new int[] {userId};
for (String packageName : affectedPackages) {
final Bundle appExtras = suspended
? getSuspendedPackageAppExtrasInternal(packageName, userId)
: null;
final Bundle intentExtras;
if (appExtras != null) {
intentExtras = new Bundle(1);
intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras);
} else {
intentExtras = null;
}
doSendBroadcast(am, action, null, intentExtras,
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
targetUserIds, false, null);
}
});
}
@Override
public boolean isPackageSuspendedForUser(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"isPackageSuspendedForUser for user " + userId);
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
throw new IllegalArgumentException("Unknown target package: " + packageName);
}
return ps.getSuspended(userId);
}
}
void unsuspendForSuspendingPackage(String suspendingPackage, int userId) {
final String[] allPackages;
synchronized (mLock) {
allPackages = mPackages.keySet().toArray(new String[mPackages.size()]);
}
removeSuspensionsBySuspendingPackage(allPackages, suspendingPackage::equals, userId);
}
boolean isSuspendingAnyPackages(String suspendingPackage, int userId) {
synchronized (mLock) {
for (final PackageSetting ps : mSettings.mPackages.values()) {
if (ps.isSuspendedBy(suspendingPackage, userId)) {
return true;
}
}
}
return false;
}
/**
* Removes any suspensions on given packages that were added by packages that pass the given
* predicate.
*
* <p> Caller must flush package restrictions if it cares about immediate data consistency.
*
* @param packagesToChange The packages on which the suspension are to be removed.
* @param suspendingPackagePredicate A predicate identifying the suspending packages whose
* suspensions will be removed.
* @param userId The user for which the changes are taking place.
*/
void removeSuspensionsBySuspendingPackage(String[] packagesToChange,
Predicate<String> suspendingPackagePredicate, int userId) {
final List<String> unsuspendedPackages = new ArrayList<>();
final IntArray unsuspendedUids = new IntArray();
synchronized (mLock) {
for (String packageName : packagesToChange) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null && ps.getSuspended(userId)) {
ps.removeSuspension(suspendingPackagePredicate, userId);
if (!ps.getSuspended(userId)) {
unsuspendedPackages.add(ps.name);
unsuspendedUids.add(UserHandle.getUid(userId, ps.getAppId()));
}
}
}
scheduleWritePackageRestrictionsLocked(userId);
}
if (!unsuspendedPackages.isEmpty()) {
final String[] packageArray = unsuspendedPackages.toArray(
new String[unsuspendedPackages.size()]);
sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId);
sendPackagesSuspendedForUser(packageArray, unsuspendedUids.toArray(), userId, false);
}
}
void removeAllDistractingPackageRestrictions(int userId) {
final String[] allPackages;
synchronized (mLock) {
allPackages = mPackages.keySet().toArray(new String[mPackages.size()]);
}
removeDistractingPackageRestrictions(allPackages, userId);
}
/**
* Removes any {@link android.content.pm.PackageManager.DistractionRestriction restrictions}
* set on given packages.
*
* <p> Caller must flush package restrictions if it cares about immediate data consistency.
*
* @param packagesToChange The packages on which restrictions are to be removed.
* @param userId the user for which changes are taking place.
*/
void removeDistractingPackageRestrictions(String[] packagesToChange, int userId) {
final List<String> changedPackages = new ArrayList<>();
final IntArray changedUids = new IntArray();
synchronized (mLock) {
for (String packageName : packagesToChange) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null && ps.getDistractionFlags(userId) != 0) {
ps.setDistractionFlags(0, userId);
changedPackages.add(ps.name);
changedUids.add(UserHandle.getUid(userId, ps.getAppId()));
}
}
if (!changedPackages.isEmpty()) {
final String[] packageArray = changedPackages.toArray(
new String[changedPackages.size()]);
sendDistractingPackagesChanged(packageArray, changedUids.toArray(), userId, 0);
scheduleWritePackageRestrictionsLocked(userId);
}
}
}
private boolean isCallerDeviceOrProfileOwner(int userId) {
final int callingUid = Binder.getCallingUid();
if (callingUid == Process.SYSTEM_UID) {
return true;
}
final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId);
if (ownerPackage != null) {
return callingUid == getPackageUidInternal(ownerPackage, 0, userId, callingUid);
}
return false;
}
private boolean isSuspendAllowedForUser(int userId) {
return isCallerDeviceOrProfileOwner(userId)
|| (!mUserManager.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL, userId)
&& !mUserManager.hasUserRestriction(UserManager.DISALLOW_UNINSTALL_APPS, userId));
}
@Override
public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) {
Objects.requireNonNull(packageNames, "packageNames cannot be null");
mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
"getUnsuspendablePackagesForUser");
final int callingUid = Binder.getCallingUid();
if (UserHandle.getUserId(callingUid) != userId) {
throw new SecurityException("Calling uid " + callingUid
+ " cannot query getUnsuspendablePackagesForUser for user " + userId);
}
if (!isSuspendAllowedForUser(userId)) {
Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
return packageNames;
}
final ArraySet<String> unactionablePackages = new ArraySet<>();
final boolean[] canSuspend = canSuspendPackageForUserInternal(packageNames, userId);
for (int i = 0; i < packageNames.length; i++) {
if (!canSuspend[i]) {
unactionablePackages.add(packageNames[i]);
continue;
}
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageNames[i]);
if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]);
unactionablePackages.add(packageNames[i]);
}
}
}
return unactionablePackages.toArray(new String[unactionablePackages.size()]);
}
/**
* Returns an array of booleans, such that the ith boolean denotes whether the ith package can
* be suspended or not.
*
* @param packageNames The package names to check suspendability for.
* @param userId The user to check in
* @return An array containing results of the checks
*/
@NonNull
private boolean[] canSuspendPackageForUserInternal(@NonNull String[] packageNames, int userId) {
final boolean[] canSuspend = new boolean[packageNames.length];
final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId);
final long callingId = Binder.clearCallingIdentity();
try {
final String activeLauncherPackageName = getActiveLauncherPackageName(userId);
final String dialerPackageName = mPermissionManager.getDefaultDialer(userId);
for (int i = 0; i < packageNames.length; i++) {
canSuspend[i] = false;
final String packageName = packageNames[i];
if (isPackageDeviceAdmin(packageName, userId)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
+ "\": has an active device admin");
continue;
}
if (packageName.equals(activeLauncherPackageName)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
+ "\": contains the active launcher");
continue;
}
if (packageName.equals(mRequiredInstallerPackage)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
+ "\": required for package installation");
continue;
}
if (packageName.equals(mRequiredUninstallerPackage)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
+ "\": required for package uninstallation");
continue;
}
if (packageName.equals(mRequiredVerifierPackage)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
+ "\": required for package verification");
continue;
}
if (packageName.equals(dialerPackageName)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
+ "\": is the default dialer");
continue;
}
if (packageName.equals(mRequiredPermissionControllerPackage)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
+ "\": required for permissions management");
continue;
}
synchronized (mLock) {
if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
+ "\": protected package");
continue;
}
if (!isCallerOwner && mSettings.getBlockUninstallLPr(userId, packageName)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
+ "\": blocked by admin");
continue;
}
// Cannot suspend static shared libs as they are considered
// a part of the using app (emulating static linking). Also
// static libs are installed always on internal storage.
AndroidPackage pkg = mPackages.get(packageName);
if (pkg != null && pkg.isStaticSharedLibrary()) {
Slog.w(TAG, "Cannot suspend package: " + packageName
+ " providing static shared library: "
+ pkg.getStaticSharedLibName());
continue;
}
}
if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
continue;
}
canSuspend[i] = true;
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
return canSuspend;
}
private String getActiveLauncherPackageName(int userId) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
ResolveInfo resolveInfo = resolveIntent(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
PackageManager.MATCH_DEFAULT_ONLY,
userId);
return resolveInfo == null ? null : resolveInfo.activityInfo.packageName;
}
@Override
public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
"Only package verification agents can verify applications");
final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
final PackageVerificationResponse response = new PackageVerificationResponse(
verificationCode, Binder.getCallingUid());
msg.arg1 = id;
msg.obj = response;
mHandler.sendMessage(msg);
}
@Override
public void extendVerificationTimeout(int id, int verificationCodeAtTimeout,
long millisecondsToDelay) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
"Only package verification agents can extend verification timeouts");
final PackageVerificationState state = mPendingVerification.get(id);
final PackageVerificationResponse response = new PackageVerificationResponse(
verificationCodeAtTimeout, Binder.getCallingUid());
if (millisecondsToDelay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT) {
millisecondsToDelay = PackageManager.MAXIMUM_VERIFICATION_TIMEOUT;
}
if (millisecondsToDelay < 0) {
millisecondsToDelay = 0;
}
if ((verificationCodeAtTimeout != PackageManager.VERIFICATION_ALLOW)
&& (verificationCodeAtTimeout != PackageManager.VERIFICATION_REJECT)) {
verificationCodeAtTimeout = PackageManager.VERIFICATION_REJECT;
}
if ((state != null) && !state.timeoutExtended()) {
state.extendTimeout();
final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
msg.arg1 = id;
msg.obj = response;
mHandler.sendMessageDelayed(msg, millisecondsToDelay);
}
}
private void broadcastPackageVerified(int verificationId, Uri packageUri,
int verificationCode, @Nullable String rootHashString, int dataLoaderType,
UserHandle user) {
final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED);
intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
intent.putExtra(PackageManager.EXTRA_VERIFICATION_RESULT, verificationCode);
if (rootHashString != null) {
intent.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString);
}
intent.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
mContext.sendBroadcastAsUser(intent, user,
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT);
}
private ComponentName matchComponentForVerifier(String packageName,
List<ResolveInfo> receivers) {
ActivityInfo targetReceiver = null;
final int NR = receivers.size();
for (int i = 0; i < NR; i++) {
final ResolveInfo info = receivers.get(i);
if (info.activityInfo == null) {
continue;
}
if (packageName.equals(info.activityInfo.packageName)) {
targetReceiver = info.activityInfo;
break;
}
}
if (targetReceiver == null) {
return null;
}
return new ComponentName(targetReceiver.packageName, targetReceiver.name);
}
private List<ComponentName> matchVerifiers(PackageInfoLite pkgInfo,
List<ResolveInfo> receivers, final PackageVerificationState verificationState) {
if (pkgInfo.verifiers.length == 0) {
return null;
}
final int N = pkgInfo.verifiers.length;
final List<ComponentName> sufficientVerifiers = new ArrayList<>(N + 1);
for (int i = 0; i < N; i++) {
final VerifierInfo verifierInfo = pkgInfo.verifiers[i];
final ComponentName comp = matchComponentForVerifier(verifierInfo.packageName,
receivers);
if (comp == null) {
continue;
}
final int verifierUid = getUidForVerifier(verifierInfo);
if (verifierUid == -1) {
continue;
}
if (DEBUG_VERIFY) {
Slog.d(TAG, "Added sufficient verifier " + verifierInfo.packageName
+ " with the correct signature");
}
sufficientVerifiers.add(comp);
verificationState.addSufficientVerifier(verifierUid);
}
return sufficientVerifiers;
}
private int getUidForVerifier(VerifierInfo verifierInfo) {
synchronized (mLock) {
final AndroidPackage pkg = mPackages.get(verifierInfo.packageName);
if (pkg == null) {
return -1;
} else if (pkg.getSigningDetails().signatures.length != 1) {
Slog.i(TAG, "Verifier package " + verifierInfo.packageName
+ " has more than one signature; ignoring");
return -1;
}
/*
* If the public key of the package's signature does not match
* our expected public key, then this is a different package and
* we should skip.
*/
final byte[] expectedPublicKey;
try {
final Signature verifierSig = pkg.getSigningDetails().signatures[0];
final PublicKey publicKey = verifierSig.getPublicKey();
expectedPublicKey = publicKey.getEncoded();
} catch (CertificateException e) {
return -1;
}
final byte[] actualPublicKey = verifierInfo.publicKey.getEncoded();
if (!Arrays.equals(actualPublicKey, expectedPublicKey)) {
Slog.i(TAG, "Verifier package " + verifierInfo.packageName
+ " does not have the expected public key; ignoring");
return -1;
}
return pkg.getUid();
}
}
private void setEnableRollbackCode(int token, int enableRollbackCode) {
final Message msg = mHandler.obtainMessage(ENABLE_ROLLBACK_STATUS);
msg.arg1 = token;
msg.arg2 = enableRollbackCode;
mHandler.sendMessage(msg);
}
@Override
public void finishPackageInstall(int token, boolean didLaunch) {
enforceSystemOrRoot("Only the system is allowed to finish installs");
if (DEBUG_INSTALL) {
Slog.v(TAG, "BM finishing package install for " + token);
}
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
final Message msg = mHandler.obtainMessage(POST_INSTALL, token, didLaunch ? 1 : 0);
mHandler.sendMessage(msg);
}
/**
* Get the verification agent timeout. Used for both the APK verifier and the
* intent filter verifier.
*
* @return verification timeout in milliseconds
*/
private long getVerificationTimeout() {
long timeout = Global.getLong(mContext.getContentResolver(),
Global.PACKAGE_VERIFIER_TIMEOUT, DEFAULT_VERIFICATION_TIMEOUT);
// The setting can be used to increase the timeout but not decrease it, since that is
// equivalent to disabling the verifier.
return Math.max(timeout, DEFAULT_VERIFICATION_TIMEOUT);
}
/**
* Get the integrity verification timeout.
*
* @return verification timeout in milliseconds
*/
private long getIntegrityVerificationTimeout() {
long timeout = Global.getLong(mContext.getContentResolver(),
Global.APP_INTEGRITY_VERIFICATION_TIMEOUT, DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT);
// The setting can be used to increase the timeout but not decrease it, since that is
// equivalent to disabling the integrity component.
return Math.max(timeout, DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT);
}
/**
* Get the default verification agent response code.
*
* @return default verification response code
*/
private int getDefaultVerificationResponse(UserHandle user) {
if (mUserManager.hasUserRestriction(UserManager.ENSURE_VERIFY_APPS, user.getIdentifier())) {
return PackageManager.VERIFICATION_REJECT;
}
return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
DEFAULT_VERIFICATION_RESPONSE);
}
/**
* Get the default integrity verification response code.
*/
private int getDefaultIntegrityVerificationResponse() {
// We are not exposing this as a user-configurable setting because we don't want to provide
// an easy way to get around the integrity check.
return PackageManager.VERIFICATION_REJECT;
}
/**
* Check whether or not package verification has been enabled.
*
* @return true if verification should be performed
*/
private boolean isVerificationEnabled(
PackageInfoLite pkgInfoLite, int userId, int installFlags, int installerUid) {
if (!DEFAULT_VERIFY_ENABLE) {
return false;
}
// Check if installing from ADB
if ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0) {
if (isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS)) {
return true;
}
// Check if the developer wants to skip verification for ADB installs
if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
synchronized (mLock) {
if (mSettings.mPackages.get(pkgInfoLite.packageName) == null) {
// Always verify fresh install
return true;
}
}
// Only skip when apk is debuggable
return !pkgInfoLite.debuggable;
}
return Global.getInt(mContext.getContentResolver(),
Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
}
if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
return false;
}
// only when not installed from ADB, skip verification for instant apps when
// the installer and verifier are the same.
if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
if (mInstantAppInstallerActivity != null
&& mInstantAppInstallerActivity.packageName.equals(
mRequiredVerifierPackage)) {
try {
mInjector.getAppOpsManager()
.checkPackage(installerUid, mRequiredVerifierPackage);
if (DEBUG_VERIFY) {
Slog.i(TAG, "disable verification for instant app");
}
return false;
} catch (SecurityException ignore) { }
}
}
return true;
}
/**
* Check whether or not integrity verification has been enabled.
*/
private boolean isIntegrityVerificationEnabled() {
// We are not exposing this as a user-configurable setting because we don't want to provide
// an easy way to get around the integrity check.
return DEFAULT_INTEGRITY_VERIFY_ENABLE;
}
@Override
public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains)
throws RemoteException {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
"Only intentfilter verification agents can verify applications");
final Message msg = mHandler.obtainMessage(INTENT_FILTER_VERIFIED);
final IntentFilterVerificationResponse response = new IntentFilterVerificationResponse(
Binder.getCallingUid(), verificationCode, failedDomains);
msg.arg1 = id;
msg.obj = response;
mHandler.sendMessage(msg);
}
@Override
public int getIntentVerificationStatus(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
if (UserHandle.getUserId(callingUid) != userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"getIntentVerificationStatus" + userId);
}
if (getInstantAppPackageName(callingUid) != null) {
return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
}
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null
|| shouldFilterApplicationLocked(
ps, callingUid, UserHandle.getUserId(callingUid))) {
return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
}
return mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
}
}
@Override
public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
boolean result = false;
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (shouldFilterApplicationLocked(
ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
return false;
}
result = mSettings.updateIntentFilterVerificationStatusLPw(packageName, status, userId);
}
if (result) {
scheduleWritePackageRestrictionsLocked(userId);
}
return result;
}
@Override
public @NonNull ParceledListSlice<IntentFilterVerificationInfo> getIntentFilterVerifications(
String packageName) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return ParceledListSlice.emptyList();
}
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (shouldFilterApplicationLocked(ps, callingUid, UserHandle.getUserId(callingUid))) {
return ParceledListSlice.emptyList();
}
return new ParceledListSlice<>(mSettings.getIntentFilterVerificationsLPr(packageName));
}
}
@Override
public @NonNull ParceledListSlice<IntentFilter> getAllIntentFilters(String packageName) {
if (TextUtils.isEmpty(packageName)) {
return ParceledListSlice.emptyList();
}
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
synchronized (mLock) {
AndroidPackage pkg = mPackages.get(packageName);
if (pkg == null || ArrayUtils.isEmpty(pkg.getActivities())) {
return ParceledListSlice.emptyList();
}
final PackageSetting ps = getPackageSetting(pkg.getPackageName());
if (ps == null) {
return ParceledListSlice.emptyList();
}
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return ParceledListSlice.emptyList();
}
final int count = ArrayUtils.size(pkg.getActivities());
ArrayList<IntentFilter> result = new ArrayList<>();
for (int n=0; n<count; n++) {
ParsedActivity activity = pkg.getActivities().get(n);
if (activity.getIntents() != null && activity.getIntents().size() > 0) {
result.addAll(activity.getIntents());
}
}
return new ParceledListSlice<IntentFilter>(result) {
@Override
protected void writeElement(IntentFilter parcelable, Parcel dest, int callFlags) {
// IntentFilter has final Parcelable methods, so redirect to the subclass
((ParsedIntentInfo) parcelable).writeIntentInfoToParcel(dest,
callFlags);
}
};
}
}
/**
* Get the "allow unknown sources" setting.
*
* @return the current "allow unknown sources" setting
*/
private int getUnknownSourcesSettings() {
return android.provider.Settings.Secure.getInt(mContext.getContentResolver(),
android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS,
-1);
}
@Override
public void setInstallerPackageName(String targetPackage, String installerPackageName) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return;
}
// writer
synchronized (mLock) {
PackageSetting targetPackageSetting = mSettings.mPackages.get(targetPackage);
if (targetPackageSetting == null
|| shouldFilterApplicationLocked(
targetPackageSetting, callingUid, UserHandle.getUserId(callingUid))) {
throw new IllegalArgumentException("Unknown target package: " + targetPackage);
}
PackageSetting installerPackageSetting;
if (installerPackageName != null) {
installerPackageSetting = mSettings.mPackages.get(installerPackageName);
if (installerPackageSetting == null) {
throw new IllegalArgumentException("Unknown installer package: "
+ installerPackageName);
}
} else {
installerPackageSetting = null;
}
Signature[] callerSignature;
final int appId = UserHandle.getAppId(callingUid);
final Object obj = mSettings.getSettingLPr(appId);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
callerSignature =
((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
} else if (obj instanceof PackageSetting) {
callerSignature = ((PackageSetting)obj).signatures.mSigningDetails.signatures;
} else {
throw new SecurityException("Bad object " + obj + " for uid " + callingUid);
}
} else {
throw new SecurityException("Unknown calling UID: " + callingUid);
}
// Verify: can't set installerPackageName to a package that is
// not signed with the same cert as the caller.
if (installerPackageSetting != null) {
if (compareSignatures(callerSignature,
installerPackageSetting.signatures.mSigningDetails.signatures)
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as new installer package "
+ installerPackageName);
}
}
// Verify: if target already has an installer package, it must
// be signed with the same cert as the caller.
String targetInstallerPackageName =
targetPackageSetting.installSource.installerPackageName;
PackageSetting targetInstallerPkgSetting = targetInstallerPackageName == null ? null :
mSettings.mPackages.get(targetInstallerPackageName);
if (targetInstallerPkgSetting != null) {
if (compareSignatures(callerSignature,
targetInstallerPkgSetting.signatures.mSigningDetails.signatures)
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as old installer package "
+ targetInstallerPackageName);
}
} else if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
// This is probably an attempt to exploit vulnerability b/150857253 of taking
// privileged installer permissions when the installer has been uninstalled or
// was never set.
EventLog.writeEvent(0x534e4554, "150857253", callingUid, "");
long binderToken = Binder.clearCallingIdentity();
try {
if (mInjector.getCompatibility().isChangeEnabledByUid(
THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE,
callingUid)) {
throw new SecurityException("Neither user " + callingUid
+ " nor current process has "
+ Manifest.permission.INSTALL_PACKAGES);
} else {
// If change disabled, fail silently for backwards compatibility
return;
}
} finally {
Binder.restoreCallingIdentity(binderToken);
}
}
// Okay!
targetPackageSetting.setInstallerPackageName(installerPackageName);
mSettings.addInstallerPackageNames(targetPackageSetting.installSource);
mAppsFilter.addPackage(targetPackageSetting);
scheduleWriteSettingsLocked();
}
}
@Override
public void setApplicationCategoryHint(String packageName, int categoryHint,
String callerPackageName) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
throw new SecurityException("Instant applications don't have access to this method");
}
mInjector.getAppOpsManager().checkPackage(Binder.getCallingUid(),
callerPackageName);
synchronized (mLock) {
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) {
throw new IllegalArgumentException("Unknown target package " + packageName);
}
if (shouldFilterApplicationLocked(
ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
throw new IllegalArgumentException("Unknown target package " + packageName);
}
if (!Objects.equals(callerPackageName, ps.installSource.installerPackageName)) {
throw new IllegalArgumentException("Calling package " + callerPackageName
+ " is not installer for " + packageName);
}
if (ps.categoryHint != categoryHint) {
ps.categoryHint = categoryHint;
scheduleWriteSettingsLocked();
}
}
}
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
if (args.mMultiPackageInstallParams != null) {
args.mMultiPackageInstallParams.tryProcessInstallRequest(args, currentStatus);
} else {
PackageInstalledInfo res = createPackageInstalledInfo(currentStatus);
processInstallRequestsAsync(
res.returnCode == PackageManager.INSTALL_SUCCEEDED,
Collections.singletonList(new InstallRequest(args, res)));
}
}
// Queue up an async operation since the package installation may take a little while.
private void processInstallRequestsAsync(boolean success,
List<InstallRequest> installRequests) {
mHandler.post(() -> {
if (success) {
for (InstallRequest request : installRequests) {
request.args.doPreInstall(request.installResult.returnCode);
}
synchronized (mInstallLock) {
installPackagesTracedLI(installRequests);
}
for (InstallRequest request : installRequests) {
request.args.doPostInstall(
request.installResult.returnCode, request.installResult.uid);
}
}
for (InstallRequest request : installRequests) {
restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
new PostInstallData(request.args, request.installResult, null));
}
});
}
private PackageInstalledInfo createPackageInstalledInfo(
int currentStatus) {
PackageInstalledInfo res = new PackageInstalledInfo();
res.setReturnCode(currentStatus);
res.uid = -1;
res.pkg = null;
res.removedInfo = null;
return res;
}
/** @param data Post-install is performed only if this is non-null. */
private void restoreAndPostInstall(
int userId, PackageInstalledInfo res, @Nullable PostInstallData data) {
if (DEBUG_INSTALL) {
Log.v(TAG, "restoreAndPostInstall userId=" + userId + " package=" + res.pkg);
}
// A restore should be performed at this point if (a) the install
// succeeded, (b) the operation is not an update, and (c) the new
// package has not opted out of backup participation.
final boolean update = res.removedInfo != null
&& res.removedInfo.removedPackage != null;
boolean allowBackup = res.pkg != null && res.pkg.isAllowBackup();
boolean doRestore = !update && allowBackup;
// Set up the post-install work request bookkeeping. This will be used
// and cleaned up by the post-install event handling regardless of whether
// there's a restore pass performed. Token values are >= 1.
int token;
if (mNextInstallToken < 0) mNextInstallToken = 1;
token = mNextInstallToken++;
if (data != null) {
mRunningInstalls.put(token, data);
} else if (DEBUG_INSTALL) {
Log.v(TAG, "No post-install required for " + token);
}
if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
// Pass responsibility to the Backup Manager. It will perform a
// restore if appropriate, then pass responsibility back to the
// Package Manager to run the post-install observer callbacks
// and broadcasts.
if (res.freezer != null) {
res.freezer.close();
}
doRestore = performBackupManagerRestore(userId, token, res);
}
// If this is an update to a package that might be potentially downgraded, then we
// need to check with the rollback manager whether there's any userdata that might
// need to be snapshotted or restored for the package.
//
// TODO(narayan): Get this working for cases where userId == UserHandle.USER_ALL.
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && !doRestore && update) {
doRestore = performRollbackManagerRestore(userId, token, res, data);
}
if (!doRestore) {
// No restore possible, or the Backup Manager was mysteriously not
// available -- just fire the post-install work request directly.
if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "postInstall", token);
Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
mHandler.sendMessage(msg);
}
}
/**
* Perform Backup Manager restore for a given {@link PackageInstalledInfo}.
* Returns whether the restore successfully completed.
*/
private boolean performBackupManagerRestore(int userId, int token, PackageInstalledInfo res) {
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
if (bm != null) {
// For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
// in the BackupManager. USER_ALL is used in compatibility tests.
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_SYSTEM;
}
if (DEBUG_INSTALL) {
Log.v(TAG, "token " + token + " to BM for possible restore for user " + userId);
}
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
try {
if (bm.isUserReadyForBackup(userId)) {
bm.restoreAtInstallForUser(
userId, res.pkg.getPackageName(), token);
} else {
Slog.w(TAG, "User " + userId + " is not ready. Restore at install "
+ "didn't take place.");
return false;
}
} catch (RemoteException e) {
// can't happen; the backup manager is local
} catch (Exception e) {
Slog.e(TAG, "Exception trying to enqueue restore", e);
return false;
}
} else {
Slog.e(TAG, "Backup Manager not found!");
return false;
}
return true;
}
/**
* Perform Rollback Manager restore for a given {@link PackageInstalledInfo}.
* Returns whether the restore successfully completed.
*/
private boolean performRollbackManagerRestore(int userId, int token, PackageInstalledInfo res,
PostInstallData data) {
IRollbackManager rm = IRollbackManager.Stub.asInterface(
ServiceManager.getService(Context.ROLLBACK_SERVICE));
final String packageName = res.pkg.getPackageName();
final int[] allUsers = mUserManager.getUserIds();
final int[] installedUsers;
final PackageSetting ps;
int appId = -1;
long ceDataInode = -1;
synchronized (mLock) {
ps = mSettings.getPackageLPr(packageName);
if (ps != null) {
appId = ps.appId;
ceDataInode = ps.getCeDataInode(userId);
}
// NOTE: We ignore the user specified in the InstallParam because we know this is
// an update, and hence need to restore data for all installed users.
installedUsers = ps.queryInstalledUsers(allUsers, true);
}
boolean doSnapshotOrRestore = data != null && data.args != null
&& ((data.args.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
|| (data.args.installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0);
if (ps != null && doSnapshotOrRestore) {
final String seInfo = AndroidPackageUtils.getSeInfo(res.pkg, ps);
try {
rm.snapshotAndRestoreUserData(packageName, installedUsers, appId, ceDataInode,
seInfo, token);
} catch (RemoteException re) {
Log.e(TAG, "Error snapshotting/restoring user data: " + re);
return false;
}
return true;
}
return false;
}
/**
* Callback from PackageSettings whenever an app is first transitioned out of the
* 'stopped' state. Normally we just issue the broadcast, but we can't do that if
* the app was "launched" for a restoreAtInstall operation. Therefore we check
* here whether the app is the target of an ongoing install, and only send the
* broadcast immediately if it is not in that state. If it *is* undergoing a restore,
* the first-launch broadcast will be sent implicitly on that basis in POST_INSTALL
* handling.
*/
void notifyFirstLaunch(final String packageName, final String installerPackage,
final int userId) {
// Serialize this with the rest of the install-process message chain. In the
// restore-at-install case, this Runnable will necessarily run before the
// POST_INSTALL message is processed, so the contents of mRunningInstalls
// are coherent. In the non-restore case, the app has already completed install
// and been launched through some other means, so it is not in a problematic
// state for observers to see the FIRST_LAUNCH signal.
mHandler.post(() -> {
for (int i = 0; i < mRunningInstalls.size(); i++) {
final PostInstallData data = mRunningInstalls.valueAt(i);
if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
continue;
}
if (packageName.equals(data.res.pkg.getPackageName())) {
// right package; but is it for the right user?
for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) {
if (userId == data.res.newUsers[uIndex]) {
if (DEBUG_BACKUP) {
Slog.i(TAG, "Package " + packageName
+ " being restored so deferring FIRST_LAUNCH");
}
return;
}
}
}
}
// didn't find it, so not being restored
if (DEBUG_BACKUP) {
Slog.i(TAG, "Package " + packageName + " sending normal FIRST_LAUNCH");
}
final boolean isInstantApp = isInstantApp(packageName, userId);
final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
sendFirstLaunchBroadcast(packageName, installerPackage, userIds, instantUserIds);
});
}
private void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
int[] userIds, int[] instantUserIds) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
installerPkg, null, userIds, instantUserIds, null /* broadcastWhitelist */);
}
private abstract class HandlerParams {
/** User handle for the user requesting the information or installation. */
private final UserHandle mUser;
String traceMethod;
int traceCookie;
HandlerParams(UserHandle user) {
mUser = user;
}
UserHandle getUser() {
return mUser;
}
/**
* Gets the user handle for the user that the rollback agent should
* use to look up information about this installation when enabling
* rollback.
*/
UserHandle getRollbackUser() {
// The session for packages installed for "all" users is
// associated with the "system" user.
if (mUser == UserHandle.ALL) {
return UserHandle.SYSTEM;
}
return mUser;
}
HandlerParams setTraceMethod(String traceMethod) {
this.traceMethod = traceMethod;
return this;
}
HandlerParams setTraceCookie(int traceCookie) {
this.traceCookie = traceCookie;
return this;
}
final void startCopy() {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
handleStartCopy();
handleReturnCode();
}
abstract void handleStartCopy();
abstract void handleReturnCode();
}
static class OriginInfo {
/**
* Location where install is coming from, before it has been
* copied/renamed into place. This could be a single monolithic APK
* file, or a cluster directory. This location may be untrusted.
*/
final File file;
/**
* Flag indicating that {@link #file} has already been staged, meaning downstream users
* don't need to defensively copy the contents.
*/
final boolean staged;
/**
* Flag indicating that {@link #file} is an already installed app that is being moved.
*/
final boolean existing;
final String resolvedPath;
final File resolvedFile;
static OriginInfo fromNothing() {
return new OriginInfo(null, false, false);
}
static OriginInfo fromUntrustedFile(File file) {
return new OriginInfo(file, false, false);
}
static OriginInfo fromExistingFile(File file) {
return new OriginInfo(file, false, true);
}
static OriginInfo fromStagedFile(File file) {
return new OriginInfo(file, true, false);
}
private OriginInfo(File file, boolean staged, boolean existing) {
this.file = file;
this.staged = staged;
this.existing = existing;
if (file != null) {
resolvedPath = file.getAbsolutePath();
resolvedFile = file;
} else {
resolvedPath = null;
resolvedFile = null;
}
}
}
static class MoveInfo {
final int moveId;
final String fromUuid;
final String toUuid;
final String packageName;
final int appId;
final String seinfo;
final int targetSdkVersion;
final String fromCodePath;
public MoveInfo(int moveId, String fromUuid, String toUuid, String packageName,
int appId, String seinfo, int targetSdkVersion,
String fromCodePath) {
this.moveId = moveId;
this.fromUuid = fromUuid;
this.toUuid = toUuid;
this.packageName = packageName;
this.appId = appId;
this.seinfo = seinfo;
this.targetSdkVersion = targetSdkVersion;
this.fromCodePath = fromCodePath;
}
}
static class VerificationInfo {
/** A constant used to indicate that a uid value is not present. */
public static final int NO_UID = -1;
/** URI referencing where the package was downloaded from. */
final Uri originatingUri;
/** HTTP referrer URI associated with the originatingURI. */
final Uri referrer;
/** UID of the application that the install request originated from. */
final int originatingUid;
/** UID of application requesting the install */
final int installerUid;
VerificationInfo(Uri originatingUri, Uri referrer, int originatingUid, int installerUid) {
this.originatingUri = originatingUri;
this.referrer = referrer;
this.originatingUid = originatingUid;
this.installerUid = installerUid;
}
}
/**
* Container for a multi-package install which refers to all install sessions and args being
* committed together.
*/
class MultiPackageInstallParams extends HandlerParams {
private int mRet = INSTALL_SUCCEEDED;
@NonNull
private final ArrayList<InstallParams> mChildParams;
@NonNull
private final Map<InstallArgs, Integer> mCurrentState;
MultiPackageInstallParams(
@NonNull UserHandle user,
@NonNull List<ActiveInstallSession> activeInstallSessions)
throws PackageManagerException {
super(user);
if (activeInstallSessions.size() == 0) {
throw new PackageManagerException("No child sessions found!");
}
mChildParams = new ArrayList<>(activeInstallSessions.size());
for (int i = 0; i < activeInstallSessions.size(); i++) {
final InstallParams childParams = new InstallParams(activeInstallSessions.get(i));
childParams.mParentInstallParams = this;
this.mChildParams.add(childParams);
}
this.mCurrentState = new ArrayMap<>(mChildParams.size());
}
@Override
void handleStartCopy() {
for (InstallParams params : mChildParams) {
params.handleStartCopy();
if (params.mRet != INSTALL_SUCCEEDED) {
mRet = params.mRet;
}
}
}
@Override
void handleReturnCode() {
for (InstallParams params : mChildParams) {
params.handleReturnCode();
if (params.mRet != INSTALL_SUCCEEDED) {
mRet = params.mRet;
}
}
}
void tryProcessInstallRequest(InstallArgs args, int currentStatus) {
mCurrentState.put(args, currentStatus);
if (mCurrentState.size() != mChildParams.size()) {
return;
}
int completeStatus = PackageManager.INSTALL_SUCCEEDED;
for (Integer status : mCurrentState.values()) {
if (status == PackageManager.INSTALL_UNKNOWN) {
return;
} else if (status != PackageManager.INSTALL_SUCCEEDED) {
completeStatus = status;
break;
}
}
final List<InstallRequest> installRequests = new ArrayList<>(mCurrentState.size());
for (Map.Entry<InstallArgs, Integer> entry : mCurrentState.entrySet()) {
installRequests.add(new InstallRequest(entry.getKey(),
createPackageInstalledInfo(completeStatus)));
}
processInstallRequestsAsync(
completeStatus == PackageManager.INSTALL_SUCCEEDED,
installRequests);
}
}
class InstallParams extends HandlerParams {
// TODO: see if we can collapse this into ActiveInstallSession
final OriginInfo origin;
final MoveInfo move;
final IPackageInstallObserver2 observer;
int installFlags;
@NonNull final InstallSource installSource;
final String volumeUuid;
private boolean mVerificationCompleted;
private boolean mIntegrityVerificationCompleted;
private boolean mEnableRollbackCompleted;
private InstallArgs mArgs;
int mRet;
final String packageAbiOverride;
final String[] grantedRuntimePermissions;
final List<String> whitelistedRestrictedPermissions;
final int autoRevokePermissionsMode;
final VerificationInfo verificationInfo;
final PackageParser.SigningDetails signingDetails;
final int installReason;
@Nullable
MultiPackageInstallParams mParentInstallParams;
final long requiredInstalledVersionCode;
final boolean forceQueryableOverride;
final int mDataLoaderType;
final int mSessionId;
InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
int installFlags, InstallSource installSource, String volumeUuid,
VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
int autoRevokePermissionsMode,
SigningDetails signingDetails, int installReason,
long requiredInstalledVersionCode, int dataLoaderType) {
super(user);
this.origin = origin;
this.move = move;
this.observer = observer;
this.installFlags = installFlags;
this.installSource = Preconditions.checkNotNull(installSource);
this.volumeUuid = volumeUuid;
this.verificationInfo = verificationInfo;
this.packageAbiOverride = packageAbiOverride;
this.grantedRuntimePermissions = grantedPermissions;
this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
this.autoRevokePermissionsMode = autoRevokePermissionsMode;
this.signingDetails = signingDetails;
this.installReason = installReason;
this.requiredInstalledVersionCode = requiredInstalledVersionCode;
this.forceQueryableOverride = false;
this.mDataLoaderType = dataLoaderType;
this.mSessionId = -1;
}
InstallParams(ActiveInstallSession activeInstallSession) {
super(activeInstallSession.getUser());
final PackageInstaller.SessionParams sessionParams =
activeInstallSession.getSessionParams();
if (DEBUG_INSTANT) {
if ((sessionParams.installFlags
& PackageManager.INSTALL_INSTANT_APP) != 0) {
Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName());
}
}
verificationInfo = new VerificationInfo(
sessionParams.originatingUri,
sessionParams.referrerUri,
sessionParams.originatingUid,
activeInstallSession.getInstallerUid());
origin = OriginInfo.fromStagedFile(activeInstallSession.getStagedDir());
move = null;
installReason = fixUpInstallReason(
activeInstallSession.getInstallSource().installerPackageName,
activeInstallSession.getInstallerUid(),
sessionParams.installReason);
observer = activeInstallSession.getObserver();
installFlags = sessionParams.installFlags;
installSource = activeInstallSession.getInstallSource();
volumeUuid = sessionParams.volumeUuid;
packageAbiOverride = sessionParams.abiOverride;
grantedRuntimePermissions = sessionParams.grantedRuntimePermissions;
whitelistedRestrictedPermissions = sessionParams.whitelistedRestrictedPermissions;
autoRevokePermissionsMode = sessionParams.autoRevokePermissionsMode;
signingDetails = activeInstallSession.getSigningDetails();
requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
forceQueryableOverride = sessionParams.forceQueryableOverride;
mDataLoaderType = (sessionParams.dataLoaderParams != null)
? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
mSessionId = activeInstallSession.getSessionId();
}
@Override
public String toString() {
return "InstallParams{" + Integer.toHexString(System.identityHashCode(this))
+ " file=" + origin.file + "}";
}
private int installLocationPolicy(PackageInfoLite pkgLite) {
String packageName = pkgLite.packageName;
int installLocation = pkgLite.installLocation;
// reader
synchronized (mLock) {
// Currently installed package which the new package is attempting to replace or
// null if no such package is installed.
AndroidPackage installedPkg = mPackages.get(packageName);
// Package which currently owns the data which the new package will own if installed.
// If an app is unstalled while keeping data (e.g., adb uninstall -k), installedPkg
// will be null whereas dataOwnerPkg will contain information about the package
// which was uninstalled while keeping its data.
AndroidPackage dataOwnerPkg = installedPkg;
if (dataOwnerPkg == null) {
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
dataOwnerPkg = ps.pkg;
}
}
if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
if (dataOwnerPkg == null) {
Slog.w(TAG, "Required installed version code was "
+ requiredInstalledVersionCode
+ " but package is not installed");
return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION;
}
if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
Slog.w(TAG, "Required installed version code was "
+ requiredInstalledVersionCode
+ " but actual installed version is "
+ dataOwnerPkg.getLongVersionCode());
return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION;
}
}
if (dataOwnerPkg != null) {
if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
dataOwnerPkg.isDebuggable())) {
try {
checkDowngrade(dataOwnerPkg, pkgLite);
} catch (PackageManagerException e) {
Slog.w(TAG, "Downgrade detected: " + e.getMessage());
return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE;
}
}
}
if (installedPkg != null) {
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
// Check for updated system application.
if (installedPkg.isSystem()) {
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
} else {
// If current upgrade specifies particular preference
if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
// Application explicitly specified internal.
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
} else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
// App explictly prefers external. Let policy decide
} else {
// Prefer previous location
if (installedPkg.isExternalStorage()) {
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
}
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
}
}
} else {
// Invalid install. Return error code
return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS;
}
}
}
return pkgLite.recommendedInstallLocation;
}
/*
* Invoke remote method to get package information and install
* location values. Override install location based on default
* policy if needed and then create install arguments based
* on the install location.
*/
public void handleStartCopy() {
int ret = PackageManager.INSTALL_SUCCEEDED;
// If we're already staged, we've firmly committed to an install location
if (origin.staged) {
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
} else {
throw new IllegalStateException("Invalid stage location");
}
}
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
PackageInfoLite pkgLite = null;
pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
origin.resolvedPath, installFlags, packageAbiOverride);
if (DEBUG_INSTANT && ephemeral) {
Slog.v(TAG, "pkgLite for install: " + pkgLite);
}
/*
* If we have too little free space, try to free cache
* before giving up.
*/
if (!origin.staged && pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
// TODO: focus freeing disk space on the target device
final StorageManager storage = StorageManager.from(mContext);
final long lowThreshold = storage.getStorageLowBytes(
Environment.getDataDirectory());
final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
origin.resolvedPath, packageAbiOverride);
if (sizeBytes >= 0) {
try {
mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
origin.resolvedPath, installFlags, packageAbiOverride);
} catch (InstallerException e) {
Slog.w(TAG, "Failed to free cache", e);
}
}
/*
* The cache free must have deleted the file we downloaded to install.
*
* TODO: fix the "freeCache" call to not delete the file we care about.
*/
if (pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
pkgLite.recommendedInstallLocation
= PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
}
if (ret == PackageManager.INSTALL_SUCCEEDED) {
int loc = pkgLite.recommendedInstallLocation;
if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
} else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
ret = PackageManager.INSTALL_FAILED_INVALID_APK;
} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
ret = PackageManager.INSTALL_FAILED_INVALID_URI;
} else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
} else {
// Override with defaults if needed.
loc = installLocationPolicy(pkgLite);
if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
} else if (loc == PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION) {
ret = PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
} else if (!onInt) {
// Override install location with flags
if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
// Set the flag to install on external media.
installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
if (DEBUG_INSTANT) {
Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
}
installFlags |= PackageManager.INSTALL_INSTANT_APP;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else {
// Make sure the flag for installing on external
// media is unset
installFlags |= PackageManager.INSTALL_INTERNAL;
}
}
}
}
final InstallArgs args = createInstallArgs(this);
mVerificationCompleted = true;
mIntegrityVerificationCompleted = true;
mEnableRollbackCompleted = true;
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
final int verificationId = mPendingVerificationToken++;
// Perform package verification (unless we are simply moving the package).
if (!origin.existing) {
PackageVerificationState verificationState =
new PackageVerificationState(this);
mPendingVerification.append(verificationId, verificationState);
sendIntegrityVerificationRequest(verificationId, pkgLite, verificationState);
ret = sendPackageVerificationRequest(
verificationId, pkgLite, verificationState);
// If both verifications are skipped, we should remove the state.
if (verificationState.areAllVerificationsComplete()) {
mPendingVerification.remove(verificationId);
}
}
if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
// TODO(ruhler) b/112431924: Don't do this in case of 'move'?
final int enableRollbackToken = mPendingEnableRollbackToken++;
Trace.asyncTraceBegin(
TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
mPendingEnableRollback.append(enableRollbackToken, this);
Intent enableRollbackIntent = new Intent(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
enableRollbackIntent.putExtra(
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN,
enableRollbackToken);
enableRollbackIntent.putExtra(
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID,
mSessionId);
enableRollbackIntent.setType(PACKAGE_MIME_TYPE);
enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Allow the broadcast to be sent before boot complete.
// This is needed when committing the apk part of a staged
// session in early boot. The rollback manager registers
// its receiver early enough during the boot process that
// it will not miss the broadcast.
enableRollbackIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendOrderedBroadcastAsUser(enableRollbackIntent, UserHandle.SYSTEM,
android.Manifest.permission.PACKAGE_ROLLBACK_AGENT,
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// the duration to wait for rollback to be enabled, in millis
long rollbackTimeout = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ROLLBACK,
PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS);
if (rollbackTimeout < 0) {
rollbackTimeout = DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS;
}
final Message msg = mHandler.obtainMessage(
ENABLE_ROLLBACK_TIMEOUT);
msg.arg1 = enableRollbackToken;
msg.arg2 = mSessionId;
mHandler.sendMessageDelayed(msg, rollbackTimeout);
}
}, null, 0, null, null);
mEnableRollbackCompleted = false;
}
}
mRet = ret;
}
/**
* Send a request to check the integrity of the package.
*/
void sendIntegrityVerificationRequest(
int verificationId,
PackageInfoLite pkgLite,
PackageVerificationState verificationState) {
if (!isIntegrityVerificationEnabled()) {
// Consider the integrity check as passed.
verificationState.setIntegrityVerificationResult(
PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
return;
}
final Intent integrityVerification =
new Intent(Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
integrityVerification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
PACKAGE_MIME_TYPE);
final int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND;
integrityVerification.addFlags(flags);
integrityVerification.putExtra(EXTRA_VERIFICATION_ID, verificationId);
integrityVerification.putExtra(EXTRA_PACKAGE_NAME, pkgLite.packageName);
integrityVerification.putExtra(EXTRA_VERSION_CODE, pkgLite.versionCode);
integrityVerification.putExtra(EXTRA_LONG_VERSION_CODE, pkgLite.getLongVersionCode());
populateInstallerExtras(integrityVerification);
// send to integrity component only.
integrityVerification.setPackage("android");
DeviceIdleInternal idleController =
mInjector.getLocalDeviceIdleController();
final long idleDuration = getVerificationTimeout();
idleController.addPowerSaveTempWhitelistAppDirect(Process.myUid(),
idleDuration,
false, "integrity component");
final BroadcastOptions options = BroadcastOptions.makeBasic();
options.setTemporaryAppWhitelistDuration(idleDuration);
mContext.sendOrderedBroadcastAsUser(integrityVerification, UserHandle.SYSTEM,
/* receiverPermission= */ null,
/* appOp= */ AppOpsManager.OP_NONE,
/* options= */ options.toBundle(),
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final Message msg =
mHandler.obtainMessage(CHECK_PENDING_INTEGRITY_VERIFICATION);
msg.arg1 = verificationId;
mHandler.sendMessageDelayed(msg, getIntegrityVerificationTimeout());
}
}, /* scheduler= */ null,
/* initialCode= */ 0,
/* initialData= */ null,
/* initialExtras= */ null);
Trace.asyncTraceBegin(
TRACE_TAG_PACKAGE_MANAGER, "integrity_verification", verificationId);
// stop the copy until verification succeeds.
mIntegrityVerificationCompleted = false;
}
/**
* Send a request to verifier(s) to verify the package if necessary, and return
* {@link PackageManager#INSTALL_SUCCEEDED} if succeeded.
*/
int sendPackageVerificationRequest(
int verificationId,
PackageInfoLite pkgLite,
PackageVerificationState verificationState) {
int ret = INSTALL_SUCCEEDED;
// TODO: http://b/22976637
// Apps installed for "all" users use the device owner to verify the app
UserHandle verifierUser = getUser();
if (verifierUser == UserHandle.ALL) {
verifierUser = UserHandle.SYSTEM;
}
/*
* Determine if we have any installed package verifiers. If we
* do, then we'll defer to them to verify the packages.
*/
final int requiredUid = mRequiredVerifierPackage == null ? -1
: getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
verifierUser.getIdentifier());
verificationState.setRequiredVerifierUid(requiredUid);
final int installerUid =
verificationInfo == null ? -1 : verificationInfo.installerUid;
final boolean isVerificationEnabled = isVerificationEnabled(
pkgLite, verifierUser.getIdentifier(), installFlags, installerUid);
final boolean isV4Signed =
(mArgs.signingDetails.signatureSchemeVersion == SIGNING_BLOCK_V4);
final boolean isIncrementalInstall =
(mArgs.mDataLoaderType == DataLoaderType.INCREMENTAL);
// NOTE: We purposefully skip verification for only incremental installs when there's
// a v4 signature block. Otherwise, proceed with verification as usual.
if (!origin.existing
&& isVerificationEnabled
&& (!isIncrementalInstall || !isV4Signed)) {
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
PACKAGE_MIME_TYPE);
verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Query all live verifiers based on current user state
final List<ResolveInfo> receivers = queryIntentReceiversInternal(verification,
PACKAGE_MIME_TYPE, 0, verifierUser.getIdentifier(),
false /*allowDynamicSplits*/);
if (DEBUG_VERIFY) {
Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "
+ verification.toString() + " with " + pkgLite.verifiers.length
+ " optional verifiers");
}
verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
verification.putExtra(
PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS, installFlags);
verification.putExtra(
PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME, pkgLite.packageName);
verification.putExtra(
PackageManager.EXTRA_VERIFICATION_VERSION_CODE, pkgLite.versionCode);
verification.putExtra(
PackageManager.EXTRA_VERIFICATION_LONG_VERSION_CODE,
pkgLite.getLongVersionCode());
populateInstallerExtras(verification);
final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
receivers, verificationState);
DeviceIdleInternal idleController =
mInjector.getLocalDeviceIdleController();
final long idleDuration = getVerificationTimeout();
final BroadcastOptions options = BroadcastOptions.makeBasic();
options.setTemporaryAppWhitelistDuration(idleDuration);
/*
* If any sufficient verifiers were listed in the package
* manifest, attempt to ask them.
*/
if (sufficientVerifiers != null) {
final int n = sufficientVerifiers.size();
if (n == 0) {
Slog.i(TAG, "Additional verifiers required, but none installed.");
ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
} else {
for (int i = 0; i < n; i++) {
final ComponentName verifierComponent = sufficientVerifiers.get(i);
idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
verifierComponent.getPackageName(), idleDuration,
verifierUser.getIdentifier(), false, "package verifier");
final Intent sufficientIntent = new Intent(verification);
sufficientIntent.setComponent(verifierComponent);
mContext.sendBroadcastAsUser(sufficientIntent, verifierUser,
/* receiverPermission= */ null,
options.toBundle());
}
}
}
if (mRequiredVerifierPackage != null) {
final ComponentName requiredVerifierComponent = matchComponentForVerifier(
mRequiredVerifierPackage, receivers);
/*
* Send the intent to the required verification agent,
* but only start the verification timeout after the
* target BroadcastReceivers have run.
*/
verification.setComponent(requiredVerifierComponent);
idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
mRequiredVerifierPackage, idleDuration,
verifierUser.getIdentifier(), false, "package verifier");
mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
/* appOp= */ AppOpsManager.OP_NONE,
/* options= */ options.toBundle(),
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final Message msg = mHandler
.obtainMessage(CHECK_PENDING_VERIFICATION);
msg.arg1 = verificationId;
mHandler.sendMessageDelayed(msg, getVerificationTimeout());
}
}, null, 0, null, null);
Trace.asyncTraceBegin(
TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
/*
* We don't want the copy to proceed until verification
* succeeds.
*/
mVerificationCompleted = false;
}
} else {
verificationState.setVerifierResponse(
requiredUid, PackageManager.VERIFICATION_ALLOW);
}
return ret;
}
void populateInstallerExtras(Intent intent) {
intent.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE,
installSource.initiatingPackageName);
if (verificationInfo != null) {
if (verificationInfo.originatingUri != null) {
intent.putExtra(Intent.EXTRA_ORIGINATING_URI,
verificationInfo.originatingUri);
}
if (verificationInfo.referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER,
verificationInfo.referrer);
}
if (verificationInfo.originatingUid >= 0) {
intent.putExtra(Intent.EXTRA_ORIGINATING_UID,
verificationInfo.originatingUid);
}
if (verificationInfo.installerUid >= 0) {
intent.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID,
verificationInfo.installerUid);
}
}
}
void setReturnCode(int ret) {
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
// Only update mRet if it was previously INSTALL_SUCCEEDED to
// ensure we do not overwrite any previous failure results.
mRet = ret;
}
}
void handleVerificationFinished() {
if (!mVerificationCompleted) {
mVerificationCompleted = true;
if (mIntegrityVerificationCompleted) {
handleReturnCode();
}
// integrity verification still pending.
}
}
void handleIntegrityVerificationFinished() {
if (!mIntegrityVerificationCompleted) {
mIntegrityVerificationCompleted = true;
if (mVerificationCompleted) {
handleReturnCode();
}
// verifier still pending
}
}
void handleRollbackEnabled() {
// TODO(ruhler) b/112431924: Consider halting the install if we
// couldn't enable rollback.
mEnableRollbackCompleted = true;
handleReturnCode();
}
@Override
void handleReturnCode() {
if (mVerificationCompleted
&& mIntegrityVerificationCompleted && mEnableRollbackCompleted) {
if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
String packageName = "";
ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
new ParseTypeImpl(
(changeId, packageName1, targetSdkVersion) -> {
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.packageName = packageName1;
appInfo.targetSdkVersion = targetSdkVersion;
return mPackageParserCallback.isChangeEnabled(changeId,
appInfo);
}).reset(),
origin.file, 0);
if (result.isError()) {
Slog.e(TAG, "Can't parse package at " + origin.file.getAbsolutePath(),
result.getException());
} else {
packageName = result.getResult().packageName;
}
try {
observer.onPackageInstalled(packageName, mRet, "Dry run", new Bundle());
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
return;
}
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
mRet = mArgs.copyApk();
}
processPendingInstall(mArgs, mRet);
}
}
}
private InstallArgs createInstallArgs(InstallParams params) {
if (params.move != null) {
return new MoveInstallArgs(params);
} else {
return new FileInstallArgs(params);
}
}
/**
* Create args that describe an existing installed package. Typically used
* when cleaning up old installs, or used as a move source.
*/
private InstallArgs createInstallArgsForExisting(String codePath,
String resourcePath, String[] instructionSets) {
return new FileInstallArgs(codePath, resourcePath, instructionSets);
}
static abstract class InstallArgs {
/** @see InstallParams#origin */
final OriginInfo origin;
/** @see InstallParams#move */
final MoveInfo move;
final IPackageInstallObserver2 observer;
// Always refers to PackageManager flags only
final int installFlags;
@NonNull final InstallSource installSource;
final String volumeUuid;
final UserHandle user;
final String abiOverride;
final String[] installGrantPermissions;
final List<String> whitelistedRestrictedPermissions;
final int autoRevokePermissionsMode;
/** If non-null, drop an async trace when the install completes */
final String traceMethod;
final int traceCookie;
final PackageParser.SigningDetails signingDetails;
final int installReason;
final boolean forceQueryableOverride;
@Nullable final MultiPackageInstallParams mMultiPackageInstallParams;
final int mDataLoaderType;
// The list of instruction sets supported by this app. This is currently
// only used during the rmdex() phase to clean up resources. We can get rid of this
// if we move dex files under the common app path.
/* nullable */ String[] instructionSets;
InstallArgs(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
int installFlags, InstallSource installSource, String volumeUuid,
UserHandle user, String[] instructionSets,
String abiOverride, String[] installGrantPermissions,
List<String> whitelistedRestrictedPermissions,
int autoRevokePermissionsMode,
String traceMethod, int traceCookie, SigningDetails signingDetails,
int installReason, boolean forceQueryableOverride,
MultiPackageInstallParams multiPackageInstallParams, int dataLoaderType) {
this.origin = origin;
this.move = move;
this.installFlags = installFlags;
this.observer = observer;
this.installSource = Preconditions.checkNotNull(installSource);
this.volumeUuid = volumeUuid;
this.user = user;
this.instructionSets = instructionSets;
this.abiOverride = abiOverride;
this.installGrantPermissions = installGrantPermissions;
this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
this.autoRevokePermissionsMode = autoRevokePermissionsMode;
this.traceMethod = traceMethod;
this.traceCookie = traceCookie;
this.signingDetails = signingDetails;
this.installReason = installReason;
this.forceQueryableOverride = forceQueryableOverride;
this.mMultiPackageInstallParams = multiPackageInstallParams;
this.mDataLoaderType = dataLoaderType;
}
/** New install */
InstallArgs(InstallParams params) {
this(params.origin, params.move, params.observer, params.installFlags,
params.installSource, params.volumeUuid,
params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions,
params.autoRevokePermissionsMode,
params.traceMethod, params.traceCookie, params.signingDetails,
params.installReason, params.forceQueryableOverride,
params.mParentInstallParams, params.mDataLoaderType);
}
abstract int copyApk();
abstract int doPreInstall(int status);
/**
* Rename package into final resting place. All paths on the given
* scanned package should be updated to reflect the rename.
*/
abstract boolean doRename(int status, ParsedPackage parsedPackage);
abstract int doPostInstall(int status, int uid);
/** @see PackageSettingBase#codePathString */
abstract String getCodePath();
/** @see PackageSettingBase#resourcePathString */
abstract String getResourcePath();
// Need installer lock especially for dex file removal.
abstract void cleanUpResourcesLI();
abstract boolean doPostDeleteLI(boolean delete);
/**
* Called before the source arguments are copied. This is used mostly
* for MoveParams when it needs to read the source file to put it in the
* destination.
*/
int doPreCopy() {
return PackageManager.INSTALL_SUCCEEDED;
}
/**
* Called after the source arguments are copied. This is used mostly for
* MoveParams when it needs to read the source file to put it in the
* destination.
*/
int doPostCopy(int uid) {
return PackageManager.INSTALL_SUCCEEDED;
}
protected boolean isEphemeral() {
return (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
}
UserHandle getUser() {
return user;
}
}
void removeDexFiles(List<String> allCodePaths, String[] instructionSets) {
if (!allCodePaths.isEmpty()) {
if (instructionSets == null) {
throw new IllegalStateException("instructionSet == null");
}
String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
for (String codePath : allCodePaths) {
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
try {
mInstaller.rmdex(codePath, dexCodeInstructionSet);
} catch (InstallerException ignored) {
}
}
}
}
}
/**
* Logic to handle installation of new applications, including copying
* and renaming logic.
*/
class FileInstallArgs extends InstallArgs {
private File codeFile;
private File resourceFile;
// Example topology:
// /data/app/com.example/base.apk
// /data/app/com.example/split_foo.apk
// /data/app/com.example/lib/arm/libfoo.so
// /data/app/com.example/lib/arm64/libfoo.so
// /data/app/com.example/dalvik/arm/base.apk@classes.dex
/** New install */
FileInstallArgs(InstallParams params) {
super(params);
}
/** Existing install */
FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
PackageParser.SigningDetails.UNKNOWN,
PackageManager.INSTALL_REASON_UNKNOWN, false, null /* parent */,
DataLoaderType.NONE);
this.codeFile = (codePath != null) ? new File(codePath) : null;
this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
}
int copyApk() {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
try {
return doCopyApk();
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private int doCopyApk() {
if (origin.staged) {
if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
codeFile = origin.file;
resourceFile = origin.file;
return PackageManager.INSTALL_SUCCEEDED;
}
try {
final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
final File tempDir =
mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
Slog.w(TAG, "Failed to create copy file: " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
int ret = PackageManagerServiceUtils.copyPackage(
origin.file.getAbsolutePath(), codeFile);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
}
final boolean isIncremental = isIncrementalPath(codeFile.getAbsolutePath());
final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(codeFile);
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
abiOverride, isIncremental);
} catch (IOException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
} finally {
IoUtils.closeQuietly(handle);
}
return ret;
}
int doPreInstall(int status) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp();
}
return status;
}
@Override
boolean doRename(int status, ParsedPackage parsedPackage) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp();
return false;
}
final File targetDir = codeFile.getParentFile();
final File beforeCodeFile = codeFile;
final File afterCodeFile = getNextCodePath(targetDir, parsedPackage.getPackageName());
if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
final boolean onIncremental = mIncrementalManager != null
&& isIncrementalPath(beforeCodeFile.getAbsolutePath());
try {
makeDirRecursive(afterCodeFile.getParentFile(), 0775);
if (onIncremental) {
mIncrementalManager.renameCodePath(beforeCodeFile, afterCodeFile);
} else {
Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
}
} catch (IOException | ErrnoException e) {
Slog.w(TAG, "Failed to rename", e);
return false;
}
//TODO(b/136132412): enable selinux restorecon for incremental directories
if (!onIncremental && !SELinux.restoreconRecursive(afterCodeFile)) {
Slog.w(TAG, "Failed to restorecon");
return false;
}
// Reflect the rename internally
codeFile = afterCodeFile;
resourceFile = afterCodeFile;
// Reflect the rename in scanned details
try {
parsedPackage.setCodePath(afterCodeFile.getCanonicalPath());
} catch (IOException e) {
Slog.e(TAG, "Failed to get path: " + afterCodeFile, e);
return false;
}
parsedPackage.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
afterCodeFile, parsedPackage.getBaseCodePath()));
parsedPackage.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
afterCodeFile, parsedPackage.getSplitCodePaths()));
return true;
}
int doPostInstall(int status, int uid) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp();
}
return status;
}
@Override
String getCodePath() {
return (codeFile != null) ? codeFile.getAbsolutePath() : null;
}
@Override
String getResourcePath() {
return (resourceFile != null) ? resourceFile.getAbsolutePath() : null;
}
private boolean cleanUp() {
if (codeFile == null || !codeFile.exists()) {
return false;
}
String codePath = codeFile.getAbsolutePath();
if (mIncrementalManager != null && isIncrementalPath(codePath)) {
mIncrementalManager.closeStorage(codePath);
}
removeCodePathLI(codeFile);
if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
resourceFile.delete();
}
return true;
}
void cleanUpResourcesLI() {
// Try enumerating all code paths before deleting
List<String> allCodePaths = Collections.EMPTY_LIST;
if (codeFile != null && codeFile.exists()) {
try {
final PackageLite pkg = PackageParser.parsePackageLite(codeFile, 0);
allCodePaths = pkg.getAllCodePaths();
} catch (PackageParserException e) {
// Ignored; we tried our best
}
}
cleanUp();
removeDexFiles(allCodePaths, instructionSets);
}
boolean doPostDeleteLI(boolean delete) {
// XXX err, shouldn't we respect the delete flag?
cleanUpResourcesLI();
return true;
}
}
/**
* Logic to handle movement of existing installed applications.
*/
class MoveInstallArgs extends InstallArgs {
private File codeFile;
private File resourceFile;
/** New install */
MoveInstallArgs(InstallParams params) {
super(params);
}
int copyApk() {
if (DEBUG_INSTALL) Slog.d(TAG, "Moving " + move.packageName + " from "
+ move.fromUuid + " to " + move.toUuid);
synchronized (mInstaller) {
try {
mInstaller.moveCompleteApp(move.fromUuid, move.toUuid, move.packageName,
move.appId, move.seinfo, move.targetSdkVersion,
move.fromCodePath);
} catch (InstallerException e) {
Slog.w(TAG, "Failed to move app", e);
return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
}
}
final String toPathName = new File(move.fromCodePath).getName();
codeFile = new File(Environment.getDataAppDirectory(move.toUuid), toPathName);
resourceFile = codeFile;
if (DEBUG_INSTALL) Slog.d(TAG, "codeFile after move is " + codeFile);
return PackageManager.INSTALL_SUCCEEDED;
}
int doPreInstall(int status) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp(move.toUuid);
}
return status;
}
@Override
boolean doRename(int status, ParsedPackage parsedPackage) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp(move.toUuid);
return false;
}
return true;
}
int doPostInstall(int status, int uid) {
if (status == PackageManager.INSTALL_SUCCEEDED) {
cleanUp(move.fromUuid);
} else {
cleanUp(move.toUuid);
}
return status;
}
@Override
String getCodePath() {
return (codeFile != null) ? codeFile.getAbsolutePath() : null;
}
@Override
String getResourcePath() {
return (resourceFile != null) ? resourceFile.getAbsolutePath() : null;
}
private boolean cleanUp(String volumeUuid) {
final String toPathName = new File(move.fromCodePath).getName();
final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid),
toPathName);
Slog.d(TAG, "Cleaning up " + move.packageName + " on " + volumeUuid);
final int[] userIds = mUserManager.getUserIds();
synchronized (mInstallLock) {
// Clean up both app data and code
// All package moves are frozen until finished
// We purposefully exclude FLAG_STORAGE_EXTERNAL here, since
// this task was only focused on moving data on internal storage.
// We don't want ART profiles cleared, because they don't move,
// so we would be deleting the only copy (b/149200535).
final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE
| Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES;
for (int userId : userIds) {
try {
mInstaller.destroyAppData(volumeUuid, move.packageName, userId, flags, 0);
} catch (InstallerException e) {
Slog.w(TAG, String.valueOf(e));
}
}
removeCodePathLI(codeFile);
}
return true;
}
void cleanUpResourcesLI() {
throw new UnsupportedOperationException();
}
boolean doPostDeleteLI(boolean delete) {
throw new UnsupportedOperationException();
}
}
/**
* Given {@code targetDir}, returns {@code targetDir/~~[randomStrA]/[packageName]-[randomStrB].}
* Makes sure that {@code targetDir/~~[randomStrA]} directory doesn't exist.
* Notice that this method doesn't actually create any directory.
*
* @param targetDir Directory that is two-levels up from the result directory.
* @param packageName Name of the package whose code files are to be installed under the result
* directory.
* @return File object for the directory that should hold the code files of {@code packageName}.
*/
private File getNextCodePath(File targetDir, String packageName) {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[16];
File firstLevelDir;
do {
random.nextBytes(bytes);
String dirName = RANDOM_DIR_PREFIX
+ Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP);
firstLevelDir = new File(targetDir, dirName);
} while (firstLevelDir.exists());
random.nextBytes(bytes);
String suffix = Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP);
return new File(firstLevelDir, packageName + "-" + suffix);
}
static class PackageInstalledInfo {
String name;
int uid;
// The set of users that originally had this package installed.
int[] origUsers;
// The set of users that now have this package installed.
int[] newUsers;
AndroidPackage pkg;
int returnCode;
String returnMsg;
String installerPackageName;
PackageRemovedInfo removedInfo;
// The set of packages consuming this shared library or null if no consumers exist.
ArrayList<AndroidPackage> libraryConsumers;
PackageFreezer freezer;
public void setError(int code, String msg) {
setReturnCode(code);
setReturnMessage(msg);
Slog.w(TAG, msg);
}
public void setError(String msg, PackageParserException e) {
setReturnCode(e.error);
setReturnMessage(ExceptionUtils.getCompleteMessage(msg, e));
Slog.w(TAG, msg, e);
}
public void setError(String msg, PackageManagerException e) {
returnCode = e.error;
setReturnMessage(ExceptionUtils.getCompleteMessage(msg, e));
Slog.w(TAG, msg, e);
}
public void setReturnCode(int returnCode) {
this.returnCode = returnCode;
}
private void setReturnMessage(String returnMsg) {
this.returnMsg = returnMsg;
}
// In some error cases we want to convey more info back to the observer
String origPackage;
String origPermission;
}
private static void updateDigest(MessageDigest digest, File file) throws IOException {
try (DigestInputStream digestStream =
new DigestInputStream(new FileInputStream(file), digest)) {
while (digestStream.read() != -1) {} // nothing to do; just plow through the file
}
}
private void removeNativeBinariesLI(PackageSetting ps) {
if (ps != null) {
NativeLibraryHelper.removeNativeBinariesLI(ps.legacyNativeLibraryPathString);
}
}
@GuardedBy("mLock")
private void enableSystemPackageLPw(AndroidPackage pkg) {
mSettings.enableSystemPackageLPw(pkg.getPackageName());
}
@GuardedBy("mLock")
private boolean disableSystemPackageLPw(AndroidPackage oldPkg) {
return mSettings.disableSystemPackageLPw(oldPkg.getPackageName(), true);
}
private void updateSettingsLI(AndroidPackage newPackage, InstallArgs installArgs,
int[] allUsers, PackageInstalledInfo res) {
updateSettingsInternalLI(newPackage, installArgs, allUsers, res);
}
private void updateSettingsInternalLI(AndroidPackage pkg, InstallArgs installArgs,
int[] allUsers, PackageInstalledInfo res) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
final String pkgName = pkg.getPackageName();
final int[] installedForUsers = res.origUsers;
final int installReason = installArgs.installReason;
InstallSource installSource = installArgs.installSource;
final String installerPackageName = installSource.installerPackageName;
if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getCodePath());
synchronized (mLock) {
// NOTE: This changes slightly to include UPDATE_PERMISSIONS_ALL regardless of the size of pkg.permissions
mPermissionManager.updatePermissions(pkgName, pkg);
// For system-bundled packages, we assume that installing an upgraded version
// of the package implies that the user actually wants to run that new code,
// so we enable the package.
final PackageSetting ps = mSettings.mPackages.get(pkgName);
final int userId = installArgs.user.getIdentifier();
if (ps != null) {
if (pkg.isSystem()) {
if (DEBUG_INSTALL) {
Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
}
// Enable system package for requested users
if (res.origUsers != null) {
for (int origUserId : res.origUsers) {
if (userId == UserHandle.USER_ALL || userId == origUserId) {
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
origUserId, installerPackageName);
}
}
}
// Also convey the prior install/uninstall state
if (allUsers != null && installedForUsers != null) {
for (int currentUserId : allUsers) {
final boolean installed = ArrayUtils.contains(
installedForUsers, currentUserId);
if (DEBUG_INSTALL) {
Slog.d(TAG, " user " + currentUserId + " => " + installed);
}
ps.setInstalled(installed, currentUserId);
}
// these install state changes will be persisted in the
// upcoming call to mSettings.writeLPr().
}
if (allUsers != null) {
for (int currentUserId : allUsers) {
ps.resetOverrideComponentLabelIcon(currentUserId);
}
}
}
// Retrieve the overlays for shared libraries of the package.
if (!ps.getPkgState().getUsesLibraryInfos().isEmpty()) {
for (SharedLibraryInfo sharedLib : ps.getPkgState().getUsesLibraryInfos()) {
for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
if (!sharedLib.isDynamic()) {
// TODO(146804378): Support overlaying static shared libraries
continue;
}
final PackageSetting libPs = mSettings.mPackages.get(
sharedLib.getPackageName());
if (libPs == null) {
continue;
}
final String[] overlayPaths = libPs.getOverlayPaths(currentUserId);
if (overlayPaths != null) {
ps.setOverlayPathsForLibrary(sharedLib.getName(),
Arrays.asList(overlayPaths), currentUserId);
}
}
}
}
// It's implied that when a user requests installation, they want the app to be
// installed and enabled. (This does not apply to USER_ALL, which here means only
// install on users for which the app is already installed).
if (userId != UserHandle.USER_ALL) {
ps.setInstalled(true, userId);
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
}
mSettings.addInstallerPackageNames(ps.installSource);
// When replacing an existing package, preserve the original install reason for all
// users that had the package installed before. Similarly for uninstall reasons.
final Set<Integer> previousUserIds = new ArraySet<>();
if (res.removedInfo != null && res.removedInfo.installReasons != null) {
final int installReasonCount = res.removedInfo.installReasons.size();
for (int i = 0; i < installReasonCount; i++) {
final int previousUserId = res.removedInfo.installReasons.keyAt(i);
final int previousInstallReason = res.removedInfo.installReasons.valueAt(i);
ps.setInstallReason(previousInstallReason, previousUserId);
previousUserIds.add(previousUserId);
}
}
if (res.removedInfo != null && res.removedInfo.uninstallReasons != null) {
for (int i = 0; i < res.removedInfo.uninstallReasons.size(); i++) {
final int previousUserId = res.removedInfo.uninstallReasons.keyAt(i);
final int previousReason = res.removedInfo.uninstallReasons.valueAt(i);
ps.setUninstallReason(previousReason, previousUserId);
}
}
// Set install reason for users that are having the package newly installed.
final int[] allUsersList = mUserManager.getUserIds();
if (userId == UserHandle.USER_ALL) {
// TODO(b/152629990): It appears that the package doesn't actually get newly
// installed in this case, so the installReason shouldn't get modified?
for (int currentUserId : allUsersList) {
if (!previousUserIds.contains(currentUserId)) {
ps.setInstallReason(installReason, currentUserId);
}
}
} else if (!previousUserIds.contains(userId)) {
ps.setInstallReason(installReason, userId);
}
// Ensure that the uninstall reason is UNKNOWN for users with the package installed.
for (int currentUserId : allUsersList) {
if (ps.getInstalled(currentUserId)) {
ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, currentUserId);
}
}
mSettings.writeKernelMappingLPr(ps);
}
res.name = pkgName;
res.uid = pkg.getUid();
res.pkg = pkg;
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
//to update install status
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
mSettings.writeLPr();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
private static class InstallRequest {
public final InstallArgs args;
public final PackageInstalledInfo installResult;
private InstallRequest(InstallArgs args, PackageInstalledInfo res) {
this.args = args;
this.installResult = res;
}
}
@GuardedBy({"mInstallLock", "mLock"})
private void installPackagesTracedLI(List<InstallRequest> requests) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
installPackagesLI(requests);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
/**
* Package state to commit to memory and disk after reconciliation has completed.
*/
private static class CommitRequest {
final Map<String, ReconciledPackage> reconciledPackages;
final int[] mAllUsers;
private CommitRequest(Map<String, ReconciledPackage> reconciledPackages, int[] allUsers) {
this.reconciledPackages = reconciledPackages;
this.mAllUsers = allUsers;
}
}
/**
* Package scan results and related request details used to reconcile the potential addition of
* one or more packages to the system.
*
* Reconcile will take a set of package details that need to be committed to the system and make
* sure that they are valid in the context of the system and the other installing apps. Any
* invalid state or app will result in a failed reconciliation and thus whatever operation (such
* as install) led to the request.
*/
private static class ReconcileRequest {
public final Map<String, ScanResult> scannedPackages;
public final Map<String, AndroidPackage> allPackages;
public final Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource;
public final Map<String, InstallArgs> installArgs;
public final Map<String, PackageInstalledInfo> installResults;
public final Map<String, PrepareResult> preparedPackages;
public final Map<String, VersionInfo> versionInfos;
public final Map<String, PackageSetting> lastStaticSharedLibSettings;
private ReconcileRequest(Map<String, ScanResult> scannedPackages,
Map<String, InstallArgs> installArgs,
Map<String, PackageInstalledInfo> installResults,
Map<String, PrepareResult> preparedPackages,
Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
Map<String, AndroidPackage> allPackages,
Map<String, VersionInfo> versionInfos,
Map<String, PackageSetting> lastStaticSharedLibSettings) {
this.scannedPackages = scannedPackages;
this.installArgs = installArgs;
this.installResults = installResults;
this.preparedPackages = preparedPackages;
this.sharedLibrarySource = sharedLibrarySource;
this.allPackages = allPackages;
this.versionInfos = versionInfos;
this.lastStaticSharedLibSettings = lastStaticSharedLibSettings;
}
private ReconcileRequest(Map<String, ScanResult> scannedPackages,
Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
Map<String, AndroidPackage> allPackages,
Map<String, VersionInfo> versionInfos,
Map<String, PackageSetting> lastStaticSharedLibSettings) {
this(scannedPackages, Collections.emptyMap(), Collections.emptyMap(),
Collections.emptyMap(), sharedLibrarySource, allPackages, versionInfos,
lastStaticSharedLibSettings);
}
}
private static class ReconcileFailure extends PackageManagerException {
ReconcileFailure(String message) {
super("Reconcile failed: " + message);
}
ReconcileFailure(int reason, String message) {
super(reason, "Reconcile failed: " + message);
}
ReconcileFailure(PackageManagerException e) {
this(e.error, e.getMessage());
}
}
/**
* A container of all data needed to commit a package to in-memory data structures and to disk.
* TODO: move most of the data contained her into a PackageSetting for commit.
*/
private static class ReconciledPackage {
public final ReconcileRequest request;
public final PackageSetting pkgSetting;
public final ScanResult scanResult;
// TODO: Remove install-specific details from the reconcile result
public final PackageInstalledInfo installResult;
@Nullable public final PrepareResult prepareResult;
@Nullable public final InstallArgs installArgs;
public final DeletePackageAction deletePackageAction;
public final List<SharedLibraryInfo> allowedSharedLibraryInfos;
public final SigningDetails signingDetails;
public final boolean sharedUserSignaturesChanged;
public ArrayList<SharedLibraryInfo> collectedSharedLibraryInfos;
public final boolean removeAppKeySetData;
private ReconciledPackage(ReconcileRequest request,
InstallArgs installArgs,
PackageSetting pkgSetting,
PackageInstalledInfo installResult,
PrepareResult prepareResult,
ScanResult scanResult,
DeletePackageAction deletePackageAction,
List<SharedLibraryInfo> allowedSharedLibraryInfos,
SigningDetails signingDetails,
boolean sharedUserSignaturesChanged,
boolean removeAppKeySetData) {
this.request = request;
this.installArgs = installArgs;
this.pkgSetting = pkgSetting;
this.installResult = installResult;
this.prepareResult = prepareResult;
this.scanResult = scanResult;
this.deletePackageAction = deletePackageAction;
this.allowedSharedLibraryInfos = allowedSharedLibraryInfos;
this.signingDetails = signingDetails;
this.sharedUserSignaturesChanged = sharedUserSignaturesChanged;
this.removeAppKeySetData = removeAppKeySetData;
}
/**
* Returns a combined set of packages containing the packages already installed combined
* with the package(s) currently being installed. The to-be installed packages take
* precedence and may shadow already installed packages.
*/
private Map<String, AndroidPackage> getCombinedAvailablePackages() {
final ArrayMap<String, AndroidPackage> combined =
new ArrayMap<>(request.allPackages.size() + request.scannedPackages.size());
combined.putAll(request.allPackages);
for (ScanResult scanResult : request.scannedPackages.values()) {
combined.put(scanResult.pkgSetting.name, scanResult.request.parsedPackage);
}
return combined;
}
}
@GuardedBy("mLock")
private static Map<String, ReconciledPackage> reconcilePackagesLocked(
final ReconcileRequest request, KeySetManagerService ksms)
throws ReconcileFailure {
final Map<String, ScanResult> scannedPackages = request.scannedPackages;
final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());
// make a copy of the existing set of packages so we can combine them with incoming packages
final ArrayMap<String, AndroidPackage> combinedPackages =
new ArrayMap<>(request.allPackages.size() + scannedPackages.size());
combinedPackages.putAll(request.allPackages);
final Map<String, LongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
new ArrayMap<>();
for (String installPackageName : scannedPackages.keySet()) {
final ScanResult scanResult = scannedPackages.get(installPackageName);
// add / replace existing with incoming packages
combinedPackages.put(scanResult.pkgSetting.name, scanResult.request.parsedPackage);
// in the first pass, we'll build up the set of incoming shared libraries
final List<SharedLibraryInfo> allowedSharedLibInfos =
getAllowedSharedLibInfos(scanResult, request.sharedLibrarySource);
final SharedLibraryInfo staticLib = scanResult.staticSharedLibraryInfo;
if (allowedSharedLibInfos != null) {
for (SharedLibraryInfo info : allowedSharedLibInfos) {
if (!addSharedLibraryToPackageVersionMap(incomingSharedLibraries, info)) {
throw new ReconcileFailure("Static Shared Library " + staticLib.getName()
+ " is being installed twice in this set!");
}
}
}
// the following may be null if we're just reconciling on boot (and not during install)
final InstallArgs installArgs = request.installArgs.get(installPackageName);
final PackageInstalledInfo res = request.installResults.get(installPackageName);
final PrepareResult prepareResult = request.preparedPackages.get(installPackageName);
final boolean isInstall = installArgs != null;
if (isInstall && (res == null || prepareResult == null)) {
throw new ReconcileFailure("Reconcile arguments are not balanced for "
+ installPackageName + "!");
}
final DeletePackageAction deletePackageAction;
// we only want to try to delete for non system apps
if (isInstall && prepareResult.replace && !prepareResult.system) {
final boolean killApp = (scanResult.request.scanFlags & SCAN_DONT_KILL_APP) == 0;
final int deleteFlags = PackageManager.DELETE_KEEP_DATA
| (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
deletePackageAction = mayDeletePackageLocked(res.removedInfo,
prepareResult.originalPs, prepareResult.disabledPs,
deleteFlags, null /* all users */);
if (deletePackageAction == null) {
throw new ReconcileFailure(
PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
"May not delete " + installPackageName + " to replace");
}
} else {
deletePackageAction = null;
}
final int scanFlags = scanResult.request.scanFlags;
final int parseFlags = scanResult.request.parseFlags;
final ParsedPackage parsedPackage = scanResult.request.parsedPackage;
final PackageSetting disabledPkgSetting = scanResult.request.disabledPkgSetting;
final PackageSetting lastStaticSharedLibSetting =
request.lastStaticSharedLibSettings.get(installPackageName);
final PackageSetting signatureCheckPs =
(prepareResult != null && lastStaticSharedLibSetting != null)
? lastStaticSharedLibSetting
: scanResult.pkgSetting;
boolean removeAppKeySetData = false;
boolean sharedUserSignaturesChanged = false;
SigningDetails signingDetails = null;
if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
// We just determined the app is signed correctly, so bring
// over the latest parsed certs.
} else {
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"Package " + parsedPackage.getPackageName()
+ " upgrade keys do not match the previously installed"
+ " version");
} else {
String msg = "System package " + parsedPackage.getPackageName()
+ " signature changed; retaining data.";
reportSettingsProblem(Log.WARN, msg);
}
}
signingDetails = parsedPackage.getSigningDetails();
} else {
try {
final VersionInfo versionInfo = request.versionInfos.get(installPackageName);
final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
final boolean compatMatch = verifySignatures(signatureCheckPs,
disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat,
compareRecover);
// The new KeySets will be re-added later in the scanning process.
if (compatMatch) {
removeAppKeySetData = true;
}
// We just determined the app is signed correctly, so bring
// over the latest parsed certs.
signingDetails = parsedPackage.getSigningDetails();
// if this is is a sharedUser, check to see if the new package is signed by a
// newer
// signing certificate than the existing one, and if so, copy over the new
// details
if (signatureCheckPs.sharedUser != null) {
// Attempt to merge the existing lineage for the shared SigningDetails with
// the lineage of the new package; if the shared SigningDetails are not
// returned this indicates the new package added new signers to the lineage
// and/or changed the capabilities of existing signers in the lineage.
SigningDetails sharedSigningDetails =
signatureCheckPs.sharedUser.signatures.mSigningDetails;
SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
signingDetails);
if (mergedDetails != sharedSigningDetails) {
signatureCheckPs.sharedUser.signatures.mSigningDetails = mergedDetails;
}
if (signatureCheckPs.sharedUser.signaturesChanged == null) {
signatureCheckPs.sharedUser.signaturesChanged = Boolean.FALSE;
}
}
} catch (PackageManagerException e) {
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
throw new ReconcileFailure(e);
}
signingDetails = parsedPackage.getSigningDetails();
// If the system app is part of a shared user we allow that shared user to
// change
// signatures as well as part of an OTA. We still need to verify that the
// signatures
// are consistent within the shared user for a given boot, so only allow
// updating
// the signatures on the first package scanned for the shared user (i.e. if the
// signaturesChanged state hasn't been initialized yet in SharedUserSetting).
if (signatureCheckPs.sharedUser != null) {
final Signature[] sharedUserSignatures =
signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures;
if (signatureCheckPs.sharedUser.signaturesChanged != null
&& compareSignatures(sharedUserSignatures,
parsedPackage.getSigningDetails().signatures)
!= PackageManager.SIGNATURE_MATCH) {
if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
// Mismatched signatures is an error and silently skipping system
// packages will likely break the device in unforeseen ways.
// However, we allow the device to boot anyway because, prior to Q,
// vendors were not expecting the platform to crash in this
// situation.
// This WILL be a hard failure on any new API levels after Q.
throw new ReconcileFailure(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
"Signature mismatch for shared user: "
+ scanResult.pkgSetting.sharedUser);
} else {
// Treat mismatched signatures on system packages using a shared
// UID as
// fatal for the system overall, rather than just failing to install
// whichever package happened to be scanned later.
throw new IllegalStateException(
"Signature mismatch on system package "
+ parsedPackage.getPackageName()
+ " for shared user "
+ scanResult.pkgSetting.sharedUser);
}
}
sharedUserSignaturesChanged = true;
signatureCheckPs.sharedUser.signatures.mSigningDetails =
parsedPackage.getSigningDetails();
signatureCheckPs.sharedUser.signaturesChanged = Boolean.TRUE;
}
// File a report about this.
String msg = "System package " + parsedPackage.getPackageName()
+ " signature changed; retaining data.";
reportSettingsProblem(Log.WARN, msg);
} catch (IllegalArgumentException e) {
// should never happen: certs matched when checking, but not when comparing
// old to new for sharedUser
throw new RuntimeException(
"Signing certificates comparison made on incomparable signing details"
+ " but somehow passed verifySignatures!", e);
}
}
result.put(installPackageName,
new ReconciledPackage(request, installArgs, scanResult.pkgSetting,
res, request.preparedPackages.get(installPackageName), scanResult,
deletePackageAction, allowedSharedLibInfos, signingDetails,
sharedUserSignaturesChanged, removeAppKeySetData));
}
for (String installPackageName : scannedPackages.keySet()) {
// Check all shared libraries and map to their actual file path.
// We only do this here for apps not on a system dir, because those
// are the only ones that can fail an install due to this. We
// will take care of the system apps by updating all of their
// library paths after the scan is done. Also during the initial
// scan don't update any libs as we do this wholesale after all
// apps are scanned to avoid dependency based scanning.
final ScanResult scanResult = scannedPackages.get(installPackageName);
if ((scanResult.request.scanFlags & SCAN_BOOTING) != 0
|| (scanResult.request.parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
continue;
}
try {
result.get(installPackageName).collectedSharedLibraryInfos =
collectSharedLibraryInfos(scanResult.request.parsedPackage,
combinedPackages, request.sharedLibrarySource,
incomingSharedLibraries);
} catch (PackageManagerException e) {
throw new ReconcileFailure(e.error, e.getMessage());
}
}
return result;
}
/**
* Compare the newly scanned package with current system state to see which of its declared
* shared libraries should be allowed to be added to the system.
*/
private static List<SharedLibraryInfo> getAllowedSharedLibInfos(
ScanResult scanResult,
Map<String, LongSparseArray<SharedLibraryInfo>> existingSharedLibraries) {
// Let's used the parsed package as scanResult.pkgSetting may be null
final ParsedPackage parsedPackage = scanResult.request.parsedPackage;
if (scanResult.staticSharedLibraryInfo == null
&& scanResult.dynamicSharedLibraryInfos == null) {
return null;
}
// Any app can add new static shared libraries
if (scanResult.staticSharedLibraryInfo != null) {
return Collections.singletonList(scanResult.staticSharedLibraryInfo);
}
final boolean hasDynamicLibraries = parsedPackage.isSystem()
&& scanResult.dynamicSharedLibraryInfos != null;
if (!hasDynamicLibraries) {
return null;
}
final boolean isUpdatedSystemApp = scanResult.pkgSetting.getPkgState()
.isUpdatedSystemApp();
// We may not yet have disabled the updated package yet, so be sure to grab the
// current setting if that's the case.
final PackageSetting updatedSystemPs = isUpdatedSystemApp
? scanResult.request.disabledPkgSetting == null
? scanResult.request.oldPkgSetting
: scanResult.request.disabledPkgSetting
: null;
if (isUpdatedSystemApp && (updatedSystemPs.pkg == null
|| updatedSystemPs.pkg.getLibraryNames() == null)) {
Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ " declares libraries that are not declared on the system image; skipping");
return null;
}
final ArrayList<SharedLibraryInfo> infos =
new ArrayList<>(scanResult.dynamicSharedLibraryInfos.size());
for (SharedLibraryInfo info : scanResult.dynamicSharedLibraryInfos) {
final String name = info.getName();
if (isUpdatedSystemApp) {
// New library entries can only be added through the
// system image. This is important to get rid of a lot
// of nasty edge cases: for example if we allowed a non-
// system update of the app to add a library, then uninstalling
// the update would make the library go away, and assumptions
// we made such as through app install filtering would now
// have allowed apps on the device which aren't compatible
// with it. Better to just have the restriction here, be
// conservative, and create many fewer cases that can negatively
// impact the user experience.
if (!updatedSystemPs.pkg.getLibraryNames().contains(name)) {
Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ " declares library " + name
+ " that is not declared on system image; skipping");
continue;
}
}
if (sharedLibExists(
name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
Slog.w(TAG, "Package " + parsedPackage.getPackageName() + " declares library "
+ name + " that already exists; skipping");
continue;
}
infos.add(info);
}
return infos;
}
/**
* Returns false if the adding shared library already exists in the map and so could not be
* added.
*/
private static boolean addSharedLibraryToPackageVersionMap(
Map<String, LongSparseArray<SharedLibraryInfo>> target,
SharedLibraryInfo library) {
final String name = library.getName();
if (target.containsKey(name)) {
if (library.getType() != SharedLibraryInfo.TYPE_STATIC) {
// We've already added this non-version-specific library to the map.
return false;
} else if (target.get(name).indexOfKey(library.getLongVersion()) >= 0) {
// We've already added this version of a version-specific library to the map.
return false;
}
} else {
target.put(name, new LongSparseArray<>());
}
target.get(name).put(library.getLongVersion(), library);
return true;
}
@GuardedBy("mLock")
private void commitPackagesLocked(final CommitRequest request) {
// TODO: remove any expected failures from this method; this should only be able to fail due
// to unavoidable errors (I/O, etc.)
for (ReconciledPackage reconciledPkg : request.reconciledPackages.values()) {
final ScanResult scanResult = reconciledPkg.scanResult;
final ScanRequest scanRequest = scanResult.request;
final ParsedPackage parsedPackage = scanRequest.parsedPackage;
final String packageName = parsedPackage.getPackageName();
final PackageInstalledInfo res = reconciledPkg.installResult;
if (reconciledPkg.prepareResult.replace) {
AndroidPackage oldPackage = mPackages.get(packageName);
// Set the update and install times
PackageSetting deletedPkgSetting = getPackageSetting(oldPackage.getPackageName());
reconciledPkg.pkgSetting.firstInstallTime = deletedPkgSetting.firstInstallTime;
reconciledPkg.pkgSetting.lastUpdateTime = System.currentTimeMillis();
res.removedInfo.broadcastWhitelist = mAppsFilter.getVisibilityWhitelist(
reconciledPkg.pkgSetting, request.mAllUsers, mSettings.mPackages);
if (reconciledPkg.prepareResult.system) {
// Remove existing system package
removePackageLI(oldPackage, true);
if (!disableSystemPackageLPw(oldPackage)) {
// We didn't need to disable the .apk as a current system package,
// which means we are replacing another update that is already
// installed. We need to make sure to delete the older one's .apk.
res.removedInfo.args = createInstallArgsForExisting(
oldPackage.getCodePath(),
oldPackage.getCodePath(),
getAppDexInstructionSets(
AndroidPackageUtils.getPrimaryCpuAbi(oldPackage,
deletedPkgSetting),
AndroidPackageUtils.getSecondaryCpuAbi(oldPackage,
deletedPkgSetting)));
} else {
res.removedInfo.args = null;
}
} else {
try {
// Settings will be written during the call to updateSettingsLI().
executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName,
true, request.mAllUsers, false, parsedPackage);
} catch (SystemDeleteException e) {
if (Build.IS_ENG) {
throw new RuntimeException("Unexpected failure", e);
// ignore; not possible for non-system app
}
}
// Successfully deleted the old package; proceed with replace.
// If deleted package lived in a container, give users a chance to
// relinquish resources before killing.
if (oldPackage.isExternalStorage()) {
if (DEBUG_INSTALL) {
Slog.i(TAG, "upgrading pkg " + oldPackage
+ " is ASEC-hosted -> UNAVAILABLE");
}
final int[] uidArray = new int[]{oldPackage.getUid()};
final ArrayList<String> pkgList = new ArrayList<>(1);
pkgList.add(oldPackage.getPackageName());
sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
}
// Update the in-memory copy of the previous code paths.
PackageSetting ps1 = mSettings.mPackages.get(
reconciledPkg.prepareResult.existingPackage.getPackageName());
if ((reconciledPkg.installArgs.installFlags & PackageManager.DONT_KILL_APP)
== 0) {
if (ps1.mOldCodePaths == null) {
ps1.mOldCodePaths = new ArraySet<>();
}
Collections.addAll(ps1.mOldCodePaths, oldPackage.getBaseCodePath());
if (oldPackage.getSplitCodePaths() != null) {
Collections.addAll(ps1.mOldCodePaths, oldPackage.getSplitCodePaths());
}
} else {
ps1.mOldCodePaths = null;
}
if (reconciledPkg.installResult.returnCode
== PackageManager.INSTALL_SUCCEEDED) {
PackageSetting ps2 = mSettings.getPackageLPr(
parsedPackage.getPackageName());
if (ps2 != null) {
res.removedInfo.removedForAllUsers = mPackages.get(ps2.name) == null;
}
}
}
}
AndroidPackage pkg = commitReconciledScanResultLocked(reconciledPkg, request.mAllUsers);
updateSettingsLI(pkg, reconciledPkg.installArgs, request.mAllUsers, res);
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
res.newUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
ps.setUpdateAvailable(false /*updateAvailable*/);
}
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
updateSequenceNumberLP(ps, res.newUsers);
updateInstantAppInstallerLocked(packageName);
}
}
}
/**
* Installs one or more packages atomically. This operation is broken up into four phases:
* <ul>
* <li><b>Prepare</b>
* <br/>Analyzes any current install state, parses the package and does initial
* validation on it.</li>
* <li><b>Scan</b>
* <br/>Interrogates the parsed packages given the context collected in prepare.</li>
* <li><b>Reconcile</b>
* <br/>Validates scanned packages in the context of each other and the current system
* state to ensure that the install will be successful.
* <li><b>Commit</b>
* <br/>Commits all scanned packages and updates system state. This is the only place
* that system state may be modified in the install flow and all predictable errors
* must be determined before this phase.</li>
* </ul>
*
* Failure at any phase will result in a full failure to install all packages.
*/
@GuardedBy("mInstallLock")
private void installPackagesLI(List<InstallRequest> requests) {
final Map<String, ScanResult> preparedScans = new ArrayMap<>(requests.size());
final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());
final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
final Map<String, VersionInfo> versionInfos = new ArrayMap<>(requests.size());
final Map<String, PackageSetting> lastStaticSharedLibSettings =
new ArrayMap<>(requests.size());
final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
boolean success = false;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
for (InstallRequest request : requests) {
// TODO(b/109941548): remove this once we've pulled everything from it and into
// scan, reconcile or commit.
final PrepareResult prepareResult;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
prepareResult =
preparePackageLI(request.args, request.installResult);
} catch (PrepareFailure prepareFailure) {
request.installResult.setError(prepareFailure.error,
prepareFailure.getMessage());
request.installResult.origPackage = prepareFailure.conflictingPackage;
request.installResult.origPermission = prepareFailure.conflictingPermission;
return;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
request.installResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
request.installResult.installerPackageName =
request.args.installSource.installerPackageName;
final String packageName = prepareResult.packageToScan.getPackageName();
prepareResults.put(packageName, prepareResult);
installResults.put(packageName, request.installResult);
installArgs.put(packageName, request.args);
try {
final ScanResult result = scanPackageTracedLI(
prepareResult.packageToScan, prepareResult.parseFlags,
prepareResult.scanFlags, System.currentTimeMillis(),
request.args.user, request.args.abiOverride);
if (null != preparedScans.put(result.pkgSetting.pkg.getPackageName(), result)) {
request.installResult.setError(
PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
"Duplicate package " + result.pkgSetting.pkg.getPackageName()
+ " in multi-package install request.");
return;
}
createdAppId.put(packageName, optimisticallyRegisterAppId(result));
versionInfos.put(result.pkgSetting.pkg.getPackageName(),
getSettingsVersionForPackage(result.pkgSetting.pkg));
if (result.staticSharedLibraryInfo != null) {
final PackageSetting sharedLibLatestVersionSetting =
getSharedLibLatestVersionSetting(result);
if (sharedLibLatestVersionSetting != null) {
lastStaticSharedLibSettings.put(result.pkgSetting.pkg.getPackageName(),
sharedLibLatestVersionSetting);
}
}
} catch (PackageManagerException e) {
request.installResult.setError("Scanning Failed.", e);
return;
}
}
ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
installResults,
prepareResults,
mSharedLibraries,
Collections.unmodifiableMap(mPackages), versionInfos,
lastStaticSharedLibSettings);
CommitRequest commitRequest = null;
synchronized (mLock) {
Map<String, ReconciledPackage> reconciledPackages;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
reconciledPackages = reconcilePackagesLocked(
reconcileRequest, mSettings.mKeySetManagerService);
} catch (ReconcileFailure e) {
for (InstallRequest request : requests) {
request.installResult.setError("Reconciliation failed...", e);
}
return;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
commitRequest = new CommitRequest(reconciledPackages,
mUserManager.getUserIds());
commitPackagesLocked(commitRequest);
success = true;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
executePostCommitSteps(commitRequest);
} finally {
if (success) {
for (InstallRequest request : requests) {
final InstallArgs args = request.args;
if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) {
continue;
}
if (args.signingDetails.signatureSchemeVersion != SIGNING_BLOCK_V4) {
continue;
}
// For incremental installs, we bypass the verifier prior to install. Now
// that we know the package is valid, send a notice to the verifier with
// the root hash of the base.apk.
final String baseCodePath = request.installResult.pkg.getBaseCodePath();
final String[] splitCodePaths = request.installResult.pkg.getSplitCodePaths();
final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
final int verificationId = mPendingVerificationToken++;
final String rootHashString = PackageManagerServiceUtils
.buildVerificationRootHashString(baseCodePath, splitCodePaths);
broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_ALLOW, rootHashString,
args.mDataLoaderType, args.getUser());
}
} else {
for (ScanResult result : preparedScans.values()) {
if (createdAppId.getOrDefault(result.request.parsedPackage.getPackageName(),
false)) {
cleanUpAppIdCreation(result);
}
}
// TODO(patb): create a more descriptive reason than unknown in future release
// mark all non-failure installs as UNKNOWN so we do not treat them as success
for (InstallRequest request : requests) {
if (request.installResult.freezer != null) {
request.installResult.freezer.close();
}
if (request.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) {
request.installResult.returnCode = PackageManager.INSTALL_UNKNOWN;
}
}
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
/**
* On successful install, executes remaining steps after commit completes and the package lock
* is released. These are typically more expensive or require calls to installd, which often
* locks on {@link #mLock}.
*/
private void executePostCommitSteps(CommitRequest commitRequest) {
final ArraySet<IncrementalStorage> incrementalStorages = new ArraySet<>();
for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
final boolean instantApp = ((reconciledPkg.scanResult.request.scanFlags
& PackageManagerService.SCAN_AS_INSTANT_APP) != 0);
final AndroidPackage pkg = reconciledPkg.pkgSetting.pkg;
final String packageName = pkg.getPackageName();
final boolean onIncremental = mIncrementalManager != null
&& isIncrementalPath(pkg.getCodePath());
if (onIncremental) {
IncrementalStorage storage = mIncrementalManager.openStorage(pkg.getCodePath());
if (storage == null) {
throw new IllegalArgumentException(
"Install: null storage for incremental package " + packageName);
}
incrementalStorages.add(storage);
}
prepareAppDataAfterInstallLIF(pkg);
if (reconciledPkg.prepareResult.clearCodeCache) {
clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
| FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
if (reconciledPkg.prepareResult.replace) {
mDexManager.notifyPackageUpdated(pkg.getPackageName(),
pkg.getBaseCodePath(), pkg.getSplitCodePaths());
}
// Prepare the application profiles for the new code paths.
// This needs to be done before invoking dexopt so that any install-time profile
// can be used for optimizations.
mArtManagerService.prepareAppProfiles(
pkg,
resolveUserIds(reconciledPkg.installArgs.user.getIdentifier()),
/* updateReferenceProfileContent= */ true);
// Check whether we need to dexopt the app.
//
// NOTE: it is IMPORTANT to call dexopt:
// - after doRename which will sync the package data from AndroidPackage and
// its corresponding ApplicationInfo.
// - after installNewPackageLIF or replacePackageLIF which will update result with the
// uid of the application (pkg.applicationInfo.uid).
// This update happens in place!
//
// We only need to dexopt if the package meets ALL of the following conditions:
// 1) it is not an instant app or if it is then dexopt is enabled via gservices.
// 2) it is not debuggable.
// 3) it is not on Incremental File System.
//
// Note that we do not dexopt instant apps by default. dexopt can take some time to
// complete, so we skip this step during installation. Instead, we'll take extra time
// the first time the instant app starts. It's preferred to do it this way to provide
// continuous progress to the useur instead of mysteriously blocking somewhere in the
// middle of running an instant app. The default behaviour can be overridden
// via gservices.
final boolean performDexopt =
(!instantApp || Global.getInt(mContext.getContentResolver(),
Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
&& !pkg.isDebuggable()
&& (!onIncremental);
if (performDexopt) {
// Compile the layout resources.
if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");
mViewCompiler.compileLayouts(pkg);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
// Do not run PackageDexOptimizer through the local performDexOpt
// method because `pkg` may not be in `mPackages` yet.
//
// Also, don't fail application installs if the dexopt step fails.
int flags = DexoptOptions.DEXOPT_BOOT_COMPLETE
| DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
if (reconciledPkg.installArgs.installReason == INSTALL_REASON_DEVICE_RESTORE
|| reconciledPkg.installArgs.installReason == INSTALL_REASON_DEVICE_SETUP) {
flags |= DexoptOptions.DEXOPT_FOR_RESTORE;
}
DexoptOptions dexoptOptions = new DexoptOptions(packageName,
REASON_INSTALL,
flags);
ScanResult result = reconciledPkg.scanResult;
// This mirrors logic from commitReconciledScanResultLocked, where the library files
// needed for dexopt are assigned.
// TODO: Fix this to have 1 mutable PackageSetting for scan/install. If the previous
// setting needs to be passed to have a comparison, hide it behind an immutable
// interface. There's no good reason to have 3 different ways to access the real
// PackageSetting object, only one of which is actually correct.
PackageSetting realPkgSetting = result.existingSettingCopied
? result.request.pkgSetting : result.pkgSetting;
if (realPkgSetting == null) {
realPkgSetting = reconciledPkg.pkgSetting;
}
// Unfortunately, the updated system app flag is only tracked on this PackageSetting
boolean isUpdatedSystemApp = reconciledPkg.pkgSetting.getPkgState()
.isUpdatedSystemApp();
realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
null /* instructionSets */,
getOrCreateCompilerPackageStats(pkg),
mDexManager.getPackageUseInfoOrDefault(packageName),
dexoptOptions);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
// Notify BackgroundDexOptService that the package has been changed.
// If this is an update of a package which used to fail to compile,
// BackgroundDexOptService will remove it from its blacklist.
// TODO: Layering violation
BackgroundDexOptService.notifyPackageChanged(packageName);
notifyPackageChangeObserversOnUpdate(reconciledPkg);
}
NativeLibraryHelper.waitForNativeBinariesExtraction(incrementalStorages);
}
private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) {
final PackageSetting pkgSetting = reconciledPkg.pkgSetting;
final PackageInstalledInfo pkgInstalledInfo = reconciledPkg.installResult;
final PackageRemovedInfo pkgRemovedInfo = pkgInstalledInfo.removedInfo;
PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
pkgChangeEvent.packageName = pkgSetting.pkg.getPackageName();
pkgChangeEvent.version = pkgSetting.versionCode;
pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.lastUpdateTime;
pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.isUpdate);
pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.dataRemoved);
pkgChangeEvent.isDeleted = false;
notifyPackageChangeObservers(pkgChangeEvent);
}
private void notifyPackageChangeObserversOnDelete(String packageName, long version) {
PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
pkgChangeEvent.packageName = packageName;
pkgChangeEvent.version = version;
pkgChangeEvent.lastUpdateTimeMillis = 0L;
pkgChangeEvent.newInstalled = false;
pkgChangeEvent.dataRemoved = false;
pkgChangeEvent.isDeleted = true;
notifyPackageChangeObservers(pkgChangeEvent);
}
private void notifyPackageChangeObservers(PackageChangeEvent event) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "notifyPackageChangeObservers");
synchronized (mPackageChangeObservers) {
for(IPackageChangeObserver observer : mPackageChangeObservers) {
try {
observer.onPackageChanged(event);
} catch(RemoteException e) {
Log.wtf(TAG, e);
}
}
}
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
/**
* The set of data needed to successfully install the prepared package. This includes data that
* will be used to scan and reconcile the package.
*/
private static class PrepareResult {
public final boolean replace;
public final int scanFlags;
public final int parseFlags;
@Nullable /* The original Package if it is being replaced, otherwise {@code null} */
public final AndroidPackage existingPackage;
public final ParsedPackage packageToScan;
public final boolean clearCodeCache;
public final boolean system;
public final PackageSetting originalPs;
public final PackageSetting disabledPs;
private PrepareResult(boolean replace, int scanFlags,
int parseFlags, AndroidPackage existingPackage,
ParsedPackage packageToScan, boolean clearCodeCache, boolean system,
PackageSetting originalPs, PackageSetting disabledPs) {
this.replace = replace;
this.scanFlags = scanFlags;
this.parseFlags = parseFlags;
this.existingPackage = existingPackage;
this.packageToScan = packageToScan;
this.clearCodeCache = clearCodeCache;
this.system = system;
this.originalPs = originalPs;
this.disabledPs = disabledPs;
}
}
private static class PrepareFailure extends PackageManagerException {
public String conflictingPackage;
public String conflictingPermission;
PrepareFailure(int error) {
super(error, "Failed to prepare for install.");
}
PrepareFailure(int error, String detailMessage) {
super(error, detailMessage);
}
PrepareFailure(String message, Exception e) {
super(e instanceof PackageParserException
? ((PackageParserException) e).error
: ((PackageManagerException) e).error,
ExceptionUtils.getCompleteMessage(message, e));
}
PrepareFailure conflictsWithExistingPermission(String conflictingPermission,
String conflictingPackage) {
this.conflictingPermission = conflictingPermission;
this.conflictingPackage = conflictingPackage;
return this;
}
}
@GuardedBy("mInstallLock")
private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
throws PrepareFailure {
final int installFlags = args.installFlags;
final File tmpPackageFile = new File(args.getCodePath());
final boolean onExternal = args.volumeUuid != null;
final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
final boolean virtualPreload =
((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
@ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
if (args.move != null) {
// moving a complete application; perform an initial scan on the new install location
scanFlags |= SCAN_INITIAL;
}
if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
scanFlags |= SCAN_DONT_KILL_APP;
}
if (instantApp) {
scanFlags |= SCAN_AS_INSTANT_APP;
}
if (fullApp) {
scanFlags |= SCAN_AS_FULL_APP;
}
if (virtualPreload) {
scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
}
if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
// Sanity check
if (instantApp && onExternal) {
Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal);
throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
}
// Retrieve PackageSettings and parse package
@ParseFlags final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
| PackageParser.PARSE_ENFORCE_CODE
| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
ParsedPackage parsedPackage;
try (PackageParser2 pp = new PackageParser2(mSeparateProcesses, false, mMetrics, null,
mPackageParserCallback)) {
parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
} catch (PackageParserException e) {
throw new PrepareFailure("Failed parse during installPackageLI", e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
// Instant apps have several additional install-time checks.
if (instantApp) {
if (parsedPackage.getTargetSdkVersion() < Build.VERSION_CODES.O) {
Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
+ " does not target at least O");
throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
"Instant app package must target at least O");
}
if (parsedPackage.getSharedUserId() != null) {
Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
+ " may not declare sharedUserId.");
throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
"Instant app package may not declare a sharedUserId");
}
}
if (parsedPackage.isStaticSharedLibrary()) {
// Static shared libraries have synthetic package names
renameStaticSharedLibraryPackage(parsedPackage);
// No static shared libs on external storage
if (onExternal) {
Slog.i(TAG, "Static shared libs can only be installed on internal storage.");
throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Packages declaring static-shared libs cannot be updated");
}
}
String pkgName = res.name = parsedPackage.getPackageName();
if (parsedPackage.isTestOnly()) {
if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
}
}
try {
// either use what we've been given or parse directly from the APK
if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
parsedPackage.setSigningDetails(args.signingDetails);
} else {
parsedPackage.setSigningDetails(
ParsingPackageUtils.getSigningDetails(parsedPackage, false /* skipVerify */));
}
} catch (PackageParserException e) {
throw new PrepareFailure("Failed collect during installPackageLI", e);
}
if (instantApp && parsedPackage.getSigningDetails().signatureSchemeVersion
< SignatureSchemeVersion.SIGNING_BLOCK_V2) {
Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
+ " is not signed with at least APK Signature Scheme v2");
throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
"Instant app package must be signed with APK Signature Scheme v2 or greater");
}
boolean systemApp = false;
boolean replace = false;
synchronized (mLock) {
// Check if installing already existing package
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
String oldName = mSettings.getRenamedPackageLPr(pkgName);
if (parsedPackage.getOriginalPackages().contains(oldName)
&& mPackages.containsKey(oldName)) {
// This package is derived from an original package,
// and this device has been updating from that original
// name. We must continue using the original name, so
// rename the new package here.
parsedPackage.setPackageName(oldName);
pkgName = parsedPackage.getPackageName();
replace = true;
if (DEBUG_INSTALL) {
Slog.d(TAG, "Replacing existing renamed package: oldName="
+ oldName + " pkgName=" + pkgName);
}
} else if (mPackages.containsKey(pkgName)) {
// This package, under its official name, already exists
// on the device; we should replace it.
replace = true;
if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);
}
if (replace) {
// Prevent apps opting out from runtime permissions
AndroidPackage oldPackage = mPackages.get(pkgName);
final int oldTargetSdk = oldPackage.getTargetSdkVersion();
final int newTargetSdk = parsedPackage.getTargetSdkVersion();
if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
&& newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {
throw new PrepareFailure(
PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
"Package " + parsedPackage.getPackageName()
+ " new target SDK " + newTargetSdk
+ " doesn't support runtime permissions but the old"
+ " target SDK " + oldTargetSdk + " does.");
}
// Prevent persistent apps from being updated
if (oldPackage.isPersistent()
&& ((installFlags & PackageManager.INSTALL_STAGED) == 0)) {
throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,
"Package " + oldPackage.getPackageName() + " is a persistent app. "
+ "Persistent apps are not updateable.");
}
}
}
PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
// Static shared libs have same package with different versions where
// we internally use a synthetic package name to allow multiple versions
// of the same package, therefore we need to compare signatures against
// the package setting for the latest library version.
PackageSetting signatureCheckPs = ps;
if (parsedPackage.isStaticSharedLibrary()) {
SharedLibraryInfo libraryInfo = getLatestSharedLibraVersionLPr(parsedPackage);
if (libraryInfo != null) {
signatureCheckPs = mSettings.getPackageLPr(libraryInfo.getPackageName());
}
}
// Quick sanity check that we're signed correctly if updating;
// we'll check this again later when scanning, but we want to
// bail early here before tripping over redefined permissions.
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ parsedPackage.getPackageName() + " upgrade keys do not match the "
+ "previously installed version");
}
} else {
try {
final boolean compareCompat = isCompatSignatureUpdateNeeded(parsedPackage);
final boolean compareRecover = isRecoverSignatureUpdateNeeded(
parsedPackage);
// We don't care about disabledPkgSetting on install for now.
final boolean compatMatch = verifySignatures(signatureCheckPs, null,
parsedPackage.getSigningDetails(), compareCompat, compareRecover);
// The new KeySets will be re-added later in the scanning process.
if (compatMatch) {
synchronized (mLock) {
ksms.removeAppKeySetDataLPw(parsedPackage.getPackageName());
}
}
} catch (PackageManagerException e) {
throw new PrepareFailure(e.error, e.getMessage());
}
}
if (ps.pkg != null) {
systemApp = ps.pkg.isSystem();
}
res.origUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
}
int N = ArrayUtils.size(parsedPackage.getPermissions());
for (int i = N - 1; i >= 0; i--) {
final ParsedPermission perm = parsedPackage.getPermissions().get(i);
final BasePermission bp = mPermissionManager.getPermissionTEMP(perm.getName());
// Don't allow anyone but the system to define ephemeral permissions.
if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
&& !systemApp) {
Slog.w(TAG, "Non-System package " + parsedPackage.getPackageName()
+ " attempting to delcare ephemeral permission "
+ perm.getName() + "; Removing ephemeral.");
perm.setProtectionLevel(perm.getProtectionLevel() & ~PermissionInfo.PROTECTION_FLAG_INSTANT);
}
// Check whether the newly-scanned package wants to define an already-defined perm
if (bp != null) {
// If the defining package is signed with our cert, it's okay. This
// also includes the "updating the same package" case, of course.
// "updating same package" could also involve key-rotation.
final boolean sigsOk;
final String sourcePackageName = bp.getSourcePackageName();
final PackageSetting sourcePackageSetting;
synchronized (mLock) {
sourcePackageSetting = mSettings.getPackageLPr(sourcePackageName);
}
final SigningDetails sourceSigningDetails = (sourcePackageSetting == null
? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails());
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
if (sourcePackageName.equals(parsedPackage.getPackageName())
&& (ksms.shouldCheckUpgradeKeySetLocked(
sourcePackageSetting, scanFlags))) {
sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage);
} else {
// in the event of signing certificate rotation, we need to see if the
// package's certificate has rotated from the current one, or if it is an
// older certificate with which the current is ok with sharing permissions
if (sourceSigningDetails.checkCapability(
parsedPackage.getSigningDetails(),
PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
sigsOk = true;
} else if (parsedPackage.getSigningDetails().checkCapability(
sourceSigningDetails,
PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
// the scanned package checks out, has signing certificate rotation
// history, and is newer; bring it over
synchronized (mLock) {
sourcePackageSetting.signatures.mSigningDetails =
parsedPackage.getSigningDetails();
}
sigsOk = true;
} else {
sigsOk = false;
}
}
if (!sigsOk) {
// If the owning package is the system itself, we log but allow
// install to proceed; we fail the install on all other permission
// redefinitions.
if (!sourcePackageName.equals("android")) {
throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
+ parsedPackage.getPackageName()
+ " attempting to redeclare permission "
+ perm.getName() + " already owned by "
+ sourcePackageName)
.conflictsWithExistingPermission(perm.getName(),
sourcePackageName);
} else {
Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ " attempting to redeclare system permission "
+ perm.getName() + "; ignoring new declaration");
parsedPackage.removePermission(i);
}
} else if (!PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())) {
// Prevent apps to change protection level to dangerous from any other
// type as this would allow a privilege escalation where an app adds a
// normal/signature permission in other app's group and later redefines
// it as dangerous leading to the group auto-grant.
if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_DANGEROUS) {
if (bp != null && !bp.isRuntime()) {
Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ " trying to change a non-runtime permission "
+ perm.getName()
+ " to runtime; keeping old protection level");
perm.setProtectionLevel(bp.getProtectionLevel());
}
}
}
}
}
}
if (systemApp) {
if (onExternal) {
// Abort update; system app can't be replaced with app on sdcard
throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Cannot install updates to system apps on sdcard");
} else if (instantApp) {
// Abort update; system app can't be replaced with an instant app
throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
"Cannot update a system app with an instant app");
}
}
if (args.move != null) {
// We did an in-place move, so dex is ready to roll
scanFlags |= SCAN_NO_DEX;
scanFlags |= SCAN_MOVE;
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps == null) {
res.setError(INSTALL_FAILED_INTERNAL_ERROR,
"Missing settings for moved package " + pkgName);
}
// We moved the entire application as-is, so bring over the
// previously derived ABI information.
parsedPackage.setPrimaryCpuAbi(ps.primaryCpuAbiString)
.setSecondaryCpuAbi(ps.secondaryCpuAbiString);
}
} else {
// Enable SCAN_NO_DEX flag to skip dexopt at a later stage
scanFlags |= SCAN_NO_DEX;
try {
final boolean extractNativeLibs = !AndroidPackageUtils.isLibrary(parsedPackage);
PackageSetting pkgSetting;
synchronized (mLock) {
pkgSetting = mSettings.getPackageLPr(pkgName);
}
String abiOverride =
(pkgSetting == null || TextUtils.isEmpty(pkgSetting.cpuAbiOverrideString)
? args.abiOverride : pkgSetting.cpuAbiOverrideString);
boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null
&& pkgSetting.getPkgState().isUpdatedSystemApp();
AndroidPackage oldPackage = mPackages.get(pkgName);
boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
derivedAbi = mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
abiOverride, extractNativeLibs);
derivedAbi.first.applyTo(parsedPackage);
derivedAbi.second.applyTo(parsedPackage);
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
"Error deriving application ABI");
}
}
if (!args.doRename(res.returnCode, parsedPackage)) {
throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
}
try {
setUpFsVerityIfPossible(parsedPackage);
} catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) {
throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
"Failed to set up verity: " + e);
}
if (!instantApp) {
startIntentFilterVerifications(args.user.getIdentifier(), replace, parsedPackage);
} else {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);
}
}
final PackageFreezer freezer =
freezePackageForInstall(pkgName, installFlags, "installPackageLI");
boolean shouldCloseFreezerBeforeReturn = true;
try {
final AndroidPackage existingPackage;
String renamedPackage = null;
boolean sysPkg = false;
int targetScanFlags = scanFlags;
int targetParseFlags = parseFlags;
final PackageSetting ps;
final PackageSetting disabledPs;
if (replace) {
if (parsedPackage.isStaticSharedLibrary()) {
// Static libs have a synthetic package name containing the version
// and cannot be updated as an update would get a new package name,
// unless this is the exact same version code which is useful for
// development.
AndroidPackage existingPkg = mPackages.get(parsedPackage.getPackageName());
if (existingPkg != null
&& existingPkg.getLongVersionCode()
!= parsedPackage.getLongVersionCode()) {
throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PACKAGE,
"Packages declaring "
+ "static-shared libs cannot be updated");
}
}
final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
final AndroidPackage oldPackage;
final String pkgName11 = parsedPackage.getPackageName();
final int[] allUsers;
final int[] installedUsers;
final int[] uninstalledUsers;
synchronized (mLock) {
oldPackage = mPackages.get(pkgName11);
existingPackage = oldPackage;
if (DEBUG_INSTALL) {
// TODO(b/135203078): PackageImpl.toString()
Slog.d(TAG,
"replacePackageLI: new=" + parsedPackage + ", old=" + oldPackage);
}
ps = mSettings.mPackages.get(pkgName11);
disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
// verify signatures are valid
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {
if (!ksms.checkUpgradeKeySetLocked(ps, parsedPackage)) {
throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"New package not signed by keys specified by upgrade-keysets: "
+ pkgName11);
}
} else {
// default to original signature matching
if (!parsedPackage.getSigningDetails().checkCapability(
oldPackage.getSigningDetails(),
SigningDetails.CertCapabilities.INSTALLED_DATA)
&& !oldPackage.getSigningDetails().checkCapability(
parsedPackage.getSigningDetails(),
SigningDetails.CertCapabilities.ROLLBACK)) {
throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"New package has a different signature: " + pkgName11);
}
}
// don't allow a system upgrade unless the upgrade hash matches
if (oldPackage.getRestrictUpdateHash() != null && oldPackage.isSystem()) {
final byte[] digestBytes;
try {
final MessageDigest digest = MessageDigest.getInstance("SHA-512");
updateDigest(digest, new File(parsedPackage.getBaseCodePath()));
if (!ArrayUtils.isEmpty(parsedPackage.getSplitCodePaths())) {
for (String path : parsedPackage.getSplitCodePaths()) {
updateDigest(digest, new File(path));
}
}
digestBytes = digest.digest();
} catch (NoSuchAlgorithmException | IOException e) {
throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
"Could not compute hash: " + pkgName11);
}
if (!Arrays.equals(oldPackage.getRestrictUpdateHash(), digestBytes)) {
throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
"New package fails restrict-update check: " + pkgName11);
}
// retain upgrade restriction
parsedPackage.setRestrictUpdateHash(oldPackage.getRestrictUpdateHash());
}
// Check for shared user id changes
String invalidPackageName = null;
if (!Objects.equals(oldPackage.getSharedUserId(),
parsedPackage.getSharedUserId())) {
invalidPackageName = parsedPackage.getPackageName();
}
if (invalidPackageName != null) {
throw new PrepareFailure(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
"Package " + invalidPackageName + " tried to change user "
+ oldPackage.getSharedUserId());
}
// In case of rollback, remember per-user/profile install state
allUsers = mUserManager.getUserIds();
installedUsers = ps.queryInstalledUsers(allUsers, true);
uninstalledUsers = ps.queryInstalledUsers(allUsers, false);
// don't allow an upgrade from full to ephemeral
if (isInstantApp) {
if (args.user == null || args.user.getIdentifier() == UserHandle.USER_ALL) {
for (int currentUser : allUsers) {
if (!ps.getInstantApp(currentUser)) {
// can't downgrade from full to instant
Slog.w(TAG,
"Can't replace full app with instant app: " + pkgName11
+ " for user: " + currentUser);
throw new PrepareFailure(
PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
}
}
} else if (!ps.getInstantApp(args.user.getIdentifier())) {
// can't downgrade from full to instant
Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11
+ " for user: " + args.user.getIdentifier());
throw new PrepareFailure(
PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
}
}
}
// Update what is removed
res.removedInfo = new PackageRemovedInfo(this);
res.removedInfo.uid = oldPackage.getUid();
res.removedInfo.removedPackage = oldPackage.getPackageName();
res.removedInfo.installerPackageName = ps.installSource.installerPackageName;
res.removedInfo.isStaticSharedLib = parsedPackage.getStaticSharedLibName() != null;
res.removedInfo.isUpdate = true;
res.removedInfo.origUsers = installedUsers;
res.removedInfo.installReasons = new SparseArray<>(installedUsers.length);
for (int i = 0; i < installedUsers.length; i++) {
final int userId = installedUsers[i];
res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));
}
res.removedInfo.uninstallReasons = new SparseArray<>(uninstalledUsers.length);
for (int i = 0; i < uninstalledUsers.length; i++) {
final int userId = uninstalledUsers[i];
res.removedInfo.uninstallReasons.put(userId, ps.getUninstallReason(userId));
}
sysPkg = oldPackage.isSystem();
if (sysPkg) {
// Set the system/privileged/oem/vendor/product flags as needed
final boolean privileged = oldPackage.isPrivileged();
final boolean oem = oldPackage.isOem();
final boolean vendor = oldPackage.isVendor();
final boolean product = oldPackage.isProduct();
final boolean odm = oldPackage.isOdm();
final boolean systemExt = oldPackage.isSystemExt();
final @ParseFlags int systemParseFlags = parseFlags;
final @ScanFlags int systemScanFlags = scanFlags
| SCAN_AS_SYSTEM
| (privileged ? SCAN_AS_PRIVILEGED : 0)
| (oem ? SCAN_AS_OEM : 0)
| (vendor ? SCAN_AS_VENDOR : 0)
| (product ? SCAN_AS_PRODUCT : 0)
| (odm ? SCAN_AS_ODM : 0)
| (systemExt ? SCAN_AS_SYSTEM_EXT : 0);
if (DEBUG_INSTALL) {
Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage
+ ", old=" + oldPackage);
}
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
targetParseFlags = systemParseFlags;
targetScanFlags = systemScanFlags;
} else { // non system replace
replace = true;
if (DEBUG_INSTALL) {
Slog.d(TAG,
"replaceNonSystemPackageLI: new=" + parsedPackage + ", old="
+ oldPackage);
}
}
} else { // new package install
ps = null;
disabledPs = null;
replace = false;
existingPackage = null;
// Remember this for later, in case we need to rollback this install
String pkgName1 = parsedPackage.getPackageName();
if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + parsedPackage);
// TODO(patb): MOVE TO RECONCILE
synchronized (mLock) {
renamedPackage = mSettings.getRenamedPackageLPr(pkgName1);
if (renamedPackage != null) {
// A package with the same name is already installed, though
// it has been renamed to an older name. The package we
// are trying to install should be installed as an update to
// the existing one, but that has not been requested, so bail.
throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,
"Attempt to re-install " + pkgName1
+ " without first uninstalling package running as "
+ renamedPackage);
}
if (mPackages.containsKey(pkgName1)) {
// Don't allow installation over an existing package with the same name.
throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,
"Attempt to re-install " + pkgName1
+ " without first uninstalling.");
}
}
}
// we're passing the freezer back to be closed in a later phase of install
shouldCloseFreezerBeforeReturn = false;
return new PrepareResult(replace, targetScanFlags, targetParseFlags,
existingPackage, parsedPackage, replace /* clearCodeCache */, sysPkg,
ps, disabledPs);
} finally {
res.freezer = freezer;
if (shouldCloseFreezerBeforeReturn) {
freezer.close();
}
}
}
/**
* Set up fs-verity for the given package if possible. This requires a feature flag of system
* property to be enabled only if the kernel supports fs-verity.
*
* <p>When the feature flag is set to legacy mode, only APK is supported (with some experimental
* kernel patches). In normal mode, all file format can be supported.
*/
private void setUpFsVerityIfPossible(AndroidPackage pkg) throws InstallerException,
PrepareFailure, IOException, DigestException, NoSuchAlgorithmException {
final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
if (!standardMode && !legacyMode) {
return;
}
// Collect files we care for fs-verity setup.
ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
if (legacyMode) {
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName());
if (ps != null && ps.isPrivileged()) {
fsverityCandidates.put(pkg.getBaseCodePath(), null);
if (pkg.getSplitCodePaths() != null) {
for (String splitPath : pkg.getSplitCodePaths()) {
fsverityCandidates.put(splitPath, null);
}
}
}
}
} else {
// NB: These files will become only accessible if the signing key is loaded in kernel's
// .fs-verity keyring.
fsverityCandidates.put(pkg.getBaseCodePath(),
VerityUtils.getFsveritySignatureFilePath(pkg.getBaseCodePath()));
final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(
pkg.getBaseCodePath());
if (new File(dmPath).exists()) {
fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath));
}
if (pkg.getSplitCodePaths() != null) {
for (String path : pkg.getSplitCodePaths()) {
fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path));
final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path);
if (new File(splitDmPath).exists()) {
fsverityCandidates.put(splitDmPath,
VerityUtils.getFsveritySignatureFilePath(splitDmPath));
}
}
}
}
for (Map.Entry<String, String> entry : fsverityCandidates.entrySet()) {
final String filePath = entry.getKey();
final String signaturePath = entry.getValue();
if (!legacyMode) {
// fs-verity is optional for now. Only set up if signature is provided.
if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {
try {
VerityUtils.setUpFsverity(filePath, signaturePath);
} catch (IOException e) {
throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
"Failed to enable fs-verity: " + e);
}
}
continue;
}
// In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN.
final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(filePath);
if (result.isOk()) {
if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling verity to " + filePath);
final FileDescriptor fd = result.getUnownedFileDescriptor();
try {
final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath);
try {
// A file may already have fs-verity, e.g. when reused during a split
// install. If the measurement succeeds, no need to attempt to set up.
mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
} catch (InstallerException e) {
mInstaller.installApkVerity(filePath, fd, result.getContentSize());
mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
}
} finally {
IoUtils.closeQuietly(fd);
}
} else if (result.isFailed()) {
throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
"Failed to generate verity");
}
}
}
private void startIntentFilterVerifications(int userId, boolean replacing, AndroidPackage pkg) {
if (mIntentFilterVerifierComponent == null) {
Slog.w(TAG, "No IntentFilter verification will not be done as "
+ "there is no IntentFilterVerifier available!");
return;
}
final int verifierUid = getPackageUid(
mIntentFilterVerifierComponent.getPackageName(),
MATCH_DEBUG_TRIAGED_MISSING,
(userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
msg.obj = new IFVerificationParams(
pkg.getPackageName(),
pkg.isHasDomainUrls(),
pkg.getActivities(),
replacing,
userId,
verifierUid
);
mHandler.sendMessage(msg);
}
private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean replacing,
String packageName,
boolean hasDomainUrls,
List<ParsedActivity> activities) {
int size = activities.size();
if (size == 0) {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"No activity, so no need to verify any IntentFilter!");
return;
}
if (!hasDomainUrls) {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"No domain URLs, so no need to verify any IntentFilter!");
return;
}
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Checking for userId:" + userId
+ " if any IntentFilter from the " + size
+ " Activities needs verification ...");
int count = 0;
boolean handlesWebUris = false;
ArraySet<String> domains = new ArraySet<>();
final boolean previouslyVerified;
boolean hostSetExpanded = false;
boolean needToRunVerify = false;
synchronized (mLock) {
// If this is a new install and we see that we've already run verification for this
// package, we have nothing to do: it means the state was restored from backup.
IntentFilterVerificationInfo ivi =
mSettings.getIntentFilterVerificationLPr(packageName);
previouslyVerified = (ivi != null);
if (!replacing && previouslyVerified) {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.i(TAG, "Package " + packageName + " already verified: status="
+ ivi.getStatusString());
}
return;
}
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.i(TAG, " Previous verified hosts: "
+ (ivi == null ? "[none]" : ivi.getDomainsString()));
}
// If any filters need to be verified, then all need to be. In addition, we need to
// know whether an updating app has any web navigation intent filters, to re-
// examine handling policy even if not re-verifying.
final boolean needsVerification = needsNetworkVerificationLPr(packageName);
for (ParsedActivity a : activities) {
for (ParsedIntentInfo filter : a.getIntents()) {
if (filter.handlesWebUris(true)) {
handlesWebUris = true;
}
if (needsVerification && filter.needsVerification()) {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG, "autoVerify requested, processing all filters");
}
needToRunVerify = true;
// It's safe to break out here because filter.needsVerification()
// can only be true if filter.handlesWebUris(true) returned true, so
// we've already noted that.
break;
}
}
}
// Compare the new set of recognized hosts if the app is either requesting
// autoVerify or has previously used autoVerify but no longer does.
if (needToRunVerify || previouslyVerified) {
final int verificationId = mIntentFilterVerificationToken++;
for (ParsedActivity a : activities) {
for (ParsedIntentInfo filter : a.getIntents()) {
// Run verification against hosts mentioned in any web-nav intent filter,
// even if the filter matches non-web schemes as well
if (filter.handlesWebUris(false /*onlyWebSchemes*/)) {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"Verification needed for IntentFilter:" + filter.toString());
mIntentFilterVerifier.addOneIntentFilterVerification(
verifierUid, userId, verificationId, filter, packageName);
domains.addAll(filter.getHostsList());
count++;
}
}
}
}
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.i(TAG, " Update published hosts: " + domains.toString());
}
// If we've previously verified this same host set (or a subset), we can trust that
// a current ALWAYS policy is still applicable. If this is the case, we're done.
// (If we aren't in ALWAYS, we want to reverify to allow for apps that had failing
// hosts in their intent filters, then pushed a new apk that removed them and now
// passes.)
//
// Cases:
// + still autoVerify (needToRunVerify):
// - preserve current state if all of: unexpanded, in always
// - otherwise rerun as usual (fall through)
// + no longer autoVerify (alreadyVerified && !needToRunVerify)
// - wipe verification history always
// - preserve current state if all of: unexpanded, in always
hostSetExpanded = !previouslyVerified
|| (ivi != null && !ivi.getDomains().containsAll(domains));
final int currentPolicy =
mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
final boolean keepCurState = !hostSetExpanded
&& currentPolicy == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
if (needToRunVerify && keepCurState) {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.i(TAG, "Host set not expanding + ALWAYS -> no need to reverify");
}
ivi.setDomains(domains);
scheduleWriteSettingsLocked();
return;
} else if (previouslyVerified && !needToRunVerify) {
// Prior autoVerify state but not requesting it now. Clear autoVerify history,
// and preserve the always policy iff the host set is not expanding.
clearIntentFilterVerificationsLPw(packageName, userId, !keepCurState);
return;
}
}
if (needToRunVerify && count > 0) {
// app requested autoVerify and has at least one matching intent filter
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Starting " + count
+ " IntentFilter verification" + (count > 1 ? "s" : "")
+ " for userId:" + userId);
mIntentFilterVerifier.startVerifications(userId);
} else {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG, "No web filters or no new host policy for " + packageName);
}
}
}
@GuardedBy("mLock")
private boolean needsNetworkVerificationLPr(String packageName) {
IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr(
packageName);
if (ivi == null) {
return true;
}
int status = ivi.getStatus();
switch (status) {
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
return true;
default:
// Nothing to do
return false;
}
}
private static boolean isExternal(PackageSetting ps) {
return (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
private static boolean isSystemApp(PackageSetting ps) {
return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
private static boolean isUpdatedSystemApp(PackageSetting ps) {
return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
private VersionInfo getSettingsVersionForPackage(AndroidPackage pkg) {
if (pkg.isExternalStorage()) {
if (TextUtils.isEmpty(pkg.getVolumeUuid())) {
return mSettings.getExternalVersion();
} else {
return mSettings.findOrCreateVersion(pkg.getVolumeUuid());
}
} else {
return mSettings.getInternalVersion();
}
}
@Override
public void deletePackageAsUser(String packageName, int versionCode,
IPackageDeleteObserver observer, int userId, int flags) {
deletePackageVersioned(new VersionedPackage(packageName, versionCode),
new LegacyPackageDeleteObserver(observer).getBinder(), userId, flags);
}
@Override
public void deleteExistingPackageAsUser(VersionedPackage versionedPackage,
final IPackageDeleteObserver2 observer, final int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_PACKAGES, null);
Preconditions.checkNotNull(versionedPackage);
Preconditions.checkNotNull(observer);
final String packageName = versionedPackage.getPackageName();
final long versionCode = versionedPackage.getLongVersionCode();
int installedForUsersCount = 0;
synchronized (mLock) {
// Normalize package name to handle renamed packages and static libs
final String internalPkgName = resolveInternalPackageNameLPr(packageName, versionCode);
final PackageSetting ps = mSettings.getPackageLPr(internalPkgName);
if (ps != null) {
int[] installedUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
installedForUsersCount = installedUsers.length;
}
}
if (installedForUsersCount > 1) {
deletePackageVersionedInternal(versionedPackage, observer, userId, 0, true);
} else {
try {
observer.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_INTERNAL_ERROR,
null);
} catch (RemoteException re) {
}
}
}
@Override
public void deletePackageVersioned(VersionedPackage versionedPackage,
final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags) {
deletePackageVersionedInternal(versionedPackage, observer, userId, deleteFlags, false);
}
private void deletePackageVersionedInternal(VersionedPackage versionedPackage,
final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags,
final boolean allowSilentUninstall) {
final int callingUid = Binder.getCallingUid();
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_PACKAGES, null);
final boolean canViewInstantApps = canViewInstantApps(callingUid, userId);
Preconditions.checkNotNull(versionedPackage);
Preconditions.checkNotNull(observer);
Preconditions.checkArgumentInRange(versionedPackage.getLongVersionCode(),
PackageManager.VERSION_CODE_HIGHEST,
Long.MAX_VALUE, "versionCode must be >= -1");
final String packageName = versionedPackage.getPackageName();
final long versionCode = versionedPackage.getLongVersionCode();
final String internalPackageName;
synchronized (mLock) {
// Normalize package name to handle renamed packages and static libs
internalPackageName = resolveInternalPackageNameLPr(packageName, versionCode);
}
final int uid = Binder.getCallingUid();
if (!isOrphaned(internalPackageName)
&& !allowSilentUninstall
&& !isCallerAllowedToSilentlyUninstall(uid, internalPackageName)) {
mHandler.post(() -> {
try {
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
intent.setData(Uri.fromParts(PACKAGE_SCHEME, packageName, null));
intent.putExtra(PackageInstaller.EXTRA_CALLBACK, observer.asBinder());
observer.onUserActionRequired(intent);
} catch (RemoteException re) {
}
});
return;
}
final boolean deleteAllUsers = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0;
final int[] users = deleteAllUsers ? mUserManager.getUserIds() : new int[]{userId};
if (UserHandle.getUserId(uid) != userId || (deleteAllUsers && users.length > 1)) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"deletePackage for user " + userId);
}
if (isUserRestricted(userId, UserManager.DISALLOW_UNINSTALL_APPS)) {
mHandler.post(() -> {
try {
observer.onPackageDeleted(packageName,
PackageManager.DELETE_FAILED_USER_RESTRICTED, null);
} catch (RemoteException re) {
}
});
return;
}
if (!deleteAllUsers && getBlockUninstallForUser(internalPackageName, userId)) {
mHandler.post(() -> {
try {
observer.onPackageDeleted(packageName,
PackageManager.DELETE_FAILED_OWNER_BLOCKED, null);
} catch (RemoteException re) {
}
});
return;
}
if (DEBUG_REMOVE) {
Slog.d(TAG, "deletePackageAsUser: pkg=" + internalPackageName + " user=" + userId
+ " deleteAllUsers: " + deleteAllUsers + " version="
+ (versionCode == PackageManager.VERSION_CODE_HIGHEST
? "VERSION_CODE_HIGHEST" : versionCode));
}
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(() -> {
int returnCode;
final PackageSetting ps = mSettings.mPackages.get(internalPackageName);
boolean doDeletePackage = true;
if (ps != null) {
final boolean targetIsInstantApp =
ps.getInstantApp(UserHandle.getUserId(callingUid));
doDeletePackage = !targetIsInstantApp
|| canViewInstantApps;
}
if (doDeletePackage) {
if (!deleteAllUsers) {
returnCode = deletePackageX(internalPackageName, versionCode,
userId, deleteFlags);
} else {
int[] blockUninstallUserIds = getBlockUninstallForUsers(
internalPackageName, users);
// If nobody is blocking uninstall, proceed with delete for all users
if (ArrayUtils.isEmpty(blockUninstallUserIds)) {
returnCode = deletePackageX(internalPackageName, versionCode,
userId, deleteFlags);
} else {
// Otherwise uninstall individually for users with blockUninstalls=false
final int userFlags = deleteFlags & ~PackageManager.DELETE_ALL_USERS;
for (int userId1 : users) {
if (!ArrayUtils.contains(blockUninstallUserIds, userId1)) {
returnCode = deletePackageX(internalPackageName, versionCode,
userId1, userFlags);
if (returnCode != PackageManager.DELETE_SUCCEEDED) {
Slog.w(TAG, "Package delete failed for user " + userId1
+ ", returnCode " + returnCode);
}
}
}
// The app has only been marked uninstalled for certain users.
// We still need to report that delete was blocked
returnCode = PackageManager.DELETE_FAILED_OWNER_BLOCKED;
}
}
} else {
returnCode = PackageManager.DELETE_FAILED_INTERNAL_ERROR;
}
try {
observer.onPackageDeleted(packageName, returnCode, null);
} catch (RemoteException e) {
Log.i(TAG, "Observer no longer exists.");
} //end catch
notifyPackageChangeObserversOnDelete(packageName, versionCode);
});
}
private String resolveExternalPackageNameLPr(AndroidPackage pkg) {
if (pkg.getStaticSharedLibName() != null) {
return pkg.getManifestPackageName();
}
return pkg.getPackageName();
}
@GuardedBy("mLock")
private String resolveInternalPackageNameLPr(String packageName, long versionCode) {
final int callingUid = Binder.getCallingUid();
return resolveInternalPackageNameInternalLocked(packageName, versionCode,
callingUid);
}
private String resolveInternalPackageNameInternalLocked(
String packageName, long versionCode, int callingUid) {
// Handle renamed packages
String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
packageName = normalizedPackageName != null ? normalizedPackageName : packageName;
// Is this a static library?
LongSparseArray<SharedLibraryInfo> versionedLib =
mStaticLibsByDeclaringPackage.get(packageName);
if (versionedLib == null || versionedLib.size() <= 0) {
return packageName;
}
// Figure out which lib versions the caller can see
LongSparseLongArray versionsCallerCanSee = null;
final int callingAppId = UserHandle.getAppId(callingUid);
if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.SHELL_UID
&& callingAppId != Process.ROOT_UID) {
versionsCallerCanSee = new LongSparseLongArray();
String libName = versionedLib.valueAt(0).getName();
String[] uidPackages = getPackagesForUidInternal(callingUid, callingUid);
if (uidPackages != null) {
for (String uidPackage : uidPackages) {
PackageSetting ps = mSettings.getPackageLPr(uidPackage);
final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName);
if (libIdx >= 0) {
final long libVersion = ps.usesStaticLibrariesVersions[libIdx];
versionsCallerCanSee.append(libVersion, libVersion);
}
}
}
}
// Caller can see nothing - done
if (versionsCallerCanSee != null && versionsCallerCanSee.size() <= 0) {
return packageName;
}
// Find the version the caller can see and the app version code
SharedLibraryInfo highestVersion = null;
final int versionCount = versionedLib.size();
for (int i = 0; i < versionCount; i++) {
SharedLibraryInfo libraryInfo = versionedLib.valueAt(i);
if (versionsCallerCanSee != null && versionsCallerCanSee.indexOfKey(
libraryInfo.getLongVersion()) < 0) {
continue;
}
final long libVersionCode = libraryInfo.getDeclaringPackage().getLongVersionCode();
if (versionCode != PackageManager.VERSION_CODE_HIGHEST) {
if (libVersionCode == versionCode) {
return libraryInfo.getPackageName();
}
} else if (highestVersion == null) {
highestVersion = libraryInfo;
} else if (libVersionCode > highestVersion
.getDeclaringPackage().getLongVersionCode()) {
highestVersion = libraryInfo;
}
}
if (highestVersion != null) {
return highestVersion.getPackageName();
}
return packageName;
}
boolean isCallerVerifier(int callingUid) {
final int callingUserId = UserHandle.getUserId(callingUid);
return mRequiredVerifierPackage != null &&
callingUid == getPackageUid(mRequiredVerifierPackage, 0, callingUserId);
}
private boolean isCallerAllowedToSilentlyUninstall(int callingUid, String pkgName) {
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID
|| UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
return true;
}
final int callingUserId = UserHandle.getUserId(callingUid);
// If the caller installed the pkgName, then allow it to silently uninstall.
if (callingUid == getPackageUid(getInstallerPackageName(pkgName), 0, callingUserId)) {
return true;
}
// Allow package verifier to silently uninstall.
if (mRequiredVerifierPackage != null &&
callingUid == getPackageUid(mRequiredVerifierPackage, 0, callingUserId)) {
return true;
}
// Allow package uninstaller to silently uninstall.
if (mRequiredUninstallerPackage != null &&
callingUid == getPackageUid(mRequiredUninstallerPackage, 0, callingUserId)) {
return true;
}
// Allow storage manager to silently uninstall.
if (mStorageManagerPackage != null &&
callingUid == getPackageUid(mStorageManagerPackage, 0, callingUserId)) {
return true;
}
// Allow caller having MANAGE_PROFILE_AND_DEVICE_OWNERS permission to silently
// uninstall for device owner provisioning.
if (checkUidPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, callingUid)
== PERMISSION_GRANTED) {
return true;
}
return false;
}
private int[] getBlockUninstallForUsers(String packageName, int[] userIds) {
int[] result = EMPTY_INT_ARRAY;
for (int userId : userIds) {
if (getBlockUninstallForUser(packageName, userId)) {
result = ArrayUtils.appendInt(result, userId);
}
}
return result;
}
@Override
public boolean isPackageDeviceAdminOnAnyUser(String packageName) {
final int callingUid = Binder.getCallingUid();
if (checkUidPermission(android.Manifest.permission.MANAGE_USERS, callingUid)
!= PERMISSION_GRANTED) {
EventLog.writeEvent(0x534e4554, "128599183", -1, "");
throw new SecurityException(android.Manifest.permission.MANAGE_USERS
+ " permission is required to call this API");
}
if (getInstantAppPackageName(callingUid) != null
&& !isCallerSameApp(packageName, callingUid)) {
return false;
}
return isPackageDeviceAdmin(packageName, UserHandle.USER_ALL);
}
private boolean isPackageDeviceAdmin(String packageName, int userId) {
IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
try {
if (dpm != null) {
final ComponentName deviceOwnerComponentName = dpm.getDeviceOwnerComponent(
/* callingUserOnly =*/ false);
final String deviceOwnerPackageName = deviceOwnerComponentName == null ? null
: deviceOwnerComponentName.getPackageName();
// Does the package contains the device owner?
// TODO Do we have to do it even if userId != UserHandle.USER_ALL? Otherwise,
// this check is probably not needed, since DO should be registered as a device
// admin on some user too. (Original bug for this: b/17657954)
if (packageName.equals(deviceOwnerPackageName)) {
return true;
}
// Does it contain a device admin for any user?
int[] users;
if (userId == UserHandle.USER_ALL) {
users = mUserManager.getUserIds();
} else {
users = new int[]{userId};
}
for (int i = 0; i < users.length; ++i) {
if (dpm.packageHasActiveAdmins(packageName, users[i])) {
return true;
}
}
}
} catch (RemoteException e) {
}
return false;
}
private boolean shouldKeepUninstalledPackageLPr(String packageName) {
return mKeepUninstalledPackages != null && mKeepUninstalledPackages.contains(packageName);
}
/**
* This method is an internal method that could be get invoked either
* to delete an installed package or to clean up a failed installation.
* After deleting an installed package, a broadcast is sent to notify any
* listeners that the package has been removed. For cleaning up a failed
* installation, the broadcast is not necessary since the package's
* installation wouldn't have sent the initial broadcast either
* The key steps in deleting a package are
* deleting the package information in internal structures like mPackages,
* deleting the packages base directories through installd
* updating mSettings to reflect current status
* persisting settings for later use
* sending a broadcast if necessary
*/
int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags) {
final PackageRemovedInfo info = new PackageRemovedInfo(this);
final boolean res;
final int removeUser = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0
? UserHandle.USER_ALL : userId;
if (isPackageDeviceAdmin(packageName, removeUser)) {
Slog.w(TAG, "Not removing package " + packageName + ": has active device admin");
return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
}
final PackageSetting uninstalledPs;
final PackageSetting disabledSystemPs;
final AndroidPackage pkg;
// for the uninstall-updates case and restricted profiles, remember the per-
// user handle installed state
int[] allUsers;
/** enabled state of the uninstalled application */
final int origEnabledState;
synchronized (mLock) {
uninstalledPs = mSettings.mPackages.get(packageName);
if (uninstalledPs == null) {
Slog.w(TAG, "Not removing non-existent package " + packageName);
return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
}
if (versionCode != PackageManager.VERSION_CODE_HIGHEST
&& uninstalledPs.versionCode != versionCode) {
Slog.w(TAG, "Not removing package " + packageName + " with versionCode "
+ uninstalledPs.versionCode + " != " + versionCode);
return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
}
disabledSystemPs = mSettings.getDisabledSystemPkgLPr(packageName);
// Save the enabled state before we delete the package. When deleting a stub
// application we always set the enabled state to 'disabled'.
origEnabledState = uninstalledPs == null
? COMPONENT_ENABLED_STATE_DEFAULT : uninstalledPs.getEnabled(userId);
// Static shared libs can be declared by any package, so let us not
// allow removing a package if it provides a lib others depend on.
pkg = mPackages.get(packageName);
allUsers = mUserManager.getUserIds();
if (pkg != null && pkg.getStaticSharedLibName() != null) {
SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(
pkg.getStaticSharedLibName(), pkg.getStaticSharedLibVersion());
if (libraryInfo != null) {
for (int currUserId : allUsers) {
if (removeUser != UserHandle.USER_ALL && removeUser != currUserId) {
continue;
}
List<VersionedPackage> libClientPackages = getPackagesUsingSharedLibraryLPr(
libraryInfo, MATCH_KNOWN_PACKAGES, currUserId);
if (!ArrayUtils.isEmpty(libClientPackages)) {
Slog.w(TAG, "Not removing package " + pkg.getManifestPackageName()
+ " hosting lib " + libraryInfo.getName() + " version "
+ libraryInfo.getLongVersion() + " used by " + libClientPackages
+ " for user " + currUserId);
return PackageManager.DELETE_FAILED_USED_SHARED_LIBRARY;
}
}
}
}
info.origUsers = uninstalledPs.queryInstalledUsers(allUsers, true);
}
final int freezeUser;
if (isUpdatedSystemApp(uninstalledPs)
&& ((deleteFlags & PackageManager.DELETE_SYSTEM_APP) == 0)) {
// We're downgrading a system app, which will apply to all users, so
// freeze them all during the downgrade
freezeUser = UserHandle.USER_ALL;
} else {
freezeUser = removeUser;
}
synchronized (mInstallLock) {
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageX: pkg=" + packageName + " user=" + userId);
try (PackageFreezer freezer = freezePackageForDelete(packageName, freezeUser,
deleteFlags, "deletePackageX")) {
res = deletePackageLIF(packageName, UserHandle.of(removeUser), true, allUsers,
deleteFlags | PackageManager.DELETE_CHATTY, info, true, null);
}
synchronized (mLock) {
if (res) {
if (pkg != null) {
mInstantAppRegistry.onPackageUninstalledLPw(pkg, uninstalledPs,
info.removedUsers);
}
updateSequenceNumberLP(uninstalledPs, info.removedUsers);
updateInstantAppInstallerLocked(packageName);
}
}
}
if (res) {
final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0;
info.sendPackageRemovedBroadcasts(killApp);
info.sendSystemPackageUpdatedBroadcasts();
}
// Force a gc here.
Runtime.getRuntime().gc();
// Delete the resources here after sending the broadcast to let
// other processes clean up before deleting resources.
synchronized (mInstallLock) {
if (info.args != null) {
info.args.doPostDeleteLI(true);
}
final AndroidPackage stubPkg =
(disabledSystemPs == null) ? null : disabledSystemPs.pkg;
if (stubPkg != null && stubPkg.isStub()) {
final PackageSetting stubPs;
synchronized (mLock) {
// restore the enabled state of the stub; the state is overwritten when
// the stub is uninstalled
stubPs = mSettings.getPackageLPr(stubPkg.getPackageName());
if (stubPs != null) {
stubPs.setEnabled(origEnabledState, userId, "android");
}
}
if (origEnabledState == COMPONENT_ENABLED_STATE_DEFAULT
|| origEnabledState == COMPONENT_ENABLED_STATE_ENABLED) {
if (DEBUG_COMPRESSION) {
Slog.i(TAG, "Enabling system stub after removal; pkg: "
+ stubPkg.getPackageName());
}
enableCompressedPackage(stubPkg, stubPs);
}
}
}
return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;
}
static class PackageRemovedInfo {
final PackageSender packageSender;
String removedPackage;
String installerPackageName;
int uid = -1;
int removedAppId = -1;
int[] origUsers;
int[] removedUsers = null;
int[] broadcastUsers = null;
int[] instantUserIds = null;
SparseArray<Integer> installReasons;
SparseArray<Integer> uninstallReasons;
boolean isRemovedPackageSystemUpdate = false;
boolean isUpdate;
boolean dataRemoved;
boolean removedForAllUsers;
boolean isStaticSharedLib;
// a two dimensional array mapping userId to the set of appIds that can receive notice
// of package changes
SparseArray<int[]> broadcastWhitelist;
// Clean up resources deleted packages.
InstallArgs args = null;
PackageRemovedInfo(PackageSender packageSender) {
this.packageSender = packageSender;
}
void sendPackageRemovedBroadcasts(boolean killApp) {
sendPackageRemovedBroadcastInternal(killApp);
}
void sendSystemPackageUpdatedBroadcasts() {
if (isRemovedPackageSystemUpdate) {
sendSystemPackageUpdatedBroadcastsInternal();
}
}
private void sendSystemPackageUpdatedBroadcastsInternal() {
Bundle extras = new Bundle(2);
extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
extras.putBoolean(Intent.EXTRA_REPLACING, true);
packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras,
0, null /*targetPackage*/, null, null, null, broadcastWhitelist);
packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, removedPackage,
extras, 0, null /*targetPackage*/, null, null, null, broadcastWhitelist);
packageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
removedPackage, null, null, null, null /* broadcastWhitelist */);
if (installerPackageName != null) {
packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
removedPackage, extras, 0 /*flags*/,
installerPackageName, null, null, null, null /* broadcastWhitelist */);
packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
removedPackage, extras, 0 /*flags*/,
installerPackageName, null, null, null, null /* broadcastWhitelist */);
}
}
private void sendPackageRemovedBroadcastInternal(boolean killApp) {
// Don't send static shared library removal broadcasts as these
// libs are visible only the the apps that depend on them an one
// cannot remove the library if it has a dependency.
if (isStaticSharedLib) {
return;
}
Bundle extras = new Bundle(2);
final int removedUid = removedAppId >= 0 ? removedAppId : uid;
extras.putInt(Intent.EXTRA_UID, removedUid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
if (isUpdate || isRemovedPackageSystemUpdate) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, removedForAllUsers);
if (removedPackage != null) {
packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
removedPackage, extras, 0, null /*targetPackage*/, null,
broadcastUsers, instantUserIds, broadcastWhitelist);
if (installerPackageName != null) {
packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
removedPackage, extras, 0 /*flags*/,
installerPackageName, null, broadcastUsers, instantUserIds, null);
}
if (dataRemoved && !isRemovedPackageSystemUpdate) {
packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null,
null, broadcastUsers, instantUserIds, broadcastWhitelist);
packageSender.notifyPackageRemoved(removedPackage, removedUid);
}
}
if (removedAppId >= 0) {
// If a system app's updates are uninstalled the UID is not actually removed. Some
// services need to know the package name affected.
if (extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
extras.putString(Intent.EXTRA_PACKAGE_NAME, removedPackage);
}
packageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED,
null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
null, null, broadcastUsers, instantUserIds, broadcastWhitelist);
}
}
void populateUsers(int[] userIds, PackageSetting deletedPackageSetting) {
removedUsers = userIds;
if (removedUsers == null) {
broadcastUsers = null;
return;
}
broadcastUsers = EMPTY_INT_ARRAY;
instantUserIds = EMPTY_INT_ARRAY;
for (int i = userIds.length - 1; i >= 0; --i) {
final int userId = userIds[i];
if (deletedPackageSetting.getInstantApp(userId)) {
instantUserIds = ArrayUtils.appendInt(instantUserIds, userId);
} else {
broadcastUsers = ArrayUtils.appendInt(broadcastUsers, userId);
}
}
}
}
/*
* This method deletes the package from internal data structures. If the DELETE_KEEP_DATA
* flag is not set, the data directory is removed as well.
* make sure this flag is set for partially installed apps. If not its meaningless to
* delete a partially installed application.
*/
private void removePackageDataLIF(final PackageSetting deletedPs, int[] allUserHandles,
PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
String packageName = deletedPs.name;
if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs);
// Retrieve object to delete permissions for shared user later on
final AndroidPackage deletedPkg = deletedPs.pkg;
if (outInfo != null) {
outInfo.removedPackage = packageName;
outInfo.installerPackageName = deletedPs.installSource.installerPackageName;
outInfo.isStaticSharedLib = deletedPkg != null
&& deletedPkg.getStaticSharedLibName() != null;
outInfo.populateUsers(deletedPs == null ? null
: deletedPs.queryInstalledUsers(mUserManager.getUserIds(), true), deletedPs);
}
removePackageLI(deletedPs.name, (flags & PackageManager.DELETE_CHATTY) != 0);
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
final AndroidPackage resolvedPkg;
if (deletedPkg != null) {
resolvedPkg = deletedPkg;
} else {
// We don't have a parsed package when it lives on an ejected
// adopted storage device, so fake something together
resolvedPkg = PackageImpl.buildFakeForDeletion(deletedPs.name,
deletedPs.volumeUuid);
}
destroyAppDataLIF(resolvedPkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
destroyAppProfilesLIF(resolvedPkg);
if (outInfo != null) {
outInfo.dataRemoved = true;
}
}
int removedAppId = -1;
// writer
boolean installedStateChanged = false;
if (deletedPs != null) {
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
final SparseBooleanArray changedUsers = new SparseBooleanArray();
synchronized (mLock) {
clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL, true);
clearDefaultBrowserIfNeeded(packageName);
mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
mAppsFilter.removePackage(getPackageSetting(packageName));
removedAppId = mSettings.removePackageLPw(packageName);
if (outInfo != null) {
outInfo.removedAppId = removedAppId;
}
mPermissionManager.updatePermissions(deletedPs.name, null);
if (deletedPs.sharedUser != null) {
// Remove permissions associated with package. Since runtime
// permissions are per user we have to kill the removed package
// or packages running under the shared user of the removed
// package if revoking the permissions requested only by the removed
// package is successful and this causes a change in gids.
boolean shouldKill = false;
for (int userId : UserManagerService.getInstance().getUserIds()) {
final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs,
userId);
shouldKill |= userIdToKill == UserHandle.USER_ALL
|| userIdToKill >= UserHandle.USER_SYSTEM;
}
// If gids changed, kill all affected packages.
if (shouldKill) {
mHandler.post(() -> {
// This has to happen with no lock held.
killApplication(deletedPs.name, deletedPs.appId,
KILL_APP_REASON_GIDS_CHANGED);
});
}
}
clearPackagePreferredActivitiesLPw(
deletedPs.name, changedUsers, UserHandle.USER_ALL);
}
if (changedUsers.size() > 0) {
updateDefaultHomeNotLocked(changedUsers);
postPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
}
}
// make sure to preserve per-user disabled state if this removal was just
// a downgrade of a system app to the factory package
if (allUserHandles != null && outInfo != null && outInfo.origUsers != null) {
if (DEBUG_REMOVE) {
Slog.d(TAG, "Propagating install state across downgrade");
}
for (int userId : allUserHandles) {
final boolean installed = ArrayUtils.contains(outInfo.origUsers, userId);
if (DEBUG_REMOVE) {
Slog.d(TAG, " user " + userId + " => " + installed);
}
if (installed != deletedPs.getInstalled(userId)) {
installedStateChanged = true;
}
deletedPs.setInstalled(installed, userId);
if (installed) {
deletedPs.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId);
}
}
}
}
synchronized (mLock) {
// can downgrade to reader
if (writeSettings) {
// Save settings now
mSettings.writeLPr();
}
if (installedStateChanged) {
mSettings.writeKernelMappingLPr(deletedPs);
}
}
if (removedAppId != -1) {
// A user ID was deleted here. Go through all users and remove it
// from KeyStore.
removeKeystoreDataIfNeeded(
mInjector.getUserManagerInternal(), UserHandle.USER_ALL, removedAppId);
}
}
private static @Nullable ScanPartition resolveApexToScanPartition(
ApexManager.ActiveApexInfo apexInfo) {
for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
ScanPartition sp = SYSTEM_PARTITIONS.get(i);
if (apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
sp.getFolder().getAbsolutePath())) {
return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX);
}
}
return null;
}
/*
* Tries to delete system package.
*/
private void deleteSystemPackageLIF(DeletePackageAction action, PackageSetting deletedPs,
int[] allUserHandles, int flags, @Nullable PackageRemovedInfo outInfo,
boolean writeSettings)
throws SystemDeleteException {
final boolean applyUserRestrictions =
(allUserHandles != null) && outInfo != null && (outInfo.origUsers != null);
final AndroidPackage deletedPkg = deletedPs.pkg;
// Confirm if the system package has been updated
// An updated system app can be deleted. This will also have to restore
// the system pkg from system partition
// reader
final PackageSetting disabledPs = action.disabledPs;
if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.getPackageName()
+ " disabledPs=" + disabledPs);
Slog.d(TAG, "Deleting system pkg from data partition");
if (DEBUG_REMOVE) {
if (applyUserRestrictions) {
Slog.d(TAG, "Remembering install states:");
for (int userId : allUserHandles) {
final boolean finstalled = ArrayUtils.contains(outInfo.origUsers, userId);
Slog.d(TAG, " u=" + userId + " inst=" + finstalled);
}
}
}
if (outInfo != null) {
// Delete the updated package
outInfo.isRemovedPackageSystemUpdate = true;
}
if (disabledPs.versionCode < deletedPs.versionCode) {
// Delete data for downgrades
flags &= ~PackageManager.DELETE_KEEP_DATA;
} else {
// Preserve data by setting flag
flags |= PackageManager.DELETE_KEEP_DATA;
}
deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
outInfo, writeSettings);
// writer
synchronized (mLock) {
// NOTE: The system package always needs to be enabled; even if it's for
// a compressed stub. If we don't, installing the system package fails
// during scan [scanning checks the disabled packages]. We will reverse
// this later, after we've "installed" the stub.
// Reinstate the old system package
enableSystemPackageLPw(disabledPs.pkg);
// Remove any native libraries from the upgraded package.
removeNativeBinariesLI(deletedPs);
}
// Install the system package
if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
try {
installPackageFromSystemLIF(disabledPs.codePathString, allUserHandles,
outInfo == null ? null : outInfo.origUsers, deletedPs.getPermissionsState(),
writeSettings);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to restore system package:" + deletedPkg.getPackageName() + ": "
+ e.getMessage());
// TODO(patb): can we avoid this; throw would come from scan...
throw new SystemDeleteException(e);
} finally {
if (disabledPs.pkg.isStub()) {
// We've re-installed the stub; make sure it's disabled here. If package was
// originally enabled, we'll install the compressed version of the application
// and re-enable it afterward.
final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.getPackageName());
if (stubPs != null) {
stubPs.setEnabled(
COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android");
}
}
}
}
/**
* Installs a package that's already on the system partition.
*/
private AndroidPackage installPackageFromSystemLIF(@NonNull String codePathString,
@Nullable int[] allUserHandles, @Nullable int[] origUserHandles,
@Nullable PermissionsState origPermissionState, boolean writeSettings)
throws PackageManagerException {
final File codePath = new File(codePathString);
@ParseFlags int parseFlags =
mDefParseFlags
| PackageParser.PARSE_MUST_BE_APK
| PackageParser.PARSE_IS_SYSTEM_DIR;
@ScanFlags int scanFlags = SCAN_AS_SYSTEM;
for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.containsFile(codePath)) {
scanFlags |= partition.scanFlag;
if (partition.containsPrivApp(codePath)) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
break;
}
}
final AndroidPackage pkg =
scanPackageTracedLI(codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
PackageSetting pkgSetting = mSettings.getPackageLPr(pkg.getPackageName());
try {
// update shared libraries for the newly re-installed system package
updateSharedLibrariesLocked(pkg, pkgSetting, null, null,
Collections.unmodifiableMap(mPackages));
} catch (PackageManagerException e) {
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
}
prepareAppDataAfterInstallLIF(pkg);
// writer
synchronized (mLock) {
PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName());
// Propagate the permissions state as we do not want to drop on the floor
// runtime permissions. The update permissions method below will take
// care of removing obsolete permissions and grant install permissions.
if (origPermissionState != null) {
ps.getPermissionsState().copyFrom(origPermissionState);
}
mPermissionManager.updatePermissions(pkg.getPackageName(), pkg);
final boolean applyUserRestrictions
= (allUserHandles != null) && (origUserHandles != null);
if (applyUserRestrictions) {
boolean installedStateChanged = false;
if (DEBUG_REMOVE) {
Slog.d(TAG, "Propagating install state across reinstall");
}
for (int userId : allUserHandles) {
final boolean installed = ArrayUtils.contains(origUserHandles, userId);
if (DEBUG_REMOVE) {
Slog.d(TAG, " user " + userId + " => " + installed);
}
if (installed != ps.getInstalled(userId)) {
installedStateChanged = true;
}
ps.setInstalled(installed, userId);
if (installed) {
ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId);
}
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}
// Regardless of writeSettings we need to ensure that this restriction
// state propagation is persisted
mSettings.writeAllUsersPackageRestrictionsLPr();
if (installedStateChanged) {
mSettings.writeKernelMappingLPr(ps);
}
}
// can downgrade to reader here
if (writeSettings) {
mSettings.writeLPr();
}
}
return pkg;
}
private void deleteInstalledPackageLIF(PackageSetting ps,
boolean deleteCodeAndResources, int flags, int[] allUserHandles,
PackageRemovedInfo outInfo, boolean writeSettings) {
synchronized (mLock) {
if (outInfo != null) {
outInfo.uid = ps.appId;
}
}
// Delete package data from internal structures and also remove data if flag is set
removePackageDataLIF(ps, allUserHandles, outInfo, flags, writeSettings);
// Delete application code and resources only for parent packages
if (deleteCodeAndResources && (outInfo != null)) {
outInfo.args = createInstallArgsForExisting(
ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(
ps.primaryCpuAbiString, ps.secondaryCpuAbiString));
if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
}
}
@Override
public boolean setBlockUninstallForUser(String packageName, boolean blockUninstall,
int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_PACKAGES, null);
// TODO (b/157774108): This should fail on non-existent packages.
synchronized (mLock) {
// Cannot block uninstall of static shared libs as they are
// considered a part of the using app (emulating static linking).
// Also static libs are installed always on internal storage.
AndroidPackage pkg = mPackages.get(packageName);
if (pkg != null && pkg.getStaticSharedLibName() != null) {
Slog.w(TAG, "Cannot block uninstall of package: " + packageName
+ " providing static shared library: " + pkg.getStaticSharedLibName());
return false;
}
mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall);
mSettings.writePackageRestrictionsLPr(userId);
}
return true;
}
@Override
public boolean getBlockUninstallForUser(String packageName, int userId) {
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null || shouldFilterApplicationLocked(ps, Binder.getCallingUid(), userId)) {
return false;
}
return mSettings.getBlockUninstallLPr(userId, packageName);
}
}
@Override
public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp) {
enforceSystemOrRoot("setRequiredForSystemUser can only be run by the system or root");
synchronized (mLock) {
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) {
Log.w(TAG, "Package doesn't exist: " + packageName);
return false;
}
if (systemUserApp) {
ps.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
} else {
ps.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
}
mSettings.writeLPr();
}
return true;
}
private static class DeletePackageAction {
public final PackageSetting deletingPs;
public final PackageSetting disabledPs;
public final PackageRemovedInfo outInfo;
public final int flags;
public final UserHandle user;
private DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs,
PackageRemovedInfo outInfo, int flags, UserHandle user) {
this.deletingPs = deletingPs;
this.disabledPs = disabledPs;
this.outInfo = outInfo;
this.flags = flags;
this.user = user;
}
}
/**
* @return a {@link DeletePackageAction} if the provided package and related state may be
* deleted, {@code null} otherwise.
*/
@Nullable
@GuardedBy("mLock")
private static DeletePackageAction mayDeletePackageLocked(
PackageRemovedInfo outInfo, PackageSetting ps, @Nullable PackageSetting disabledPs,
int flags, UserHandle user) {
if (ps == null) {
return null;
}
if (isSystemApp(ps)) {
final boolean deleteSystem = (flags & PackageManager.DELETE_SYSTEM_APP) != 0;
final boolean deleteAllUsers =
user == null || user.getIdentifier() == UserHandle.USER_ALL;
if ((!deleteSystem || deleteAllUsers) && disabledPs == null) {
Slog.w(TAG, "Attempt to delete unknown system package " + ps.pkg.getPackageName());
return null;
}
// Confirmed if the system package has been updated
// An updated system app can be deleted. This will also have to restore
// the system pkg from system partition reader
}
return new DeletePackageAction(ps, disabledPs, outInfo, flags, user);
}
/*
* This method handles package deletion in general
*/
private boolean deletePackageLIF(@NonNull String packageName, UserHandle user,
boolean deleteCodeAndResources, int[] allUserHandles, int flags,
PackageRemovedInfo outInfo, boolean writeSettings,
ParsedPackage replacingPackage) {
final DeletePackageAction action;
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
action = mayDeletePackageLocked(outInfo, ps, disabledPs, flags, user);
}
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
if (null == action) {
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: action was null");
return false;
}
try {
executeDeletePackageLIF(action, packageName, deleteCodeAndResources,
allUserHandles, writeSettings, replacingPackage);
} catch (SystemDeleteException e) {
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: system deletion failure", e);
return false;
}
return true;
}
private static class SystemDeleteException extends Exception {
public final PackageManagerException reason;
private SystemDeleteException(PackageManagerException reason) {
this.reason = reason;
}
}
/** Deletes a package. Only throws when install of a disabled package fails. */
private void executeDeletePackageLIF(DeletePackageAction action,
String packageName, boolean deleteCodeAndResources,
int[] allUserHandles, boolean writeSettings,
ParsedPackage replacingPackage) throws SystemDeleteException {
final PackageSetting ps = action.deletingPs;
final PackageRemovedInfo outInfo = action.outInfo;
final UserHandle user = action.user;
final int flags = action.flags;
final boolean systemApp = isSystemApp(ps);
// We need to get the permission state before package state is (potentially) destroyed.
final SparseBooleanArray hadSuspendAppsPermission = new SparseBooleanArray();
// allUserHandles could be null, so call mUserManager.getUserIds() directly which is cached anyway.
for (int userId : mUserManager.getUserIds()) {
hadSuspendAppsPermission.put(userId, checkPermission(Manifest.permission.SUSPEND_APPS,
packageName, userId) == PERMISSION_GRANTED);
}
final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
if ((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0)
&& userId != UserHandle.USER_ALL) {
// The caller is asking that the package only be deleted for a single
// user. To do this, we just mark its uninstalled state and delete
// its data. If this is a system app, we only allow this to happen if
// they have set the special DELETE_SYSTEM_APP which requests different
// semantics than normal for uninstalling system apps.
final boolean clearPackageStateAndReturn;
synchronized (mLock) {
markPackageUninstalledForUserLPw(ps, user);
if (!systemApp) {
// Do not uninstall the APK if an app should be cached
boolean keepUninstalledPackage = shouldKeepUninstalledPackageLPr(packageName);
if (ps.isAnyInstalled(mUserManager.getUserIds()) || keepUninstalledPackage) {
// Other users still have this package installed, so all
// we need to do is clear this user's data and save that
// it is uninstalled.
if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
clearPackageStateAndReturn = true;
} else {
// We need to set it back to 'installed' so the uninstall
// broadcasts will be sent correctly.
if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");
ps.setInstalled(true, userId);
mSettings.writeKernelMappingLPr(ps);
clearPackageStateAndReturn = false;
}
} else {
// This is a system app, so we assume that the
// other users still have this package installed, so all
// we need to do is clear this user's data and save that
// it is uninstalled.
if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
clearPackageStateAndReturn = true;
}
}
if (clearPackageStateAndReturn) {
clearPackageStateForUserLIF(ps, userId, outInfo, flags);
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(user);
}
return;
}
}
// TODO(b/109941548): break reasons for ret = false out into mayDelete method
if (systemApp) {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);
// When an updated system application is deleted we delete the existing resources
// as well and fall back to existing code in system partition
deleteSystemPackageLIF(action, ps, allUserHandles, flags, outInfo, writeSettings);
} else {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
outInfo, writeSettings);
}
// If the package removed had SUSPEND_APPS, unset any restrictions that might have been in
// place for all affected users.
int[] affectedUserIds = (outInfo != null) ? outInfo.removedUsers : null;
if (affectedUserIds == null) {
affectedUserIds = resolveUserIds(userId);
}
for (final int affectedUserId : affectedUserIds) {
if (hadSuspendAppsPermission.get(affectedUserId)) {
unsuspendForSuspendingPackage(packageName, affectedUserId);
removeAllDistractingPackageRestrictions(affectedUserId);
}
}
// Take a note whether we deleted the package for all users
if (outInfo != null) {
outInfo.removedForAllUsers = mPackages.get(ps.name) == null;
}
}
@GuardedBy("mLock")
private void markPackageUninstalledForUserLPw(PackageSetting ps, UserHandle user) {
final int[] userIds = (user == null || user.getIdentifier() == UserHandle.USER_ALL)
? mUserManager.getUserIds() : new int[] {user.getIdentifier()};
for (int nextUserId : userIds) {
if (DEBUG_REMOVE) {
Slog.d(TAG, "Marking package:" + ps.name + " uninstalled for user:" + nextUserId);
}
ps.setUserState(nextUserId, 0, COMPONENT_ENABLED_STATE_DEFAULT,
false /*installed*/,
true /*stopped*/,
true /*notLaunched*/,
false /*hidden*/,
0 /*distractionFlags*/,
false /*suspended*/,
null /*suspendParams*/,
false /*instantApp*/,
false /*virtualPreload*/,
null /*lastDisableAppCaller*/,
null /*enabledComponents*/,
null /*disabledComponents*/,
ps.readUserState(nextUserId).domainVerificationStatus,
0 /*linkGeneration*/,
PackageManager.INSTALL_REASON_UNKNOWN,
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/);
}
mSettings.writeKernelMappingLPr(ps);
}
private void clearPackageStateForUserLIF(PackageSetting ps, int userId,
PackageRemovedInfo outInfo, int flags) {
final AndroidPackage pkg;
synchronized (mLock) {
pkg = mPackages.get(ps.name);
}
destroyAppProfilesLIF(pkg);
final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds()
: new int[] {userId};
for (int nextUserId : userIds) {
if (DEBUG_REMOVE) {
Slog.d(TAG, "Updating package:" + ps.name + " install state for user:"
+ nextUserId);
}
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
destroyAppDataLIF(pkg, nextUserId,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
}
clearDefaultBrowserIfNeededForUser(ps.name, nextUserId);
removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), nextUserId, ps.appId);
clearPackagePreferredActivities(ps.name, nextUserId);
mPermissionManager.resetRuntimePermissions(pkg, nextUserId);
}
if (outInfo != null) {
outInfo.removedPackage = ps.name;
outInfo.installerPackageName = ps.installSource.installerPackageName;
outInfo.isStaticSharedLib = pkg != null && pkg.getStaticSharedLibName() != null;
outInfo.removedAppId = ps.appId;
outInfo.removedUsers = userIds;
outInfo.broadcastUsers = userIds;
}
}
@Override
public void clearApplicationProfileData(String packageName) {
enforceSystemOrRoot("Only the system can clear all profile data");
final AndroidPackage pkg;
synchronized (mLock) {
pkg = mPackages.get(packageName);
}
try (PackageFreezer freezer = freezePackage(packageName, "clearApplicationProfileData")) {
synchronized (mInstallLock) {
clearAppProfilesLIF(pkg, UserHandle.USER_ALL);
}
}
}
@Override
public void clearApplicationUserData(final String packageName,
final IPackageDataObserver observer, final int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CLEAR_APP_USER_DATA, null);
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */, "clear application data");
final boolean filterApp;
synchronized (mLock) {
final PackageSetting ps = mSettings.getPackageLPr(packageName);
filterApp = shouldFilterApplicationLocked(ps, callingUid, userId);
}
if (!filterApp && mProtectedPackages.isPackageDataProtected(userId, packageName)) {
throw new SecurityException("Cannot clear data for a protected package: "
+ packageName);
}
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
final boolean succeeded;
if (!filterApp) {
try (PackageFreezer freezer = freezePackage(packageName,
"clearApplicationUserData")) {
synchronized (mInstallLock) {
succeeded = clearApplicationUserDataLIF(packageName, userId);
}
synchronized (mLock) {
mInstantAppRegistry.deleteInstantApplicationMetadataLPw(
packageName, userId);
}
}
if (succeeded) {
// invoke DeviceStorageMonitor's update method to clear any notifications
DeviceStorageMonitorInternal dsm = LocalServices
.getService(DeviceStorageMonitorInternal.class);
if (dsm != null) {
dsm.checkMemory();
}
if (checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
== PERMISSION_GRANTED) {
unsuspendForSuspendingPackage(packageName, userId);
removeAllDistractingPackageRestrictions(userId);
flushPackageRestrictionsAsUserInternalLocked(userId);
}
}
} else {
succeeded = false;
}
if (observer != null) {
try {
observer.onRemoveCompleted(packageName, succeeded);
} catch (RemoteException e) {
Log.i(TAG, "Observer no longer exists.");
}
} //end if observer
} //end run
});
}
private boolean clearApplicationUserDataLIF(String packageName, int userId) {
if (packageName == null) {
Slog.w(TAG, "Attempt to delete null packageName.");
return false;
}
// Try finding details about the requested package
AndroidPackage pkg;
PackageSetting ps;
synchronized (mLock) {
pkg = mPackages.get(packageName);
ps = mSettings.mPackages.get(packageName);
if (pkg == null) {
if (ps != null) {
pkg = ps.pkg;
}
}
}
if (pkg == null) {
Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
return false;
}
mPermissionManager.resetRuntimePermissions(pkg, userId);
clearAppDataLIF(pkg, userId,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
final int appId = UserHandle.getAppId(pkg.getUid());
removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), userId, appId);
UserManagerInternal umInternal = mInjector.getUserManagerInternal();
final int flags;
if (umInternal.isUserUnlockingOrUnlocked(userId)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(userId)) {
flags = StorageManager.FLAG_STORAGE_DE;
} else {
flags = 0;
}
prepareAppDataContentsLIF(pkg, ps, userId, flags);
return true;
}
private void resetNetworkPolicies(int userId) {
mInjector.getNetworkPolicyManagerInternal().resetUserState(userId);
}
/**
* Remove entries from the keystore daemon. Will only remove it if the
* {@code appId} is valid.
*/
private static void removeKeystoreDataIfNeeded(UserManagerInternal um, @UserIdInt int userId,
@AppIdInt int appId) {
if (appId < 0) {
return;
}
final KeyStore keyStore = KeyStore.getInstance();
if (keyStore != null) {
if (userId == UserHandle.USER_ALL) {
for (final int individual : um.getUserIds()) {
keyStore.clearUid(UserHandle.getUid(individual, appId));
}
} else {
keyStore.clearUid(UserHandle.getUid(userId, appId));
}
} else {
Slog.w(TAG, "Could not contact keystore to clear entries for app id " + appId);
}
}
@Override
public void deleteApplicationCacheFiles(final String packageName,
final IPackageDataObserver observer) {
final int userId = UserHandle.getCallingUserId();
deleteApplicationCacheFilesAsUser(packageName, userId, observer);
}
@Override
public void deleteApplicationCacheFilesAsUser(final String packageName, final int userId,
final IPackageDataObserver observer) {
final int callingUid = Binder.getCallingUid();
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES)
!= PackageManager.PERMISSION_GRANTED) {
// If the caller has the old delete cache permission, silently ignore. Else throw.
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.DELETE_CACHE_FILES)
== PackageManager.PERMISSION_GRANTED) {
Slog.w(TAG, "Calling uid " + callingUid + " does not have " +
android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES +
", silently ignoring");
return;
}
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES, null);
}
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
/* requireFullPermission= */ true, /* checkShell= */ false,
"delete application cache files");
final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.ACCESS_INSTANT_APPS);
final AndroidPackage pkg;
synchronized (mLock) {
pkg = mPackages.get(packageName);
}
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(() -> {
final PackageSetting ps = pkg == null ? null : getPackageSetting(pkg.getPackageName());
boolean doClearData = true;
if (ps != null) {
final boolean targetIsInstantApp =
ps.getInstantApp(UserHandle.getUserId(callingUid));
doClearData = !targetIsInstantApp
|| hasAccessInstantApps == PackageManager.PERMISSION_GRANTED;
}
if (doClearData) {
synchronized (mInstallLock) {
final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL;
// We're only clearing cache files, so we don't care if the
// app is unfrozen and still able to run
clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY);
clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
}
if (observer != null) {
try {
observer.onRemoveCompleted(packageName, true);
} catch (RemoteException e) {
Log.i(TAG, "Observer no longer exists.");
}
}
});
}
@Override
public void getPackageSizeInfo(final String packageName, int userId,
final IPackageStatsObserver observer) {
throw new UnsupportedOperationException(
"Shame on you for calling the hidden API getPackageSizeInfo(). Shame!");
}
@GuardedBy("mInstallLock")
private boolean getPackageSizeInfoLI(String packageName, int userId, PackageStats stats) {
final PackageSetting ps;
synchronized (mLock) {
ps = mSettings.mPackages.get(packageName);
if (ps == null) {
Slog.w(TAG, "Failed to find settings for " + packageName);
return false;
}
}
final String[] packageNames = { packageName };
final long[] ceDataInodes = { ps.getCeDataInode(userId) };
final String[] codePaths = { ps.codePathString };
try {
mInstaller.getAppSize(ps.volumeUuid, packageNames, userId, 0,
ps.appId, ceDataInodes, codePaths, stats);
// For now, ignore code size of packages on system partition
if (isSystemApp(ps) && !isUpdatedSystemApp(ps)) {
stats.codeSize = 0;
}
// External clients expect these to be tracked separately
stats.dataSize -= stats.cacheSize;
} catch (InstallerException e) {
Slog.w(TAG, String.valueOf(e));
return false;
}
return true;
}
@GuardedBy("mLock")
private int getUidTargetSdkVersionLockedLPr(int uid) {
final int appId = UserHandle.getAppId(uid);
final Object obj = mSettings.getSettingLPr(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
int vers = Build.VERSION_CODES.CUR_DEVELOPMENT;
final Iterator<PackageSetting> it = sus.packages.iterator();
while (it.hasNext()) {
final PackageSetting ps = it.next();
if (ps.pkg != null) {
int v = ps.pkg.getTargetSdkVersion();
if (v < vers) vers = v;
}
}
return vers;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (ps.pkg != null) {
return ps.pkg.getTargetSdkVersion();
}
}
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
@GuardedBy("mLock")
private int getPackageTargetSdkVersionLockedLPr(String packageName) {
final AndroidPackage p = mPackages.get(packageName);
if (p != null) {
return p.getTargetSdkVersion();
}
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
@Override
public void addPreferredActivity(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity, int userId) {
addPreferredActivityInternal(filter, match, set, activity, true, userId,
"Adding preferred");
}
private void addPreferredActivityInternal(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity, boolean always, int userId,
String opname) {
// writer
int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */, "add preferred activity");
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
synchronized (mLock) {
if (getUidTargetSdkVersionLockedLPr(callingUid)
< Build.VERSION_CODES.FROYO) {
Slog.w(TAG, "Ignoring addPreferredActivity() from uid "
+ callingUid);
return;
}
}
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
}
if (filter.countActions() == 0) {
Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
return;
}
if (DEBUG_PREFERRED) {
Slog.i(TAG, opname + " activity " + activity.flattenToShortString() + " for user "
+ userId + ":");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
}
synchronized (mLock) {
final PreferredIntentResolver pir = mSettings.editPreferredActivitiesLPw(userId);
pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
scheduleWritePackageRestrictionsLocked(userId);
}
if (!updateDefaultHomeNotLocked(userId)) {
postPreferredActivityChangedBroadcast(userId);
}
}
private void postPreferredActivityChangedBroadcast(int userId) {
mHandler.post(() -> {
final IActivityManager am = ActivityManager.getService();
if (am == null) {
return;
}
final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
try {
am.broadcastIntentWithFeature(null, null, intent, null, null,
0, null, null, null, android.app.AppOpsManager.OP_NONE,
null, false, false, userId);
} catch (RemoteException e) {
}
});
}
@Override
public void replacePreferredActivity(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity, int userId) {
if (filter.countActions() != 1) {
throw new IllegalArgumentException(
"replacePreferredActivity expects filter to have only 1 action.");
}
if (filter.countDataAuthorities() != 0
|| filter.countDataPaths() != 0
|| filter.countDataSchemes() > 1
|| filter.countDataTypes() != 0) {
throw new IllegalArgumentException(
"replacePreferredActivity expects filter to have no data authorities, " +
"paths, or types; and at most one scheme.");
}
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"replace preferred activity");
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
synchronized (mLock) {
if (getUidTargetSdkVersionLockedLPr(callingUid)
< Build.VERSION_CODES.FROYO) {
Slog.w(TAG, "Ignoring replacePreferredActivity() from uid "
+ Binder.getCallingUid());
return;
}
}
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
}
synchronized (mLock) {
final PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
if (pir != null) {
// Get all of the existing entries that exactly match this filter.
final ArrayList<PreferredActivity> existing = pir.findFilters(filter);
if (existing != null && existing.size() == 1) {
final PreferredActivity cur = existing.get(0);
if (DEBUG_PREFERRED) {
Slog.i(TAG, "Checking replace of preferred:");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
if (!cur.mPref.mAlways) {
Slog.i(TAG, " -- CUR; not mAlways!");
} else {
Slog.i(TAG, " -- CUR: mMatch=" + cur.mPref.mMatch);
Slog.i(TAG, " -- CUR: mSet="
+ Arrays.toString(cur.mPref.mSetComponents));
Slog.i(TAG, " -- CUR: mComponent=" + cur.mPref.mShortComponent);
Slog.i(TAG, " -- NEW: mMatch="
+ (match&IntentFilter.MATCH_CATEGORY_MASK));
Slog.i(TAG, " -- CUR: mSet=" + Arrays.toString(set));
Slog.i(TAG, " -- CUR: mComponent=" + activity.flattenToShortString());
}
}
if (cur.mPref.mAlways && cur.mPref.mComponent.equals(activity)
&& cur.mPref.mMatch == (match&IntentFilter.MATCH_CATEGORY_MASK)
&& cur.mPref.sameSet(set)) {
// Setting the preferred activity to what it happens to be already
if (DEBUG_PREFERRED) {
Slog.i(TAG, "Replacing with same preferred activity "
+ cur.mPref.mShortComponent + " for user "
+ userId + ":");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
}
return;
}
}
if (existing != null) {
if (DEBUG_PREFERRED) {
Slog.i(TAG, existing.size() + " existing preferred matches for:");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
}
for (int i = existing.size() - 1; i >= 0; --i) {
final PreferredActivity pa = existing.get(i);
if (DEBUG_PREFERRED) {
Slog.i(TAG, "Removing existing preferred activity "
+ pa.mPref.mComponent + ":");
pa.dump(new LogPrinter(Log.INFO, TAG), " ");
}
pir.removeFilter(pa);
}
}
}
}
addPreferredActivityInternal(filter, match, set, activity, true, userId,
"Replacing preferred");
}
@Override
public void clearPackagePreferredActivities(String packageName) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return;
}
// writer
synchronized (mLock) {
AndroidPackage pkg = mPackages.get(packageName);
if (pkg == null || !isCallerSameApp(packageName, callingUid)) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
if (getUidTargetSdkVersionLockedLPr(callingUid)
< Build.VERSION_CODES.FROYO) {
Slog.w(TAG, "Ignoring clearPackagePreferredActivities() from uid "
+ callingUid);
return;
}
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
}
}
final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null
&& shouldFilterApplicationLocked(
ps, callingUid, UserHandle.getUserId(callingUid))) {
return;
}
}
int callingUserId = UserHandle.getCallingUserId();
clearPackagePreferredActivities(packageName, callingUserId);
}
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
private void clearPackagePreferredActivities(String packageName, int userId) {
final SparseBooleanArray changedUsers = new SparseBooleanArray();
synchronized (mLock) {
clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId);
}
if (changedUsers.size() > 0) {
updateDefaultHomeNotLocked(changedUsers);
postPreferredActivityChangedBroadcast(userId);
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(userId);
}
}
}
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
@GuardedBy("mLock")
private void clearPackagePreferredActivitiesLPw(String packageName,
@NonNull SparseBooleanArray outUserChanged, int userId) {
ArrayList<PreferredActivity> removed = null;
for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
final int thisUserId = mSettings.mPreferredActivities.keyAt(i);
PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
if (userId != UserHandle.USER_ALL && userId != thisUserId) {
continue;
}
Iterator<PreferredActivity> it = pir.filterIterator();
while (it.hasNext()) {
PreferredActivity pa = it.next();
// Mark entry for removal only if it matches the package name
// and the entry is of type "always".
if (packageName == null ||
(pa.mPref.mComponent.getPackageName().equals(packageName)
&& pa.mPref.mAlways)) {
if (removed == null) {
removed = new ArrayList<>();
}
removed.add(pa);
}
}
if (removed != null) {
for (int j=0; j<removed.size(); j++) {
PreferredActivity pa = removed.get(j);
pir.removeFilter(pa);
}
outUserChanged.put(thisUserId, true);
}
}
}
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
@GuardedBy("mLock")
private void clearIntentFilterVerificationsLPw(int userId) {
final int packageCount = mPackages.size();
for (int i = 0; i < packageCount; i++) {
AndroidPackage pkg = mPackages.valueAt(i);
clearIntentFilterVerificationsLPw(pkg.getPackageName(), userId, true);
}
}
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
@GuardedBy("mLock")
void clearIntentFilterVerificationsLPw(String packageName, int userId,
boolean alsoResetStatus) {
if (userId == UserHandle.USER_ALL) {
if (mSettings.removeIntentFilterVerificationLPw(packageName,
mUserManager.getUserIds())) {
for (int oneUserId : mUserManager.getUserIds()) {
scheduleWritePackageRestrictionsLocked(oneUserId);
}
}
} else {
if (mSettings.removeIntentFilterVerificationLPw(packageName, userId,
alsoResetStatus)) {
scheduleWritePackageRestrictionsLocked(userId);
}
}
}
/** Clears state for all users, and touches intent filter verification policy */
void clearDefaultBrowserIfNeeded(String packageName) {
for (int oneUserId : mUserManager.getUserIds()) {
clearDefaultBrowserIfNeededForUser(packageName, oneUserId);
}
}
private void clearDefaultBrowserIfNeededForUser(String packageName, int userId) {
final String defaultBrowserPackageName = mPermissionManager.getDefaultBrowser(userId);
if (!TextUtils.isEmpty(defaultBrowserPackageName)) {
if (packageName.equals(defaultBrowserPackageName)) {
mPermissionManager.setDefaultBrowser(null, true, true, userId);
}
}
}
private void clearRolesAndRestorePermissionsForNewUserInstall(String packageName,
int installReason, @UserIdInt int userId) {
// If this app is a browser and it's newly-installed for some
// users, clear any default-browser state in those users. The
// app's nature doesn't depend on the user, so we can just check
// its browser nature in any user and generalize.
if (packageIsBrowser(packageName, userId)) {
// If this browser is restored from user's backup, do not clear
// default-browser state for this user
if (installReason != PackageManager.INSTALL_REASON_DEVICE_RESTORE) {
mPermissionManager.setDefaultBrowser(null, true, true, userId);
}
}
// We may also need to apply pending (restored) runtime permission grants
// within these users.
mPermissionManager.restoreDelayedRuntimePermissions(packageName,
UserHandle.of(userId));
// Persistent preferred activity might have came into effect due to this
// install.
updateDefaultHomeNotLocked(userId);
}
@Override
public void resetApplicationPreferences(int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
final long identity = Binder.clearCallingIdentity();
// writer
try {
final SparseBooleanArray changedUsers = new SparseBooleanArray();
synchronized (mLock) {
clearPackagePreferredActivitiesLPw(null, changedUsers, userId);
}
if (changedUsers.size() > 0) {
postPreferredActivityChangedBroadcast(userId);
}
synchronized (mLock) {
mSettings.applyDefaultPreferredAppsLPw(userId);
clearIntentFilterVerificationsLPw(userId);
primeDomainVerificationsLPw(userId);
}
mPermissionManager.resetAllRuntimePermissions(userId);
updateDefaultHomeNotLocked(userId);
// TODO: We have to reset the default SMS and Phone. This requires
// significant refactoring to keep all default apps in the package
// manager (cleaner but more work) or have the services provide
// callbacks to the package manager to request a default app reset.
mPermissionManager.setDefaultBrowser(null, true, true, userId);
resetNetworkPolicies(userId);
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(userId);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public int getPreferredActivities(List<IntentFilter> outFilters,
List<ComponentName> outActivities, String packageName) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return 0;
}
int num = 0;
final int userId = UserHandle.getCallingUserId();
// reader
synchronized (mLock) {
PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
if (pir != null) {
final Iterator<PreferredActivity> it = pir.filterIterator();
while (it.hasNext()) {
final PreferredActivity pa = it.next();
if (packageName == null
|| (pa.mPref.mComponent.getPackageName().equals(packageName)
&& pa.mPref.mAlways)) {
if (outFilters != null) {
outFilters.add(new IntentFilter(pa));
}
if (outActivities != null) {
outActivities.add(pa.mPref.mComponent);
}
}
}
}
}
return num;
}
@Override
public void addPersistentPreferredActivity(IntentFilter filter, ComponentName activity,
int userId) {
int callingUid = Binder.getCallingUid();
if (callingUid != Process.SYSTEM_UID) {
throw new SecurityException(
"addPersistentPreferredActivity can only be run by the system");
}
if (filter.countActions() == 0) {
Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
return;
}
if (DEBUG_PREFERRED) {
Slog.i(TAG, "Adding persistent preferred activity " + activity
+ " for user " + userId + ":");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
}
synchronized (mLock) {
mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
new PersistentPreferredActivity(filter, activity, true));
scheduleWritePackageRestrictionsLocked(userId);
}
updateDefaultHomeNotLocked(userId);
postPreferredActivityChangedBroadcast(userId);
}
@Override
public void clearPackagePersistentPreferredActivities(String packageName, int userId) {
int callingUid = Binder.getCallingUid();
if (callingUid != Process.SYSTEM_UID) {
throw new SecurityException(
"clearPackagePersistentPreferredActivities can only be run by the system");
}
ArrayList<PersistentPreferredActivity> removed = null;
boolean changed = false;
synchronized (mLock) {
for (int i=0; i<mSettings.mPersistentPreferredActivities.size(); i++) {
final int thisUserId = mSettings.mPersistentPreferredActivities.keyAt(i);
PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities
.valueAt(i);
if (userId != thisUserId) {
continue;
}
Iterator<PersistentPreferredActivity> it = ppir.filterIterator();
while (it.hasNext()) {
PersistentPreferredActivity ppa = it.next();
// Mark entry for removal only if it matches the package name.
if (ppa.mComponent.getPackageName().equals(packageName)) {
if (removed == null) {
removed = new ArrayList<>();
}
removed.add(ppa);
}
}
if (removed != null) {
for (int j=0; j<removed.size(); j++) {
PersistentPreferredActivity ppa = removed.get(j);
ppir.removeFilter(ppa);
}
changed = true;
}
}
}
if (changed) {
updateDefaultHomeNotLocked(userId);
postPreferredActivityChangedBroadcast(userId);
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(userId);
}
}
}
/**
* Common machinery for picking apart a restored XML blob and passing
* it to a caller-supplied functor to be applied to the running system.
*/
private void restoreFromXml(XmlPullParser parser, int userId,
String expectedStartTag, BlobXmlRestorer functor)
throws IOException, XmlPullParserException {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
}
if (type != XmlPullParser.START_TAG) {
// oops didn't find a start tag?!
if (DEBUG_BACKUP) {
Slog.e(TAG, "Didn't find start tag during restore");
}
return;
}
// this is supposed to be TAG_PREFERRED_BACKUP
if (!expectedStartTag.equals(parser.getName())) {
if (DEBUG_BACKUP) {
Slog.e(TAG, "Found unexpected tag " + parser.getName());
}
return;
}
// skip interfering stuff, then we're aligned with the backing implementation
while ((type = parser.next()) == XmlPullParser.TEXT) { }
functor.apply(parser, userId);
}
private interface BlobXmlRestorer {
void apply(XmlPullParser parser, int userId) throws IOException, XmlPullParserException;
}
/**
* Non-Binder method, support for the backup/restore mechanism: write the
* full set of preferred activities in its canonical XML format. Returns the
* XML output as a byte array, or null if there is none.
*/
@Override
public byte[] getPreferredActivityBackup(int userId) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Only the system may call getPreferredActivityBackup()");
}
ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
try {
final XmlSerializer serializer = new FastXmlSerializer();
serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
serializer.startDocument(null, true);
serializer.startTag(null, TAG_PREFERRED_BACKUP);
synchronized (mLock) {
mSettings.writePreferredActivitiesLPr(serializer, userId, true);
}
serializer.endTag(null, TAG_PREFERRED_BACKUP);
serializer.endDocument();
serializer.flush();
} catch (Exception e) {
if (DEBUG_BACKUP) {
Slog.e(TAG, "Unable to write preferred activities for backup", e);
}
return null;
}
return dataStream.toByteArray();
}
@Override
public void restorePreferredActivities(byte[] backup, int userId) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Only the system may call restorePreferredActivities()");
}
try {
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
restoreFromXml(parser, userId, TAG_PREFERRED_BACKUP,
(readParser, readUserId) -> {
synchronized (mLock) {
mSettings.readPreferredActivitiesLPw(readParser, readUserId);
}
updateDefaultHomeNotLocked(readUserId);
});
} catch (Exception e) {
if (DEBUG_BACKUP) {
Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
}
}
}
/**
* Non-Binder method, support for the backup/restore mechanism: write the
* default browser (etc) settings in its canonical XML format. Returns the default
* browser XML representation as a byte array, or null if there is none.
*/
@Override
public byte[] getDefaultAppsBackup(int userId) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Only the system may call getDefaultAppsBackup()");
}
ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
try {
final XmlSerializer serializer = new FastXmlSerializer();
serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
serializer.startDocument(null, true);
serializer.startTag(null, TAG_DEFAULT_APPS);
synchronized (mLock) {
mSettings.writeDefaultAppsLPr(serializer, userId);
}
serializer.endTag(null, TAG_DEFAULT_APPS);
serializer.endDocument();
serializer.flush();
} catch (Exception e) {
if (DEBUG_BACKUP) {
Slog.e(TAG, "Unable to write default apps for backup", e);
}
return null;
}
return dataStream.toByteArray();
}
@Override
public void restoreDefaultApps(byte[] backup, int userId) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Only the system may call restoreDefaultApps()");
}
try {
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
restoreFromXml(parser, userId, TAG_DEFAULT_APPS,
(parser1, userId1) -> {
final String defaultBrowser;
synchronized (mLock) {
mSettings.readDefaultAppsLPw(parser1, userId1);
defaultBrowser = mSettings.removeDefaultBrowserPackageNameLPw(userId1);
}
if (defaultBrowser != null) {
mPermissionManager
.setDefaultBrowser(defaultBrowser, false, false, userId1);
}
});
} catch (Exception e) {
if (DEBUG_BACKUP) {
Slog.e(TAG, "Exception restoring default apps: " + e.getMessage());
}
}
}
@Override
public byte[] getIntentFilterVerificationBackup(int userId) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Only the system may call getIntentFilterVerificationBackup()");
}
ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
try {
final XmlSerializer serializer = new FastXmlSerializer();
serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
serializer.startDocument(null, true);
serializer.startTag(null, TAG_INTENT_FILTER_VERIFICATION);
synchronized (mLock) {
mSettings.writeAllDomainVerificationsLPr(serializer, userId);
}
serializer.endTag(null, TAG_INTENT_FILTER_VERIFICATION);
serializer.endDocument();
serializer.flush();
} catch (Exception e) {
if (DEBUG_BACKUP) {
Slog.e(TAG, "Unable to write default apps for backup", e);
}
return null;
}
return dataStream.toByteArray();
}
@Override
public void restoreIntentFilterVerification(byte[] backup, int userId) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Only the system may call restorePreferredActivities()");
}
try {
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
restoreFromXml(parser, userId, TAG_INTENT_FILTER_VERIFICATION,
(parser1, userId1) -> {
synchronized (mLock) {
mSettings.readAllDomainVerificationsLPr(parser1, userId1);
mSettings.writeLPr();
}
});
} catch (Exception e) {
if (DEBUG_BACKUP) {
Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
}
}
}
@Override
public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage,
int sourceUserId, int targetUserId, int flags) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
int callingUid = Binder.getCallingUid();
enforceOwnerRights(ownerPackage, callingUid);
PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
if (intentFilter.countActions() == 0) {
Slog.w(TAG, "Cannot set a crossProfile intent filter with no filter actions");
return;
}
synchronized (mLock) {
CrossProfileIntentFilter newFilter = new CrossProfileIntentFilter(intentFilter,
ownerPackage, targetUserId, flags);
CrossProfileIntentResolver resolver =
mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
ArrayList<CrossProfileIntentFilter> existing = resolver.findFilters(intentFilter);
// We have all those whose filter is equal. Now checking if the rest is equal as well.
if (existing != null) {
int size = existing.size();
for (int i = 0; i < size; i++) {
if (newFilter.equalsIgnoreFilter(existing.get(i))) {
return;
}
}
}
resolver.addFilter(newFilter);
scheduleWritePackageRestrictionsLocked(sourceUserId);
}
}
@Override
public void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
final int callingUid = Binder.getCallingUid();
enforceOwnerRights(ownerPackage, callingUid);
PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
synchronized (mLock) {
CrossProfileIntentResolver resolver =
mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
ArraySet<CrossProfileIntentFilter> set =
new ArraySet<>(resolver.filterSet());
for (CrossProfileIntentFilter filter : set) {
if (filter.getOwnerPackage().equals(ownerPackage)) {
resolver.removeFilter(filter);
}
}
scheduleWritePackageRestrictionsLocked(sourceUserId);
}
}
// Enforcing that callingUid is owning pkg on userId
private void enforceOwnerRights(String pkg, int callingUid) {
// The system owns everything.
if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
return;
}
final int callingUserId = UserHandle.getUserId(callingUid);
PackageInfo pi = getPackageInfo(pkg, 0, callingUserId);
if (pi == null) {
throw new IllegalArgumentException("Unknown package " + pkg + " on user "
+ callingUserId);
}
if (!UserHandle.isSameApp(pi.applicationInfo.uid, callingUid)) {
throw new SecurityException("Calling uid " + callingUid
+ " does not own package " + pkg);
}
}
@Override
public ComponentName getHomeActivities(List<ResolveInfo> allHomeCandidates) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
return getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getCallingUserId());
}
/**
* Send a {@code PackageInstaller.ACTION_SESSION_UPDATED} broadcast intent, containing
* the {@code sessionInfo} in the extra field {@code PackageInstaller.EXTRA_SESSION}.
*/
public void sendSessionUpdatedBroadcast(PackageInstaller.SessionInfo sessionInfo,
int userId) {
if (TextUtils.isEmpty(sessionInfo.installerPackageName)) {
return;
}
Intent sessionUpdatedIntent = new Intent(PackageInstaller.ACTION_SESSION_UPDATED)
.putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
.setPackage(sessionInfo.installerPackageName);
mContext.sendBroadcastAsUser(sessionUpdatedIntent, UserHandle.of(userId));
}
public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) {
UserManagerService ums = UserManagerService.getInstance();
if (ums != null && !sessionInfo.isStaged()) {
final UserInfo parent = ums.getProfileParent(userId);
final int launcherUid = (parent != null) ? parent.id : userId;
final ComponentName launcherComponent = getDefaultHomeActivity(launcherUid);
if (launcherComponent != null) {
Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
.putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
.setPackage(launcherComponent.getPackageName());
mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid));
}
// TODO(b/122900055) Change/Remove this and replace with new permission role.
if (mAppPredictionServicePackage != null) {
Intent predictorIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
.putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
.setPackage(mAppPredictionServicePackage);
mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUid));
}
}
}
/**
* Report the 'Home' activity which is currently set as "always use this one". If non is set
* then reports the most likely home activity or null if there are more than one.
*/
private ComponentName getDefaultHomeActivity(int userId) {
List<ResolveInfo> allHomeCandidates = new ArrayList<>();
ComponentName cn = getHomeActivitiesAsUser(allHomeCandidates, userId);
if (cn != null) {
return cn;
}
// Find the launcher with the highest priority and return that component if there are no
// other home activity with the same priority.
int lastPriority = Integer.MIN_VALUE;
ComponentName lastComponent = null;
final int size = allHomeCandidates.size();
for (int i = 0; i < size; i++) {
final ResolveInfo ri = allHomeCandidates.get(i);
if (ri.priority > lastPriority) {
lastComponent = ri.activityInfo.getComponentName();
lastPriority = ri.priority;
} else if (ri.priority == lastPriority) {
// Two components found with same priority.
lastComponent = null;
}
}
return lastComponent;
}
private Intent getHomeIntent() {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.addCategory(Intent.CATEGORY_DEFAULT);
return intent;
}
private IntentFilter getHomeFilter() {
IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
filter.addCategory(Intent.CATEGORY_HOME);
filter.addCategory(Intent.CATEGORY_DEFAULT);
return filter;
}
ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
int userId) {
Intent intent = getHomeIntent();
List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
PackageManager.GET_META_DATA, userId);
allHomeCandidates.clear();
if (resolveInfos == null) {
return null;
}
allHomeCandidates.addAll(resolveInfos);
final String packageName = mPermissionManager.getDefaultHome(userId);
if (packageName == null) {
return null;
}
int resolveInfosSize = resolveInfos.size();
for (int i = 0; i < resolveInfosSize; i++) {
ResolveInfo resolveInfo = resolveInfos.get(i);
if (resolveInfo.activityInfo != null && TextUtils.equals(
resolveInfo.activityInfo.packageName, packageName)) {
return new ComponentName(resolveInfo.activityInfo.packageName,
resolveInfo.activityInfo.name);
}
}
return null;
}
/** <b>must not hold {@link #mLock}</b> */
private void updateDefaultHomeNotLocked(SparseBooleanArray userIds) {
if (Thread.holdsLock(mLock)) {
Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
+ " is holding mLock", new Throwable());
}
for (int i = userIds.size() - 1; i >= 0; --i) {
final int userId = userIds.keyAt(i);
updateDefaultHomeNotLocked(userId);
}
}
/**
* <b>must not hold {@link #mLock}</b>
*
* @return Whether the ACTION_PREFERRED_ACTIVITY_CHANGED broadcast has been scheduled.
*/
private boolean updateDefaultHomeNotLocked(int userId) {
if (Thread.holdsLock(mLock)) {
Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
+ " is holding mLock", new Throwable());
}
if (!mSystemReady) {
// We might get called before system is ready because of package changes etc, but
// finding preferred activity depends on settings provider, so we ignore the update
// before that.
return false;
}
final Intent intent = getHomeIntent();
final List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
PackageManager.GET_META_DATA, userId);
final ResolveInfo preferredResolveInfo = findPreferredActivityNotLocked(
intent, null, 0, resolveInfos, 0, true, false, false, userId);
final String packageName = preferredResolveInfo != null
&& preferredResolveInfo.activityInfo != null
? preferredResolveInfo.activityInfo.packageName : null;
final String currentPackageName = mPermissionManager.getDefaultHome(userId);
if (TextUtils.equals(currentPackageName, packageName)) {
return false;
}
final String[] callingPackages = getPackagesForUid(Binder.getCallingUid());
if (callingPackages != null && ArrayUtils.contains(callingPackages,
mRequiredPermissionControllerPackage)) {
// PermissionController manages default home directly.
return false;
}
mPermissionManager.setDefaultHome(packageName, userId, (successful) -> {
if (successful) {
postPreferredActivityChangedBroadcast(userId);
}
});
return true;
}
@Override
public void setHomeActivity(ComponentName comp, int userId) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return;
}
ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
getHomeActivitiesAsUser(homeActivities, userId);
boolean found = false;
final int size = homeActivities.size();
final ComponentName[] set = new ComponentName[size];
for (int i = 0; i < size; i++) {
final ResolveInfo candidate = homeActivities.get(i);
final ActivityInfo info = candidate.activityInfo;
final ComponentName activityName = new ComponentName(info.packageName, info.name);
set[i] = activityName;
if (!found && activityName.equals(comp)) {
found = true;
}
}
if (!found) {
throw new IllegalArgumentException("Component " + comp + " cannot be home on user "
+ userId);
}
replacePreferredActivity(getHomeFilter(), IntentFilter.MATCH_CATEGORY_EMPTY,
set, comp, userId);
}
private @Nullable String getSetupWizardPackageNameImpl() {
final Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
| MATCH_DISABLED_COMPONENTS,
UserHandle.myUserId());
if (matches.size() == 1) {
return matches.get(0).getComponentInfo().packageName;
} else {
Slog.e(TAG, "There should probably be exactly one setup wizard; found " + matches.size()
+ ": matches=" + matches);
return null;
}
}
private @Nullable String getStorageManagerPackageName() {
final Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
| MATCH_DISABLED_COMPONENTS,
UserHandle.myUserId());
if (matches.size() == 1) {
return matches.get(0).getComponentInfo().packageName;
} else {
Slog.e(TAG, "There should probably be exactly one storage manager; found "
+ matches.size() + ": matches=" + matches);
return null;
}
}
@Override
public String getDefaultTextClassifierPackageName() {
return ensureSystemPackageName(
mContext.getString(R.string.config_servicesExtensionPackage));
}
@Override
public String getSystemTextClassifierPackageName() {
return ensureSystemPackageName(
mContext.getString(R.string.config_defaultTextClassifierPackage));
}
@Override
public @Nullable String getAttentionServicePackageName() {
final String flattenedComponentName =
mContext.getString(R.string.config_defaultAttentionService);
if (flattenedComponentName != null) {
ComponentName componentName = ComponentName.unflattenFromString(flattenedComponentName);
if (componentName != null && componentName.getPackageName() != null) {
return ensureSystemPackageName(componentName.getPackageName());
}
}
return null;
}
private @Nullable String getDocumenterPackageName() {
final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, resolvedType,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
| MATCH_DISABLED_COMPONENTS,
UserHandle.myUserId());
if (matches.size() == 1) {
return matches.get(0).getComponentInfo().packageName;
} else {
Slog.e(TAG, "There should probably be exactly one documenter; found "
+ matches.size() + ": matches=" + matches);
return null;
}
}
@Nullable
private String getDeviceConfiguratorPackageName() {
return ensureSystemPackageName(mContext.getString(
R.string.config_deviceConfiguratorPackageName));
}
@Override
public String getWellbeingPackageName() {
return ensureSystemPackageName(mContext.getString(R.string.config_defaultWellbeingPackage));
}
@Override
public String getAppPredictionServicePackageName() {
String flattenedAppPredictionServiceComponentName =
mContext.getString(R.string.config_defaultAppPredictionService);
if (flattenedAppPredictionServiceComponentName == null) {
return null;
}
ComponentName appPredictionServiceComponentName =
ComponentName.unflattenFromString(flattenedAppPredictionServiceComponentName);
if (appPredictionServiceComponentName == null) {
return null;
}
return ensureSystemPackageName(appPredictionServiceComponentName.getPackageName());
}
private @NonNull String[] dropNonSystemPackages(@NonNull String[] pkgNames) {
return emptyIfNull(filter(pkgNames, String[]::new, mIsSystemPackage), String.class);
}
private Predicate<String> mIsSystemPackage = (pkgName) -> {
if ("android".equals(pkgName)) {
return true;
}
AndroidPackage pkg = mPackages.get(pkgName);
return pkg != null && pkg.isSystem();
};
@Override
public String getSystemCaptionsServicePackageName() {
String flattenedSystemCaptionsServiceComponentName =
mContext.getString(R.string.config_defaultSystemCaptionsService);
if (TextUtils.isEmpty(flattenedSystemCaptionsServiceComponentName)) {
return null;
}
ComponentName systemCaptionsServiceComponentName =
ComponentName.unflattenFromString(flattenedSystemCaptionsServiceComponentName);
if (systemCaptionsServiceComponentName == null) {
return null;
}
return ensureSystemPackageName(systemCaptionsServiceComponentName.getPackageName());
}
@Override
public String getSetupWizardPackageName() {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Non-system caller");
}
return mPmInternal.getSetupWizardPackageName();
}
public String getIncidentReportApproverPackageName() {
return ensureSystemPackageName(mContext.getString(
R.string.config_incidentReportApproverPackage));
}
@Override
public String getContentCaptureServicePackageName() {
final String flattenedContentCaptureService =
mContext.getString(R.string.config_defaultContentCaptureService);
if (TextUtils.isEmpty(flattenedContentCaptureService)) {
return null;
}
final ComponentName contentCaptureServiceComponentName =
ComponentName.unflattenFromString(flattenedContentCaptureService);
if (contentCaptureServiceComponentName == null) {
return null;
}
return ensureSystemPackageName(contentCaptureServiceComponentName.getPackageName());
}
@Nullable
private String getRetailDemoPackageName() {
final String predefinedPkgName = mContext.getString(R.string.config_retailDemoPackage);
final String predefinedSignature = mContext.getString(
R.string.config_retailDemoPackageSignature);
if (TextUtils.isEmpty(predefinedPkgName) || TextUtils.isEmpty(predefinedSignature)) {
return null;
}
final AndroidPackage androidPkg = mPackages.get(predefinedPkgName);
if (androidPkg != null) {
final SigningDetails signingDetail = androidPkg.getSigningDetails();
if (signingDetail != null && signingDetail.signatures != null) {
try {
final MessageDigest msgDigest = MessageDigest.getInstance("SHA-256");
for (Signature signature : signingDetail.signatures) {
if (TextUtils.equals(predefinedSignature,
HexEncoding.encodeToString(msgDigest.digest(
signature.toByteArray()), false))) {
return predefinedPkgName;
}
}
} catch (NoSuchAlgorithmException e) {
Slog.e(
TAG,
"Unable to verify signatures as getting the retail demo package name",
e);
}
}
}
return null;
}
@Nullable
private String ensureSystemPackageName(@Nullable String packageName) {
if (packageName == null) {
return null;
}
long token = Binder.clearCallingIdentity();
try {
if (getPackageInfo(packageName, MATCH_FACTORY_ONLY, UserHandle.USER_SYSTEM) == null) {
PackageInfo packageInfo = getPackageInfo(packageName, 0, UserHandle.USER_SYSTEM);
if (packageInfo != null) {
EventLog.writeEvent(0x534e4554, "145981139", packageInfo.applicationInfo.uid,
"");
}
return null;
}
} finally {
Binder.restoreCallingIdentity(token);
}
return packageName;
}
@Nullable
private String[] ensureSystemPackageNames(@Nullable String[] packageNames) {
if (packageNames == null) {
return null;
}
final int packageNamesLength = packageNames.length;
for (int i = 0; i < packageNamesLength; i++) {
packageNames[i] = ensureSystemPackageName(packageNames[i]);
}
return ArrayUtils.filterNotNull(packageNames, String[]::new);
}
@Override
public void setApplicationEnabledSetting(String appPackageName,
int newState, int flags, int userId, String callingPackage) {
if (!mUserManager.exists(userId)) return;
if (callingPackage == null) {
callingPackage = Integer.toString(Binder.getCallingUid());
}
setEnabledSetting(appPackageName, null, newState, flags, userId, callingPackage);
}
@Override
public void setUpdateAvailable(String packageName, boolean updateAvailable) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
synchronized (mLock) {
final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting != null) {
pkgSetting.setUpdateAvailable(updateAvailable);
}
}
}
@Override
public void overrideLabelAndIcon(@NonNull ComponentName componentName,
@NonNull String nonLocalizedLabel, int icon, int userId) {
if (TextUtils.isEmpty(nonLocalizedLabel)) {
throw new IllegalArgumentException("Override label should be a valid String");
}
updateComponentLabelIcon(componentName, nonLocalizedLabel, icon, userId);
}
@Override
public void restoreLabelAndIcon(@NonNull ComponentName componentName, int userId) {
updateComponentLabelIcon(componentName, null, null, userId);
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
public void updateComponentLabelIcon(/*@NonNull*/ ComponentName componentName,
@Nullable String nonLocalizedLabel, @Nullable Integer icon, int userId) {
if (componentName == null) {
throw new IllegalArgumentException("Must specify a component");
}
boolean componentExists = mComponentResolver.componentExists(componentName);
if (!componentExists) {
throw new IllegalArgumentException("Component " + componentName + " not found");
}
int callingUid = Binder.getCallingUid();
String componentPkgName = componentName.getPackageName();
int componentUid = getPackageUid(componentPkgName, 0, userId);
if (!UserHandle.isSameApp(callingUid, componentUid)) {
throw new SecurityException("The calling UID (" + callingUid + ")"
+ " does not match the target UID");
}
String allowedCallerPkg = mContext.getString(R.string.config_overrideComponentUiPackage);
if (TextUtils.isEmpty(allowedCallerPkg)) {
throw new SecurityException(
"There is no package defined as allowed to change a component's label or icon");
}
int allowedCallerUid = getPackageUid(allowedCallerPkg, PackageManager.MATCH_SYSTEM_ONLY,
userId);
if (allowedCallerUid == -1 || !UserHandle.isSameApp(callingUid, allowedCallerUid)) {
throw new SecurityException("The calling UID (" + callingUid + ")"
+ " is not allowed to change a component's label or icon");
}
synchronized (mLock) {
AndroidPackage pkg = mPackages.get(componentPkgName);
PackageSetting pkgSetting = getPackageSetting(componentPkgName);
if (pkg == null || pkgSetting == null
|| (!pkg.isSystem() && !pkgSetting.getPkgState().isUpdatedSystemApp())) {
throw new SecurityException(
"Changing the label is not allowed for " + componentName);
}
if (!pkgSetting.overrideNonLocalizedLabelAndIcon(componentName, nonLocalizedLabel,
icon, userId)) {
// Nothing changed
return;
}
}
ArrayList<String> components = mPendingBroadcasts.get(userId, componentPkgName);
if (components == null) {
components = new ArrayList<>();
mPendingBroadcasts.put(userId, componentPkgName, components);
}
String className = componentName.getClassName();
if (!components.contains(className)) {
components.add(className);
}
if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, BROADCAST_DELAY);
}
}
@Override
public void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags, int userId) {
if (!mUserManager.exists(userId)) return;
setEnabledSetting(componentName.getPackageName(),
componentName.getClassName(), newState, flags, userId, null);
}
private void setEnabledSetting(final String packageName, String className, int newState,
final int flags, int userId, String callingPackage) {
if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT
|| newState == COMPONENT_ENABLED_STATE_ENABLED
|| newState == COMPONENT_ENABLED_STATE_DISABLED
|| newState == COMPONENT_ENABLED_STATE_DISABLED_USER
|| newState == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
throw new IllegalArgumentException("Invalid new component state: "
+ newState);
}
PackageSetting pkgSetting;
final int callingUid = Binder.getCallingUid();
final int permission;
if (callingUid == Process.SYSTEM_UID) {
permission = PackageManager.PERMISSION_GRANTED;
} else {
permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
}
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, true /* checkShell */, "set enabled");
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
boolean sendNow = false;
boolean isApp = (className == null);
final boolean isCallerInstantApp = (getInstantAppPackageName(callingUid) != null);
String componentName = isApp ? packageName : className;
ArrayList<String> components;
// reader
synchronized (mLock) {
pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null) {
if (!isCallerInstantApp) {
if (className == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
throw new IllegalArgumentException(
"Unknown component: " + packageName + "/" + className);
} else {
// throw SecurityException to prevent leaking package information
throw new SecurityException(
"Attempt to change component state; "
+ "pid=" + Binder.getCallingPid()
+ ", uid=" + callingUid
+ (className == null
? ", package=" + packageName
: ", component=" + packageName + "/" + className));
}
}
}
// Limit who can change which apps
if (!UserHandle.isSameApp(callingUid, pkgSetting.appId)) {
// Don't allow apps that don't have permission to modify other apps
final boolean filterApp;
synchronized (mLock) {
filterApp = (!allowedByPermission
|| shouldFilterApplicationLocked(pkgSetting, callingUid, userId));
}
if (filterApp) {
throw new SecurityException(
"Attempt to change component state; "
+ "pid=" + Binder.getCallingPid()
+ ", uid=" + callingUid
+ (className == null
? ", package=" + packageName
: ", component=" + packageName + "/" + className));
}
// Don't allow changing protected packages.
if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
throw new SecurityException("Cannot disable a protected package: " + packageName);
}
}
// Only allow apps with CHANGE_COMPONENT_ENABLED_STATE permission to change hidden
// app details activity
if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)
&& !allowedByPermission) {
throw new SecurityException("Cannot disable a system-generated component");
}
synchronized (mLock) {
if (callingUid == Process.SHELL_UID
&& (pkgSetting.pkgFlags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
// Shell can only change whole packages between ENABLED and DISABLED_USER states
// unless it is a test package.
int oldState = pkgSetting.getEnabled(userId);
if (className == null
&&
(oldState == COMPONENT_ENABLED_STATE_DISABLED_USER
|| oldState == COMPONENT_ENABLED_STATE_DEFAULT
|| oldState == COMPONENT_ENABLED_STATE_ENABLED)
&&
(newState == COMPONENT_ENABLED_STATE_DISABLED_USER
|| newState == COMPONENT_ENABLED_STATE_DEFAULT
|| newState == COMPONENT_ENABLED_STATE_ENABLED)) {
// ok
} else {
throw new SecurityException(
"Shell cannot change component state for " + packageName + "/"
+ className + " to " + newState);
}
}
}
if (className == null) {
// We're dealing with an application/package level state change
synchronized (mLock) {
if (pkgSetting.getEnabled(userId) == newState) {
// Nothing to do
return;
}
}
// If we're enabling a system stub, there's a little more work to do.
// Prior to enabling the package, we need to decompress the APK(s) to the
// data partition and then replace the version on the system partition.
final AndroidPackage deletedPkg = pkgSetting.pkg;
final boolean isSystemStub = deletedPkg.isStub()
&& deletedPkg.isSystem();
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
if (!enableCompressedPackage(deletedPkg, pkgSetting)) {
return;
}
}
if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
// Don't care about who enables an app.
callingPackage = null;
}
synchronized (mLock) {
pkgSetting.setEnabled(newState, userId, callingPackage);
if ((newState == COMPONENT_ENABLED_STATE_DISABLED_USER
|| newState == COMPONENT_ENABLED_STATE_DISABLED)
&& checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
== PERMISSION_GRANTED) {
// This app should not generally be allowed to get disabled by the UI, but if it
// ever does, we don't want to end up with some of the user's apps permanently
// suspended.
unsuspendForSuspendingPackage(packageName, userId);
removeAllDistractingPackageRestrictions(userId);
}
}
} else {
synchronized (mLock) {
// We're dealing with a component level state change
// First, verify that this is a valid class name.
AndroidPackage pkg = pkgSetting.pkg;
if (pkg == null || !AndroidPackageUtils.hasComponentClassName(pkg, className)) {
if (pkg != null &&
pkg.getTargetSdkVersion() >=
Build.VERSION_CODES.JELLY_BEAN) {
throw new IllegalArgumentException("Component class " + className
+ " does not exist in " + packageName);
} else {
Slog.w(TAG, "Failed setComponentEnabledSetting: component class "
+ className + " does not exist in " + packageName);
}
}
switch (newState) {
case COMPONENT_ENABLED_STATE_ENABLED:
if (!pkgSetting.enableComponentLPw(className, userId)) {
return;
}
break;
case COMPONENT_ENABLED_STATE_DISABLED:
if (!pkgSetting.disableComponentLPw(className, userId)) {
return;
}
break;
case COMPONENT_ENABLED_STATE_DEFAULT:
if (!pkgSetting.restoreComponentLPw(className, userId)) {
return;
}
break;
default:
Slog.e(TAG, "Invalid new component state: " + newState);
return;
}
}
}
synchronized (mLock) {
if ((flags & PackageManager.SYNCHRONOUS) != 0) {
flushPackageRestrictionsAsUserInternalLocked(userId);
} else {
scheduleWritePackageRestrictionsLocked(userId);
}
updateSequenceNumberLP(pkgSetting, new int[] { userId });
final long callingId = Binder.clearCallingIdentity();
try {
updateInstantAppInstallerLocked(packageName);
} finally {
Binder.restoreCallingIdentity(callingId);
}
components = mPendingBroadcasts.get(userId, packageName);
final boolean newPackage = components == null;
if (newPackage) {
components = new ArrayList<>();
}
if (!components.contains(componentName)) {
components.add(componentName);
}
if ((flags&PackageManager.DONT_KILL_APP) == 0) {
sendNow = true;
// Purge entry from pending broadcast list if another one exists already
// since we are sending one right away.
mPendingBroadcasts.remove(userId, packageName);
} else {
if (newPackage) {
mPendingBroadcasts.put(userId, packageName, components);
}
if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
// Schedule a message - if it has been a "reasonably long time" since the
// service started, send the broadcast with a delay of one second to avoid
// delayed reactions from the receiver, else keep the default ten second delay
// to avoid extreme thrashing on service startup.
final long broadcastDelay = SystemClock.uptimeMillis() > mServiceStartWithDelay
? BROADCAST_DELAY
: BROADCAST_DELAY_DURING_STARTUP;
mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, broadcastDelay);
}
}
}
long callingId = Binder.clearCallingIdentity();
try {
if (sendNow) {
int packageUid = UserHandle.getUid(userId, pkgSetting.appId);
sendPackageChangedBroadcast(packageName,
(flags & PackageManager.DONT_KILL_APP) != 0, components, packageUid, null);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
@WorkerThread
@Override
public void flushPackageRestrictionsAsUser(int userId) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return;
}
if (!mUserManager.exists(userId)) {
return;
}
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
false /* checkShell */, "flushPackageRestrictions");
synchronized (mLock) {
flushPackageRestrictionsAsUserInternalLocked(userId);
}
}
@GuardedBy("mLock")
private void flushPackageRestrictionsAsUserInternalLocked(int userId) {
// NOTE: this invokes synchronous disk access, so callers using this
// method should consider running on a background thread
mSettings.writePackageRestrictionsLPr(userId);
mDirtyUsers.remove(userId);
if (mDirtyUsers.isEmpty()) {
mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
}
}
private void sendPackageChangedBroadcast(String packageName,
boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason) {
if (DEBUG_INSTALL)
Log.v(TAG, "Sending package changed: package=" + packageName + " components="
+ componentNames);
Bundle extras = new Bundle(4);
extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0));
String nameList[] = new String[componentNames.size()];
componentNames.toArray(nameList);
extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, dontKillApp);
extras.putInt(Intent.EXTRA_UID, packageUid);
if (reason != null) {
extras.putString(Intent.EXTRA_REASON, reason);
}
// If this is not reporting a change of the overall package, then only send it
// to registered receivers. We don't want to launch a swath of apps for every
// little component state change.
final int flags = !componentNames.contains(packageName)
? Intent.FLAG_RECEIVER_REGISTERED_ONLY : 0;
final int userId = UserHandle.getUserId(packageUid);
final boolean isInstantApp = isInstantApp(packageName, userId);
final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
final SparseArray<int[]> broadcastWhitelist;
synchronized (mLock) {
PackageSetting setting = getPackageSettingInternal(packageName, Process.SYSTEM_UID);
if (setting == null) {
return;
}
broadcastWhitelist = isInstantApp ? null : mAppsFilter.getVisibilityWhitelist(setting,
userIds, mSettings.mPackages);
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null,
userIds, instantUserIds, broadcastWhitelist);
}
@Override
public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
if (!mUserManager.exists(userId)) return;
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return;
}
final int permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */, "stop package");
// writer
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (!shouldFilterApplicationLocked(ps, callingUid, userId)
&& mSettings.setPackageStoppedStateLPw(this, packageName, stopped,
allowedByPermission, callingUid, userId)) {
scheduleWritePackageRestrictionsLocked(userId);
}
}
}
@Override
public String getInstallerPackageName(String packageName) {
final int callingUid = Binder.getCallingUid();
synchronized (mLock) {
final InstallSource installSource = getInstallSourceLocked(packageName, callingUid);
if (installSource == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
String installerPackageName = installSource.installerPackageName;
if (installerPackageName != null) {
final PackageSetting ps = mSettings.mPackages.get(installerPackageName);
if (ps == null || shouldFilterApplicationLocked(ps, callingUid,
UserHandle.getUserId(callingUid))) {
installerPackageName = null;
}
}
return installerPackageName;
}
}
@Override
@Nullable
public InstallSourceInfo getInstallSourceInfo(String packageName) {
final int callingUid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(callingUid);
String installerPackageName;
String initiatingPackageName;
String originatingPackageName;
final InstallSource installSource;
synchronized (mLock) {
installSource = getInstallSourceLocked(packageName, callingUid);
if (installSource == null) {
return null;
}
installerPackageName = installSource.installerPackageName;
if (installerPackageName != null) {
final PackageSetting ps = mSettings.mPackages.get(installerPackageName);
if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
installerPackageName = null;
}
}
if (installSource.isInitiatingPackageUninstalled) {
// We can't check visibility in the usual way, since the initiating package is no
// longer present. So we apply simpler rules to whether to expose the info:
// 1. Instant apps can't see it.
// 2. Otherwise only the installed app itself can see it.
final boolean isInstantApp = getInstantAppPackageName(callingUid) != null;
if (!isInstantApp && isCallerSameApp(packageName, callingUid)) {
initiatingPackageName = installSource.initiatingPackageName;
} else {
initiatingPackageName = null;
}
} else {
// All installSource strings are interned, so == is ok here
if (installSource.initiatingPackageName == installSource.installerPackageName) {
// The installer and initiator will often be the same, and when they are
// we can skip doing the same check again.
initiatingPackageName = installerPackageName;
} else {
initiatingPackageName = installSource.initiatingPackageName;
final PackageSetting ps = mSettings.mPackages.get(initiatingPackageName);
if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
initiatingPackageName = null;
}
}
}
originatingPackageName = installSource.originatingPackageName;
if (originatingPackageName != null) {
final PackageSetting ps = mSettings.mPackages.get(originatingPackageName);
if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
originatingPackageName = null;
}
}
}
// Remaining work can safely be done outside the lock. (Note that installSource is
// immutable so it's ok to carry on reading from it.)
if (originatingPackageName != null && mContext.checkCallingOrSelfPermission(
Manifest.permission.INSTALL_PACKAGES) != PackageManager.PERMISSION_GRANTED) {
originatingPackageName = null;
}
// If you can see the initiatingPackageName, and we have valid signing info for it,
// then we let you see that too.
final SigningInfo initiatingPackageSigningInfo;
final PackageSignatures signatures = installSource.initiatingPackageSignatures;
if (initiatingPackageName != null && signatures != null
&& signatures.mSigningDetails != SigningDetails.UNKNOWN) {
initiatingPackageSigningInfo = new SigningInfo(signatures.mSigningDetails);
} else {
initiatingPackageSigningInfo = null;
}
return new InstallSourceInfo(initiatingPackageName, initiatingPackageSigningInfo,
originatingPackageName, installerPackageName);
}
@GuardedBy("mLock")
@Nullable
private InstallSource getInstallSourceLocked(String packageName, int callingUid) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
// Installer info for Apex is not stored in PackageManager
if (ps == null && mApexManager.isApexPackage(packageName)) {
return InstallSource.EMPTY;
}
if (ps == null || shouldFilterApplicationLocked(ps, callingUid,
UserHandle.getUserId(callingUid))) {
return null;
}
return ps.installSource;
}
public boolean isOrphaned(String packageName) {
// reader
synchronized (mLock) {
if (!mPackages.containsKey(packageName)) {
return false;
}
return mSettings.isOrphaned(packageName);
}
}
@Override
public int getApplicationEnabledSetting(String packageName, int userId) {
if (!mUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get enabled");
// reader
synchronized (mLock) {
try {
if (shouldFilterApplicationLocked(
mSettings.getPackageLPr(packageName), callingUid, userId)) {
throw new PackageManager.NameNotFoundException(packageName);
}
return mSettings.getApplicationEnabledSettingLPr(packageName, userId);
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
}
}
@Override
public int getComponentEnabledSetting(@NonNull ComponentName component, int userId) {
if (component == null) return COMPONENT_ENABLED_STATE_DEFAULT;
if (!mUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getComponentEnabled");
synchronized (mLock) {
try {
if (shouldFilterApplicationLocked(
mSettings.getPackageLPr(component.getPackageName()), callingUid,
component, TYPE_UNKNOWN, userId)) {
throw new PackageManager.NameNotFoundException(component.getPackageName());
}
return mSettings.getComponentEnabledSettingLPr(component, userId);
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalArgumentException("Unknown component: " + component);
}
}
}
@Override
public void enterSafeMode() {
enforceSystemOrRoot("Only the system can request entering safe mode");
if (!mSystemReady) {
mSafeMode = true;
}
}
@Override
public void systemReady() {
enforceSystemOrRoot("Only the system can claim the system is ready");
mSystemReady = true;
final ContentResolver resolver = mContext.getContentResolver();
ContentObserver co = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
final boolean ephemeralFeatureDisabled =
Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0;
for (int userId : UserManagerService.getInstance().getUserIds()) {
final boolean instantAppsDisabledForUser =
ephemeralFeatureDisabled || Secure.getIntForUser(resolver,
Secure.INSTANT_APPS_ENABLED, 1, userId) == 0;
mWebInstantAppsDisabled.put(userId, instantAppsDisabledForUser);
}
}
};
mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
.getUriFor(Global.ENABLE_EPHEMERAL_FEATURE),
false, co, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(android.provider.Settings.Secure
.getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL);
co.onChange(true);
mAppsFilter.onSystemReady();
// Disable any carrier apps. We do this very early in boot to prevent the apps from being
// disabled after already being started.
CarrierAppUtils.disableCarrierAppsUntilPrivileged(
mContext.getOpPackageName(), UserHandle.USER_SYSTEM, mContext);
disableSkuSpecificApps();
// Read the compatibilty setting when the system is ready.
boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
mContext.getContentResolver(),
android.provider.Settings.Global.COMPATIBILITY_MODE, 1) == 1;
PackageParser.setCompatibilityModeEnabled(compatibilityModeEnabled);
if (DEBUG_SETTINGS) {
Log.d(TAG, "compatibility mode:" + compatibilityModeEnabled);
}
synchronized (mLock) {
// Verify that all of the preferred activity components actually
// exist. It is possible for applications to be updated and at
// that point remove a previously declared activity component that
// had been set as a preferred activity. We try to clean this up
// the next time we encounter that preferred activity, but it is
// possible for the user flow to never be able to return to that
// situation so here we do a sanity check to make sure we haven't
// left any junk around.
ArrayList<PreferredActivity> removed = new ArrayList<>();
for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
removed.clear();
for (PreferredActivity pa : pir.filterSet()) {
if (!mComponentResolver.isActivityDefined(pa.mPref.mComponent)) {
removed.add(pa);
}
}
if (removed.size() > 0) {
for (int r=0; r<removed.size(); r++) {
PreferredActivity pa = removed.get(r);
Slog.w(TAG, "Removing dangling preferred activity: "
+ pa.mPref.mComponent);
pir.removeFilter(pa);
}
mSettings.writePackageRestrictionsLPr(
mSettings.mPreferredActivities.keyAt(i));
}
}
}
mUserManager.systemReady();
// Now that we've scanned all packages, and granted any default
// permissions, ensure permissions are updated. Beware of dragons if you
// try optimizing this.
synchronized (mLock) {
mPermissionManager.updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, false);
final PermissionPolicyInternal permissionPolicyInternal =
mInjector.getPermissionPolicyInternal();
permissionPolicyInternal.setOnInitializedCallback(userId -> {
// The SDK updated case is already handled when we run during the ctor.
synchronized (mLock) {
mPermissionManager.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, false);
}
});
}
// Watch for external volumes that come and go over time
final StorageManager storage = mInjector.getStorageManager();
storage.registerListener(mStorageListener);
mInstallerService.systemReady();
mPackageDexOptimizer.systemReady();
mInjector.getStorageManagerInternal().addExternalStoragePolicy(
new StorageManagerInternal.ExternalStorageMountPolicy() {
@Override
public int getMountMode(int uid, String packageName) {
if (Process.isIsolated(uid)) {
return Zygote.MOUNT_EXTERNAL_NONE;
}
if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
return Zygote.MOUNT_EXTERNAL_DEFAULT;
}
if (checkUidPermission(WRITE_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
return Zygote.MOUNT_EXTERNAL_READ;
}
return Zygote.MOUNT_EXTERNAL_WRITE;
}
@Override
public boolean hasExternalStorage(int uid, String packageName) {
return true;
}
});
// Now that we're mostly running, clean up stale users and apps
mUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
mPermissionManager.systemReady();
if (mInstantAppResolverConnection != null) {
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mInstantAppResolverConnection.optimisticBind();
mContext.unregisterReceiver(this);
}
}, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
}
IntentFilter overlayFilter = new IntentFilter(Intent.ACTION_OVERLAY_CHANGED);
overlayFilter.addDataScheme("package");
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null) {
return;
}
Uri data = intent.getData();
if (data == null) {
return;
}
String packageName = data.getSchemeSpecificPart();
if (packageName == null) {
return;
}
AndroidPackage pkg = mPackages.get(packageName);
if (pkg == null) {
return;
}
sendPackageChangedBroadcast(pkg.getPackageName(),
true /* dontKillApp */,
new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
pkg.getUid(),
Intent.ACTION_OVERLAY_CHANGED);
}
}, overlayFilter);
mModuleInfoProvider.systemReady();
// Installer service might attempt to install some packages that have been staged for
// installation on reboot. Make sure this is the last component to be call since the
// installation might require other components to be ready.
mInstallerService.restoreAndApplyStagedSessionIfNeeded();
mExistingPackages = null;
}
public void waitForAppDataPrepared() {
if (mPrepareAppDataFuture == null) {
return;
}
ConcurrentUtils.waitForFutureNoInterrupt(mPrepareAppDataFuture, "wait for prepareAppData");
mPrepareAppDataFuture = null;
}
@Override
public boolean isSafeMode() {
// allow instant applications
return mSafeMode;
}
@Override
public boolean hasSystemUidErrors() {
// allow instant applications
return mHasSystemUidErrors;
}
static String arrayToString(int[] array) {
StringBuilder stringBuilder = new StringBuilder(128);
stringBuilder.append('[');
if (array != null) {
for (int i=0; i<array.length; i++) {
if (i > 0) stringBuilder.append(", ");
stringBuilder.append(array[i]);
}
}
stringBuilder.append(']');
return stringBuilder.toString();
}
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
(new PackageManagerShellCommand(this, mPermissionManagerService)).exec(
this, in, out, err, args, callback, resultReceiver);
}
@SuppressWarnings("resource")
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
DumpState dumpState = new DumpState();
boolean fullPreferred = false;
boolean checkin = false;
String packageName = null;
ArraySet<String> permissionNames = null;
int opti = 0;
while (opti < args.length) {
String opt = args[opti];
if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
break;
}
opti++;
if ("-a".equals(opt)) {
// Right now we only know how to print all.
} else if ("-h".equals(opt)) {
pw.println("Package manager dump options:");
pw.println(" [-h] [-f] [--checkin] [--all-components] [cmd] ...");
pw.println(" --checkin: dump for a checkin");
pw.println(" -f: print details of intent filters");
pw.println(" -h: print this help");
pw.println(" --all-components: include all component names in package dump");
pw.println(" cmd may be one of:");
pw.println(" apex: list active APEXes and APEX session state");
pw.println(" l[ibraries]: list known shared libraries");
pw.println(" f[eatures]: list device features");
pw.println(" k[eysets]: print known keysets");
pw.println(" r[esolvers] [activity|service|receiver|content]: dump intent resolvers");
pw.println(" perm[issions]: dump permissions");
pw.println(" permission [name ...]: dump declaration and use of given permission");
pw.println(" pref[erred]: print preferred package settings");
pw.println(" preferred-xml [--full]: print preferred package settings as xml");
pw.println(" prov[iders]: dump content providers");
pw.println(" p[ackages]: dump installed packages");
pw.println(" q[ueries]: dump app queryability calculations");
pw.println(" s[hared-users]: dump shared user IDs");
pw.println(" m[essages]: print collected runtime messages");
pw.println(" v[erifiers]: print package verifier info");
pw.println(" d[omain-preferred-apps]: print domains preferred apps");
pw.println(" i[ntent-filter-verifiers]|ifv: print intent filter verifier info");
pw.println(" version: print database version info");
pw.println(" write: write current settings now");
pw.println(" installs: details about install sessions");
pw.println(" check-permission <permission> <package> [<user>]: does pkg hold perm?");
pw.println(" dexopt: dump dexopt state");
pw.println(" compiler-stats: dump compiler statistics");
pw.println(" service-permissions: dump permissions required by services");
pw.println(" <package.name>: info about given package");
return;
} else if ("--checkin".equals(opt)) {
checkin = true;
} else if ("--all-components".equals(opt)) {
dumpState.setOptionEnabled(DumpState.OPTION_DUMP_ALL_COMPONENTS);
} else if ("-f".equals(opt)) {
dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
} else if ("--proto".equals(opt)) {
dumpProto(fd);
return;
} else {
pw.println("Unknown argument: " + opt + "; use -h for help");
}
}
// Is the caller requesting to dump a particular piece of data?
if (opti < args.length) {
String cmd = args[opti];
opti++;
// Is this a package name?
if ("android".equals(cmd) || cmd.contains(".")) {
packageName = cmd;
// When dumping a single package, we always dump all of its
// filter information since the amount of data will be reasonable.
dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
} else if ("check-permission".equals(cmd)) {
if (opti >= args.length) {
pw.println("Error: check-permission missing permission argument");
return;
}
String perm = args[opti];
opti++;
if (opti >= args.length) {
pw.println("Error: check-permission missing package argument");
return;
}
String pkg = args[opti];
opti++;
int user = UserHandle.getUserId(Binder.getCallingUid());
if (opti < args.length) {
try {
user = Integer.parseInt(args[opti]);
} catch (NumberFormatException e) {
pw.println("Error: check-permission user argument is not a number: "
+ args[opti]);
return;
}
}
// Normalize package name to handle renamed packages and static libs
pkg = resolveInternalPackageNameLPr(pkg, PackageManager.VERSION_CODE_HIGHEST);
pw.println(checkPermission(perm, pkg, user));
return;
} else if ("l".equals(cmd) || "libraries".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_LIBS);
} else if ("f".equals(cmd) || "features".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_FEATURES);
} else if ("r".equals(cmd) || "resolvers".equals(cmd)) {
if (opti >= args.length) {
dumpState.setDump(DumpState.DUMP_ACTIVITY_RESOLVERS
| DumpState.DUMP_SERVICE_RESOLVERS
| DumpState.DUMP_RECEIVER_RESOLVERS
| DumpState.DUMP_CONTENT_RESOLVERS);
} else {
while (opti < args.length) {
String name = args[opti];
if ("a".equals(name) || "activity".equals(name)) {
dumpState.setDump(DumpState.DUMP_ACTIVITY_RESOLVERS);
} else if ("s".equals(name) || "service".equals(name)) {
dumpState.setDump(DumpState.DUMP_SERVICE_RESOLVERS);
} else if ("r".equals(name) || "receiver".equals(name)) {
dumpState.setDump(DumpState.DUMP_RECEIVER_RESOLVERS);
} else if ("c".equals(name) || "content".equals(name)) {
dumpState.setDump(DumpState.DUMP_CONTENT_RESOLVERS);
} else {
pw.println("Error: unknown resolver table type: " + name);
return;
}
opti++;
}
}
} else if ("perm".equals(cmd) || "permissions".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_PERMISSIONS);
} else if ("permission".equals(cmd)) {
if (opti >= args.length) {
pw.println("Error: permission requires permission name");
return;
}
permissionNames = new ArraySet<>();
while (opti < args.length) {
permissionNames.add(args[opti]);
opti++;
}
dumpState.setDump(DumpState.DUMP_PERMISSIONS
| DumpState.DUMP_PACKAGES | DumpState.DUMP_SHARED_USERS);
} else if ("pref".equals(cmd) || "preferred".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_PREFERRED);
} else if ("preferred-xml".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_PREFERRED_XML);
if (opti < args.length && "--full".equals(args[opti])) {
fullPreferred = true;
opti++;
}
} else if ("d".equals(cmd) || "domain-preferred-apps".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_DOMAIN_PREFERRED);
} else if ("p".equals(cmd) || "packages".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_PACKAGES);
} else if ("q".equals(cmd) || "queries".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_QUERIES);
} else if ("s".equals(cmd) || "shared-users".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_SHARED_USERS);
if (opti < args.length && "noperm".equals(args[opti])) {
dumpState.setOptionEnabled(DumpState.OPTION_SKIP_PERMISSIONS);
}
} else if ("prov".equals(cmd) || "providers".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_PROVIDERS);
} else if ("m".equals(cmd) || "messages".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_MESSAGES);
} else if ("v".equals(cmd) || "verifiers".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_VERIFIERS);
} else if ("i".equals(cmd) || "ifv".equals(cmd)
|| "intent-filter-verifiers".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_INTENT_FILTER_VERIFIERS);
} else if ("version".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_VERSION);
} else if ("k".equals(cmd) || "keysets".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_KEYSETS);
} else if ("installs".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_INSTALLS);
} else if ("frozen".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_FROZEN);
} else if ("volumes".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_VOLUMES);
} else if ("dexopt".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_DEXOPT);
} else if ("compiler-stats".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_COMPILER_STATS);
} else if ("changes".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_CHANGES);
} else if ("service-permissions".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS);
} else if ("write".equals(cmd)) {
synchronized (mLock) {
mSettings.writeLPr();
pw.println("Settings written.");
return;
}
}
}
if (checkin) {
pw.println("vers,1");
}
// reader
synchronized (mLock) {
if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) {
if (!checkin) {
if (dumpState.onTitlePrinted())
pw.println();
pw.println("Database versions:");
mSettings.dumpVersionLPr(new IndentingPrintWriter(pw, " "));
}
}
if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
if (!checkin) {
if (dumpState.onTitlePrinted())
pw.println();
pw.println("Verifiers:");
pw.print(" Required: ");
pw.print(mRequiredVerifierPackage);
pw.print(" (uid=");
pw.print(getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.USER_SYSTEM));
pw.println(")");
} else if (mRequiredVerifierPackage != null) {
pw.print("vrfy,"); pw.print(mRequiredVerifierPackage);
pw.print(",");
pw.println(getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.USER_SYSTEM));
}
}
if (dumpState.isDumping(DumpState.DUMP_INTENT_FILTER_VERIFIERS) &&
packageName == null) {
if (mIntentFilterVerifierComponent != null) {
String verifierPackageName = mIntentFilterVerifierComponent.getPackageName();
if (!checkin) {
if (dumpState.onTitlePrinted())
pw.println();
pw.println("Intent Filter Verifier:");
pw.print(" Using: ");
pw.print(verifierPackageName);
pw.print(" (uid=");
pw.print(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.USER_SYSTEM));
pw.println(")");
} else if (verifierPackageName != null) {
pw.print("ifv,"); pw.print(verifierPackageName);
pw.print(",");
pw.println(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.USER_SYSTEM));
}
} else {
pw.println();
pw.println("No Intent Filter Verifier available!");
}
}
if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
boolean printedHeader = false;
final Iterator<String> it = mSharedLibraries.keySet().iterator();
while (it.hasNext()) {
String libName = it.next();
LongSparseArray<SharedLibraryInfo> versionedLib
= mSharedLibraries.get(libName);
if (versionedLib == null) {
continue;
}
final int versionCount = versionedLib.size();
for (int i = 0; i < versionCount; i++) {
SharedLibraryInfo libraryInfo = versionedLib.valueAt(i);
if (!checkin) {
if (!printedHeader) {
if (dumpState.onTitlePrinted())
pw.println();
pw.println("Libraries:");
printedHeader = true;
}
pw.print(" ");
} else {
pw.print("lib,");
}
pw.print(libraryInfo.getName());
if (libraryInfo.isStatic()) {
pw.print(" version=" + libraryInfo.getLongVersion());
}
if (!checkin) {
pw.print(" -> ");
}
if (libraryInfo.getPath() != null) {
pw.print(" (jar) ");
pw.print(libraryInfo.getPath());
} else {
pw.print(" (apk) ");
pw.print(libraryInfo.getPackageName());
}
pw.println();
}
}
}
if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
if (dumpState.onTitlePrinted())
pw.println();
if (!checkin) {
pw.println("Features:");
}
synchronized (mAvailableFeatures) {
for (FeatureInfo feat : mAvailableFeatures.values()) {
if (checkin) {
pw.print("feat,");
pw.print(feat.name);
pw.print(",");
pw.println(feat.version);
} else {
pw.print(" ");
pw.print(feat.name);
if (feat.version > 0) {
pw.print(" version=");
pw.print(feat.version);
}
pw.println();
}
}
}
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName);
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName);
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName);
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName);
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
int user = mSettings.mPreferredActivities.keyAt(i);
if (pir.dump(pw,
dumpState.getTitlePrinted()
? "\nPreferred Activities User " + user + ":"
: "Preferred Activities User " + user + ":", " ",
packageName, true, false)) {
dumpState.setTitlePrinted(true);
}
}
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
pw.flush();
FileOutputStream fout = new FileOutputStream(fd);
BufferedOutputStream str = new BufferedOutputStream(fout);
XmlSerializer serializer = new FastXmlSerializer();
try {
serializer.setOutput(str, StandardCharsets.UTF_8.name());
serializer.startDocument(null, true);
serializer.setFeature(
"http://xmlpull.org/v1/doc/features.html#indent-output", true);
mSettings.writePreferredActivitiesLPr(serializer, 0, fullPreferred);
serializer.endDocument();
serializer.flush();
} catch (IllegalArgumentException e) {
pw.println("Failed writing: " + e);
} catch (IllegalStateException e) {
pw.println("Failed writing: " + e);
} catch (IOException e) {
pw.println("Failed writing: " + e);
}
}
if (!checkin
&& dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)
&& packageName == null) {
pw.println();
int count = mSettings.mPackages.size();
if (count == 0) {
pw.println("No applications!");
pw.println();
} else {
final String prefix = " ";
Collection<PackageSetting> allPackageSettings = mSettings.mPackages.values();
if (allPackageSettings.size() == 0) {
pw.println("No domain preferred apps!");
pw.println();
} else {
pw.println("App verification status:");
pw.println();
count = 0;
for (PackageSetting ps : allPackageSettings) {
IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
if (ivi == null || ivi.getPackageName() == null) continue;
pw.println(prefix + "Package: " + ivi.getPackageName());
pw.println(prefix + "Domains: " + ivi.getDomainsString());
pw.println(prefix + "Status: " + ivi.getStatusString());
pw.println();
count++;
}
if (count == 0) {
pw.println(prefix + "No app verification established.");
pw.println();
}
for (int userId : mUserManager.getUserIds()) {
pw.println("App linkages for user " + userId + ":");
pw.println();
count = 0;
for (PackageSetting ps : allPackageSettings) {
final long status = ps.getDomainVerificationStatusForUser(userId);
if (status >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
&& !DEBUG_DOMAIN_VERIFICATION) {
continue;
}
pw.println(prefix + "Package: " + ps.name);
pw.println(prefix + "Domains: " + dumpDomainString(ps.name));
String statusStr = IntentFilterVerificationInfo.
getStatusStringFromValue(status);
pw.println(prefix + "Status: " + statusStr);
pw.println();
count++;
}
if (count == 0) {
pw.println(prefix + "No configured app linkages.");
pw.println();
}
}
}
}
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
mSettings.dumpPermissionsLPr(pw, packageName, permissionNames, dumpState);
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
mSettings.mKeySetManagerService.dumpLPr(pw, packageName, dumpState);
}
if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) {
mSettings.dumpPackagesLPr(pw, packageName, permissionNames, dumpState, checkin);
}
if (dumpState.isDumping(DumpState.DUMP_QUERIES)) {
final PackageSetting setting = mSettings.getPackageLPr(packageName);
Integer filteringAppId = setting == null ? null : setting.appId;
mAppsFilter.dumpQueries(
pw, this, filteringAppId, dumpState,
mUserManager.getUserIds());
}
if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
mSettings.dumpSharedUsersLPr(pw, packageName, permissionNames, dumpState, checkin);
}
if (dumpState.isDumping(DumpState.DUMP_CHANGES)) {
if (dumpState.onTitlePrinted()) pw.println();
pw.println("Package Changes:");
pw.print(" Sequence number="); pw.println(mChangedPackagesSequenceNumber);
final int K = mChangedPackages.size();
for (int i = 0; i < K; i++) {
final SparseArray<String> changes = mChangedPackages.valueAt(i);
pw.print(" User "); pw.print(mChangedPackages.keyAt(i)); pw.println(":");
final int N = changes.size();
if (N == 0) {
pw.print(" "); pw.println("No packages changed");
} else {
for (int j = 0; j < N; j++) {
final String pkgName = changes.valueAt(j);
final int sequenceNumber = changes.keyAt(j);
pw.print(" ");
pw.print("seq=");
pw.print(sequenceNumber);
pw.print(", package=");
pw.println(pkgName);
}
}
}
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) {
// XXX should handle packageName != null by dumping only install data that
// the given package is involved with.
if (dumpState.onTitlePrinted()) pw.println();
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
ipw.println();
ipw.println("Frozen packages:");
ipw.increaseIndent();
if (mFrozenPackages.size() == 0) {
ipw.println("(none)");
} else {
for (int i = 0; i < mFrozenPackages.size(); i++) {
ipw.println(mFrozenPackages.valueAt(i));
}
}
ipw.decreaseIndent();
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) {
if (dumpState.onTitlePrinted()) pw.println();
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
ipw.println();
ipw.println("Loaded volumes:");
ipw.increaseIndent();
if (mLoadedVolumes.size() == 0) {
ipw.println("(none)");
} else {
for (int i = 0; i < mLoadedVolumes.size(); i++) {
ipw.println(mLoadedVolumes.valueAt(i));
}
}
ipw.decreaseIndent();
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
&& packageName == null) {
mComponentResolver.dumpServicePermissions(pw, dumpState);
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
if (dumpState.onTitlePrinted()) pw.println();
dumpDexoptStateLPr(pw, packageName);
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
if (dumpState.onTitlePrinted()) pw.println();
dumpCompilerStatsLPr(pw, packageName);
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
if (dumpState.onTitlePrinted()) pw.println();
mSettings.dumpReadMessagesLPr(pw, dumpState);
pw.println();
pw.println("Package warning messages:");
dumpCriticalInfo(pw, null);
}
if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) {
dumpCriticalInfo(pw, "msg,");
}
}
// PackageInstaller should be called outside of mPackages lock
if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) {
// XXX should handle packageName != null by dumping only install data that
// the given package is involved with.
if (dumpState.onTitlePrinted()) pw.println();
mInstallerService.dump(new IndentingPrintWriter(pw, " ", 120));
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) {
mApexManager.dump(pw, packageName);
}
}
//TODO: b/111402650
private void disableSkuSpecificApps() {
String apkList[] = mContext.getResources().getStringArray(
R.array.config_disableApksUnlessMatchedSku_apk_list);
String skuArray[] = mContext.getResources().getStringArray(
R.array.config_disableApkUnlessMatchedSku_skus_list);
if (ArrayUtils.isEmpty(apkList)) {
return;
}
String sku = SystemProperties.get("ro.boot.hardware.sku");
if (!TextUtils.isEmpty(sku) && ArrayUtils.contains(skuArray, sku)) {
return;
}
for (String packageName : apkList) {
setSystemAppHiddenUntilInstalled(packageName, true);
for (UserInfo user : mInjector.getUserManagerInternal().getUsers(false)) {
setSystemAppInstallState(packageName, false, user.id);
}
}
}
private void dumpProto(FileDescriptor fd) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
synchronized (mLock) {
final long requiredVerifierPackageToken =
proto.start(PackageServiceDumpProto.REQUIRED_VERIFIER_PACKAGE);
proto.write(PackageServiceDumpProto.PackageShortProto.NAME, mRequiredVerifierPackage);
proto.write(
PackageServiceDumpProto.PackageShortProto.UID,
getPackageUid(
mRequiredVerifierPackage,
MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.USER_SYSTEM));
proto.end(requiredVerifierPackageToken);
if (mIntentFilterVerifierComponent != null) {
String verifierPackageName = mIntentFilterVerifierComponent.getPackageName();
final long verifierPackageToken =
proto.start(PackageServiceDumpProto.VERIFIER_PACKAGE);
proto.write(PackageServiceDumpProto.PackageShortProto.NAME, verifierPackageName);
proto.write(
PackageServiceDumpProto.PackageShortProto.UID,
getPackageUid(
verifierPackageName,
MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.USER_SYSTEM));
proto.end(verifierPackageToken);
}
dumpSharedLibrariesProto(proto);
dumpFeaturesProto(proto);
mSettings.dumpPackagesProto(proto);
mSettings.dumpSharedUsersProto(proto);
dumpCriticalInfo(proto);
}
proto.flush();
}
private void dumpFeaturesProto(ProtoOutputStream proto) {
synchronized (mAvailableFeatures) {
final int count = mAvailableFeatures.size();
for (int i = 0; i < count; i++) {
mAvailableFeatures.valueAt(i).dumpDebug(proto, PackageServiceDumpProto.FEATURES);
}
}
}
private void dumpSharedLibrariesProto(ProtoOutputStream proto) {
final int count = mSharedLibraries.size();
for (int i = 0; i < count; i++) {
final String libName = mSharedLibraries.keyAt(i);
LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(libName);
if (versionedLib == null) {
continue;
}
final int versionCount = versionedLib.size();
for (int j = 0; j < versionCount; j++) {
final SharedLibraryInfo libraryInfo = versionedLib.valueAt(j);
final long sharedLibraryToken =
proto.start(PackageServiceDumpProto.SHARED_LIBRARIES);
proto.write(PackageServiceDumpProto.SharedLibraryProto.NAME, libraryInfo.getName());
final boolean isJar = (libraryInfo.getPath() != null);
proto.write(PackageServiceDumpProto.SharedLibraryProto.IS_JAR, isJar);
if (isJar) {
proto.write(PackageServiceDumpProto.SharedLibraryProto.PATH,
libraryInfo.getPath());
} else {
proto.write(PackageServiceDumpProto.SharedLibraryProto.APK,
libraryInfo.getPackageName());
}
proto.end(sharedLibraryToken);
}
}
}
@GuardedBy("mLock")
@SuppressWarnings("resource")
private void dumpDexoptStateLPr(PrintWriter pw, String packageName) {
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.println();
ipw.println("Dexopt state:");
ipw.increaseIndent();
Collection<PackageSetting> pkgSettings;
if (packageName != null) {
PackageSetting targetPkgSetting = mSettings.mPackages.get(packageName);
if (targetPkgSetting != null) {
pkgSettings = Collections.singletonList(targetPkgSetting);
} else {
ipw.println("Unable to find package: " + packageName);
return;
}
} else {
pkgSettings = mSettings.mPackages.values();
}
for (PackageSetting pkgSetting : pkgSettings) {
if (pkgSetting.pkg == null) {
continue;
}
ipw.println("[" + pkgSetting.name + "]");
ipw.increaseIndent();
mPackageDexOptimizer.dumpDexoptState(ipw, pkgSetting.pkg, pkgSetting,
mDexManager.getPackageUseInfoOrDefault(pkgSetting.pkg.getPackageName()));
ipw.decreaseIndent();
}
}
@GuardedBy("mLock")
@SuppressWarnings("resource")
private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) {
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.println();
ipw.println("Compiler stats:");
ipw.increaseIndent();
Collection<AndroidPackage> packages;
if (packageName != null) {
AndroidPackage targetPackage = mPackages.get(packageName);
if (targetPackage != null) {
packages = Collections.singletonList(targetPackage);
} else {
ipw.println("Unable to find package: " + packageName);
return;
}
} else {
packages = mPackages.values();
}
for (AndroidPackage pkg : packages) {
ipw.println("[" + pkg.getPackageName() + "]");
ipw.increaseIndent();
CompilerStats.PackageStats stats = getCompilerPackageStats(pkg.getPackageName());
if (stats == null) {
ipw.println("(No recorded stats)");
} else {
stats.dump(ipw);
}
ipw.decreaseIndent();
}
}
private String dumpDomainString(String packageName) {
List<IntentFilterVerificationInfo> iviList = getIntentFilterVerifications(packageName)
.getList();
List<IntentFilter> filters = getAllIntentFilters(packageName).getList();
ArraySet<String> result = new ArraySet<>();
if (iviList.size() > 0) {
for (IntentFilterVerificationInfo ivi : iviList) {
result.addAll(ivi.getDomains());
}
}
if (filters != null && filters.size() > 0) {
for (IntentFilter filter : filters) {
if (filter.hasCategory(Intent.CATEGORY_BROWSABLE)
&& (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) {
result.addAll(filter.getHostsList());
}
}
}
StringBuilder sb = new StringBuilder(result.size() * 16);
for (String domain : result) {
if (sb.length() > 0) sb.append(" ");
sb.append(domain);
}
return sb.toString();
}
// ------- apps on sdcard specific code -------
static final boolean DEBUG_SD_INSTALL = false;
private static final String SD_ENCRYPTION_KEYSTORE_NAME = "AppsOnSD";
private static final String SD_ENCRYPTION_ALGORITHM = "AES";
private boolean mMediaMounted = false;
static String getEncryptKey() {
try {
String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(
SD_ENCRYPTION_KEYSTORE_NAME);
if (sdEncKey == null) {
sdEncKey = SystemKeyStore.getInstance().generateNewKeyHexString(128,
SD_ENCRYPTION_ALGORITHM, SD_ENCRYPTION_KEYSTORE_NAME);
if (sdEncKey == null) {
Slog.e(TAG, "Failed to create encryption keys");
return null;
}
}
return sdEncKey;
} catch (NoSuchAlgorithmException nsae) {
Slog.e(TAG, "Failed to create encryption keys with exception: " + nsae);
return null;
} catch (IOException ioe) {
Slog.e(TAG, "Failed to retrieve encryption keys with exception: " + ioe);
return null;
}
}
private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
ArrayList<AndroidPackage> packages, IIntentReceiver finishedReceiver) {
final int size = packages.size();
final String[] packageNames = new String[size];
final int[] packageUids = new int[size];
for (int i = 0; i < size; i++) {
final AndroidPackage pkg = packages.get(i);
packageNames[i] = pkg.getPackageName();
packageUids[i] = pkg.getUid();
}
sendResourcesChangedBroadcast(mediaStatus, replacing, packageNames, packageUids,
finishedReceiver);
}
private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
sendResourcesChangedBroadcast(mediaStatus, replacing,
pkgList.toArray(new String[pkgList.size()]), uidArr, finishedReceiver);
}
private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
String[] pkgList, int[] uidArr, IIntentReceiver finishedReceiver) {
int size = pkgList.length;
if (size > 0) {
// Send broadcasts here
Bundle extras = new Bundle();
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
if (uidArr != null) {
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
}
if (replacing) {
extras.putBoolean(Intent.EXTRA_REPLACING, replacing);
}
String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
: Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
// TODO: not sure how to handle this one.
sendPackageBroadcast(action, null, extras, 0, null, finishedReceiver, null, null, null);
}
}
private void loadPrivatePackages(final VolumeInfo vol) {
mHandler.post(() -> loadPrivatePackagesInner(vol));
}
private void loadPrivatePackagesInner(VolumeInfo vol) {
final String volumeUuid = vol.fsUuid;
if (TextUtils.isEmpty(volumeUuid)) {
Slog.e(TAG, "Loading internal storage is probably a mistake; ignoring");
return;
}
final ArrayList<PackageFreezer> freezers = new ArrayList<>();
final ArrayList<AndroidPackage> loaded = new ArrayList<>();
final int parseFlags = mDefParseFlags | PackageParser.PARSE_EXTERNAL_STORAGE;
final VersionInfo ver;
final List<PackageSetting> packages;
synchronized (mLock) {
ver = mSettings.findOrCreateVersion(volumeUuid);
packages = mSettings.getVolumePackagesLPr(volumeUuid);
}
for (PackageSetting ps : packages) {
freezers.add(freezePackage(ps.name, "loadPrivatePackagesInner"));
synchronized (mInstallLock) {
final AndroidPackage pkg;
try {
pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0, null);
loaded.add(pkg);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
}
if (!Build.FINGERPRINT.equals(ver.fingerprint)) {
clearAppDataLIF(ps.pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
| FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY
| Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
}
}
}
// Reconcile app data for all started/unlocked users
final StorageManager sm = mInjector.getStorageManager();
UserManagerInternal umInternal = mInjector.getUserManagerInternal();
for (UserInfo user : mUserManager.getUsers(false /* includeDying */)) {
final int flags;
if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE;
} else {
continue;
}
try {
sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, flags);
synchronized (mInstallLock) {
reconcileAppsDataLI(volumeUuid, user.id, flags, true /* migrateAppData */);
}
} catch (IllegalStateException e) {
// Device was probably ejected, and we'll process that event momentarily
Slog.w(TAG, "Failed to prepare storage: " + e);
}
}
synchronized (mLock) {
final boolean sdkUpdated = (ver.sdkVersion != mSdkVersion);
if (sdkUpdated) {
logCriticalInfo(Log.INFO, "Platform changed from " + ver.sdkVersion + " to "
+ mSdkVersion + "; regranting permissions for " + volumeUuid);
}
mPermissionManager.updateAllPermissions(volumeUuid, sdkUpdated);
// Yay, everything is now upgraded
ver.forceCurrent();
mSettings.writeLPr();
}
for (PackageFreezer freezer : freezers) {
freezer.close();
}
if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
sendResourcesChangedBroadcast(true, false, loaded, null);
mLoadedVolumes.add(vol.getId());
}
private void unloadPrivatePackages(final VolumeInfo vol) {
mHandler.post(() -> unloadPrivatePackagesInner(vol));
}
private void unloadPrivatePackagesInner(VolumeInfo vol) {
final String volumeUuid = vol.fsUuid;
if (TextUtils.isEmpty(volumeUuid)) {
Slog.e(TAG, "Unloading internal storage is probably a mistake; ignoring");
return;
}
final ArrayList<AndroidPackage> unloaded = new ArrayList<>();
synchronized (mInstallLock) {
synchronized (mLock) {
final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid);
for (PackageSetting ps : packages) {
if (ps.pkg == null) continue;
final AndroidPackage pkg = ps.pkg;
final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
final PackageRemovedInfo outInfo = new PackageRemovedInfo(this);
try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags,
"unloadPrivatePackagesInner")) {
if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo,
false, null)) {
unloaded.add(pkg);
} else {
Slog.w(TAG, "Failed to unload " + ps.codePath);
}
}
// Try very hard to release any references to this package
// so we don't risk the system server being killed due to
// open FDs
AttributeCache.instance().removePackage(ps.name);
}
mSettings.writeLPr();
}
}
if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
sendResourcesChangedBroadcast(false, false, unloaded, null);
mLoadedVolumes.remove(vol.getId());
// Try very hard to release any references to this path so we don't risk
// the system server being killed due to open FDs
ResourcesManager.getInstance().invalidatePath(vol.getPath().getAbsolutePath());
for (int i = 0; i < 3; i++) {
System.gc();
System.runFinalization();
}
}
private void assertPackageKnownAndInstalled(String volumeUuid, String packageName, int userId)
throws PackageManagerException {
synchronized (mLock) {
// Normalize package name to handle renamed packages
packageName = normalizePackageNameLPr(packageName);
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) {
throw new PackageManagerException("Package " + packageName + " is unknown");
} else if (!TextUtils.equals(volumeUuid, ps.volumeUuid)) {
throw new PackageManagerException(
"Package " + packageName + " found on unknown volume " + volumeUuid
+ "; expected volume " + ps.volumeUuid);
} else if (!ps.getInstalled(userId)) {
throw new PackageManagerException(
"Package " + packageName + " not installed for user " + userId);
}
}
}
private List<String> collectAbsoluteCodePaths() {
synchronized (mLock) {
List<String> codePaths = new ArrayList<>();
final int packageCount = mSettings.mPackages.size();
for (int i = 0; i < packageCount; i++) {
final PackageSetting ps = mSettings.mPackages.valueAt(i);
codePaths.add(ps.codePath.getAbsolutePath());
}
return codePaths;
}
}
/**
* Examine all apps present on given mounted volume, and destroy apps that
* aren't expected, either due to uninstallation or reinstallation on
* another volume.
*/
private void reconcileApps(String volumeUuid) {
List<String> absoluteCodePaths = collectAbsoluteCodePaths();
List<File> filesToDelete = null;
final File[] files = FileUtils.listFilesOrEmpty(
Environment.getDataAppDirectory(volumeUuid));
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
String absolutePath = file.getAbsolutePath();
boolean pathValid = false;
final int absoluteCodePathCount = absoluteCodePaths.size();
for (int i = 0; i < absoluteCodePathCount; i++) {
String absoluteCodePath = absoluteCodePaths.get(i);
if (absoluteCodePath.startsWith(absolutePath)) {
pathValid = true;
break;
}
}
if (!pathValid) {
if (filesToDelete == null) {
filesToDelete = new ArrayList<>();
}
filesToDelete.add(file);
}
}
if (filesToDelete != null) {
final int fileToDeleteCount = filesToDelete.size();
for (int i = 0; i < fileToDeleteCount; i++) {
File fileToDelete = filesToDelete.get(i);
logCriticalInfo(Log.WARN, "Destroying orphaned" + fileToDelete);
synchronized (mInstallLock) {
removeCodePathLI(fileToDelete);
}
}
}
}
/**
* Reconcile all app data for the given user.
* <p>
* Verifies that directories exist and that ownership and labeling is
* correct for all installed apps on all mounted volumes.
*/
void reconcileAppsData(int userId, int flags, boolean migrateAppsData) {
final StorageManager storage = mInjector.getStorageManager();
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final String volumeUuid = vol.getFsUuid();
synchronized (mInstallLock) {
reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppsData);
}
}
}
@GuardedBy("mInstallLock")
private void reconcileAppsDataLI(String volumeUuid, int userId, int flags,
boolean migrateAppData) {
reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppData, false /* onlyCoreApps */);
}
/**
* Reconcile all app data on given mounted volume.
* <p>
* Destroys app data that isn't expected, either due to uninstallation or
* reinstallation on another volume.
* <p>
* Verifies that directories exist and that ownership and labeling is
* correct for all installed apps.
* @return list of skipped non-core packages (if {@code onlyCoreApps} is true)
*/
@GuardedBy("mInstallLock")
private List<String> reconcileAppsDataLI(String volumeUuid, int userId, int flags,
boolean migrateAppData, boolean onlyCoreApps) {
Slog.v(TAG, "reconcileAppsData for " + volumeUuid + " u" + userId + " 0x"
+ Integer.toHexString(flags) + " migrateAppData=" + migrateAppData);
List<String> result = onlyCoreApps ? new ArrayList<>() : null;
final File ceDir = Environment.getDataUserCeDirectory(volumeUuid, userId);
final File deDir = Environment.getDataUserDeDirectory(volumeUuid, userId);
// First look for stale data that doesn't belong, and check if things
// have changed since we did our last restorecon
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
if (StorageManager.isFileEncryptedNativeOrEmulated()
&& !StorageManager.isUserKeyUnlocked(userId)) {
throw new RuntimeException(
"Yikes, someone asked us to reconcile CE storage while " + userId
+ " was still locked; this would have caused massive data loss!");
}
final File[] files = FileUtils.listFilesOrEmpty(ceDir);
for (File file : files) {
final String packageName = file.getName();
try {
assertPackageKnownAndInstalled(volumeUuid, packageName, userId);
} catch (PackageManagerException e) {
logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
try {
mInstaller.destroyAppData(volumeUuid, packageName, userId,
StorageManager.FLAG_STORAGE_CE, 0);
} catch (InstallerException e2) {
logCriticalInfo(Log.WARN, "Failed to destroy: " + e2);
}
}
}
}
if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
final File[] files = FileUtils.listFilesOrEmpty(deDir);
for (File file : files) {
final String packageName = file.getName();
try {
assertPackageKnownAndInstalled(volumeUuid, packageName, userId);
} catch (PackageManagerException e) {
logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
try {
mInstaller.destroyAppData(volumeUuid, packageName, userId,
StorageManager.FLAG_STORAGE_DE, 0);
} catch (InstallerException e2) {
logCriticalInfo(Log.WARN, "Failed to destroy: " + e2);
}
}
}
}
// Ensure that data directories are ready to roll for all packages
// installed for this volume and user
final List<PackageSetting> packages;
synchronized (mLock) {
packages = mSettings.getVolumePackagesLPr(volumeUuid);
}
int preparedCount = 0;
for (PackageSetting ps : packages) {
final String packageName = ps.name;
if (ps.pkg == null) {
Slog.w(TAG, "Odd, missing scanned package " + packageName);
// TODO: might be due to legacy ASEC apps; we should circle back
// and reconcile again once they're scanned
continue;
}
// Skip non-core apps if requested
if (onlyCoreApps && !ps.pkg.isCoreApp()) {
result.add(packageName);
continue;
}
if (ps.getInstalled(userId)) {
prepareAppDataAndMigrateLIF(ps.pkg, userId, flags, migrateAppData);
preparedCount++;
}
}
Slog.v(TAG, "reconcileAppsData finished " + preparedCount + " packages");
return result;
}
/**
* Prepare app data for the given app just after it was installed or
* upgraded. This method carefully only touches users that it's installed
* for, and it forces a restorecon to handle any seinfo changes.
* <p>
* Verifies that directories exist and that ownership and labeling is
* correct for all installed apps. If there is an ownership mismatch, it
* will try recovering system apps by wiping data; third-party app data is
* left intact.
* <p>
* <em>Note: To avoid a deadlock, do not call this method with {@code mLock} lock held</em>
*/
private void prepareAppDataAfterInstallLIF(AndroidPackage pkg) {
final PackageSetting ps;
synchronized (mLock) {
ps = mSettings.mPackages.get(pkg.getPackageName());
mSettings.writeKernelMappingLPr(ps);
}
UserManagerInternal umInternal = mInjector.getUserManagerInternal();
StorageManagerInternal smInternal = mInjector.getStorageManagerInternal();
for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) {
final int flags;
if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE;
} else {
continue;
}
if (ps.getInstalled(user.id)) {
// TODO: when user data is locked, mark that we're still dirty
prepareAppDataLIF(pkg, user.id, flags);
if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
// Prepare app data on external storage; currently this is used to
// setup any OBB dirs that were created by the installer correctly.
int uid = UserHandle.getUid(user.id, UserHandle.getAppId(pkg.getUid()));
smInternal.prepareAppDataAfterInstall(pkg.getPackageName(), uid);
}
}
}
}
/**
* Prepare app data for the given app.
* <p>
* Verifies that directories exist and that ownership and labeling is
* correct for all installed apps. If there is an ownership mismatch, this
* will try recovering system apps by wiping data; third-party app data is
* left intact.
*/
private void prepareAppDataLIF(AndroidPackage pkg, int userId, int flags) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
prepareAppDataLeafLIF(pkg, userId, flags);
}
private void prepareAppDataAndMigrateLIF(AndroidPackage pkg, int userId, int flags,
boolean maybeMigrateAppData) {
prepareAppDataLIF(pkg, userId, flags);
if (maybeMigrateAppData && maybeMigrateAppDataLIF(pkg, userId)) {
// We may have just shuffled around app data directories, so
// prepare them one more time
prepareAppDataLIF(pkg, userId, flags);
}
}
private void prepareAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) {
if (DEBUG_APP_DATA) {
Slog.v(TAG, "prepareAppData for " + pkg.getPackageName() + " u" + userId + " 0x"
+ Integer.toHexString(flags));
}
final PackageSetting ps;
synchronized (mLock) {
ps = mSettings.mPackages.get(pkg.getPackageName());
}
final String volumeUuid = pkg.getVolumeUuid();
final String packageName = pkg.getPackageName();
final int appId = UserHandle.getAppId(pkg.getUid());
String pkgSeInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
Preconditions.checkNotNull(pkgSeInfo);
final String seInfo = pkgSeInfo + (pkg.getSeInfoUser() != null ? pkg.getSeInfoUser() : "");
long ceDataInode = -1;
try {
ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
appId, seInfo, pkg.getTargetSdkVersion());
} catch (InstallerException e) {
if (pkg.isSystem()) {
logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName
+ ", but trying to recover: " + e);
destroyAppDataLeafLIF(pkg, userId, flags);
try {
ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
appId, seInfo, pkg.getTargetSdkVersion());
logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
} catch (InstallerException e2) {
logCriticalInfo(Log.DEBUG, "Recovery failed!");
}
} else {
Slog.e(TAG, "Failed to create app data for " + packageName + ": " + e);
}
}
// Prepare the application profiles only for upgrades and first boot (so that we don't
// repeat the same operation at each boot).
// We only have to cover the upgrade and first boot here because for app installs we
// prepare the profiles before invoking dexopt (in installPackageLI).
//
// We also have to cover non system users because we do not call the usual install package
// methods for them.
//
// NOTE: in order to speed up first boot time we only create the current profile and do not
// update the content of the reference profile. A system image should already be configured
// with the right profile keys and the profiles for the speed-profile prebuilds should
// already be copied. That's done in #performDexOptUpgrade.
//
// TODO(calin, mathieuc): We should use .dm files for prebuilds profiles instead of
// manually copying them in #performDexOptUpgrade. When we do that we should have a more
// granular check here and only update the existing profiles.
if (mIsUpgrade || mFirstBoot || (userId != UserHandle.USER_SYSTEM)) {
mArtManagerService.prepareAppProfiles(pkg, userId,
/* updateReferenceProfileContent= */ false);
}
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
// TODO: mark this structure as dirty so we persist it!
synchronized (mLock) {
if (ps != null) {
ps.setCeDataInode(ceDataInode, userId);
}
}
}
prepareAppDataContentsLeafLIF(pkg, ps, userId, flags);
}
private void prepareAppDataContentsLIF(AndroidPackage pkg, @Nullable PackageSetting pkgSetting,
int userId, int flags) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
prepareAppDataContentsLeafLIF(pkg, pkgSetting, userId, flags);
}
private void prepareAppDataContentsLeafLIF(AndroidPackage pkg,
@Nullable PackageSetting pkgSetting, int userId, int flags) {
final String volumeUuid = pkg.getVolumeUuid();
final String packageName = pkg.getPackageName();
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
// Create a native library symlink only if we have native libraries
// and if the native libraries are 32 bit libraries. We do not provide
// this symlink for 64 bit libraries.
String primaryCpuAbi = AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting);
if (primaryCpuAbi != null && !VMRuntime.is64BitAbi(primaryCpuAbi)) {
final String nativeLibPath = pkg.getNativeLibraryDir();
try {
mInstaller.linkNativeLibraryDirectory(volumeUuid, packageName,
nativeLibPath, userId);
} catch (InstallerException e) {
Slog.e(TAG, "Failed to link native for " + packageName + ": " + e);
}
}
}
}
/**
* For system apps on non-FBE devices, this method migrates any existing
* CE/DE data to match the {@code defaultToDeviceProtectedStorage} flag
* requested by the app.
*/
private boolean maybeMigrateAppDataLIF(AndroidPackage pkg, int userId) {
if (pkg.isSystem() && !StorageManager.isFileEncryptedNativeOrEmulated()
&& PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
final int storageTarget = pkg.isDefaultToDeviceProtectedStorage()
? StorageManager.FLAG_STORAGE_DE : StorageManager.FLAG_STORAGE_CE;
try {
mInstaller.migrateAppData(pkg.getVolumeUuid(), pkg.getPackageName(), userId,
storageTarget);
} catch (InstallerException e) {
logCriticalInfo(Log.WARN,
"Failed to migrate " + pkg.getPackageName() + ": " + e.getMessage());
}
return true;
} else {
return false;
}
}
public PackageFreezer freezePackage(String packageName, String killReason) {
return freezePackage(packageName, UserHandle.USER_ALL, killReason);
}
public PackageFreezer freezePackage(String packageName, int userId, String killReason) {
return new PackageFreezer(packageName, userId, killReason);
}
public PackageFreezer freezePackageForInstall(String packageName, int installFlags,
String killReason) {
return freezePackageForInstall(packageName, UserHandle.USER_ALL, installFlags, killReason);
}
public PackageFreezer freezePackageForInstall(String packageName, int userId, int installFlags,
String killReason) {
if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
return new PackageFreezer();
} else {
return freezePackage(packageName, userId, killReason);
}
}
public PackageFreezer freezePackageForDelete(String packageName, int deleteFlags,
String killReason) {
return freezePackageForDelete(packageName, UserHandle.USER_ALL, deleteFlags, killReason);
}
public PackageFreezer freezePackageForDelete(String packageName, int userId, int deleteFlags,
String killReason) {
if ((deleteFlags & PackageManager.DELETE_DONT_KILL_APP) != 0) {
return new PackageFreezer();
} else {
return freezePackage(packageName, userId, killReason);
}
}
/**
* Class that freezes and kills the given package upon creation, and
* unfreezes it upon closing. This is typically used when doing surgery on
* app code/data to prevent the app from running while you're working.
*/
private class PackageFreezer implements AutoCloseable {
private final String mPackageName;
private final boolean mWeFroze;
private final AtomicBoolean mClosed = new AtomicBoolean();
private final CloseGuard mCloseGuard = CloseGuard.get();
/**
* Create and return a stub freezer that doesn't actually do anything,
* typically used when someone requested
* {@link PackageManager#INSTALL_DONT_KILL_APP} or
* {@link PackageManager#DELETE_DONT_KILL_APP}.
*/
public PackageFreezer() {
mPackageName = null;
mWeFroze = false;
mCloseGuard.open("close");
}
public PackageFreezer(String packageName, int userId, String killReason) {
synchronized (mLock) {
mPackageName = packageName;
mWeFroze = mFrozenPackages.add(mPackageName);
final PackageSetting ps = mSettings.mPackages.get(mPackageName);
if (ps != null) {
killApplication(ps.name, ps.appId, userId, killReason);
}
}
mCloseGuard.open("close");
}
@Override
protected void finalize() throws Throwable {
try {
mCloseGuard.warnIfOpen();
close();
} finally {
super.finalize();
}
}
@Override
public void close() {
mCloseGuard.close();
if (mClosed.compareAndSet(false, true)) {
synchronized (mLock) {
if (mWeFroze) {
mFrozenPackages.remove(mPackageName);
}
}
}
}
}
/**
* Verify that given package is currently frozen.
*/
private void checkPackageFrozen(String packageName) {
synchronized (mLock) {
if (!mFrozenPackages.contains(packageName)) {
Slog.wtf(TAG, "Expected " + packageName + " to be frozen!", new Throwable());
}
}
}
@Override
public int movePackage(final String packageName, final String volumeUuid) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
final int callingUid = Binder.getCallingUid();
final UserHandle user = new UserHandle(UserHandle.getUserId(callingUid));
final int moveId = mNextMoveId.getAndIncrement();
mHandler.post(() -> {
try {
movePackageInternal(packageName, volumeUuid, moveId, callingUid, user);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to move " + packageName, e);
mMoveCallbacks.notifyStatusChanged(moveId, e.error);
}
});
return moveId;
}
private void movePackageInternal(final String packageName, final String volumeUuid,
final int moveId, final int callingUid, UserHandle user)
throws PackageManagerException {
final StorageManager storage = mInjector.getStorageManager();
final PackageManager pm = mContext.getPackageManager();
final String currentVolumeUuid;
final File codeFile;
final InstallSource installSource;
final String packageAbiOverride;
final int appId;
final String seinfo;
final String label;
final int targetSdkVersion;
final PackageFreezer freezer;
final int[] installedUserIds;
final boolean isCurrentLocationExternal;
final String fromCodePath;
// reader
synchronized (mLock) {
final AndroidPackage pkg = mPackages.get(packageName);
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (pkg == null
|| ps == null
|| shouldFilterApplicationLocked(ps, callingUid, user.getIdentifier())) {
throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
}
if (pkg.isSystem()) {
throw new PackageManagerException(MOVE_FAILED_SYSTEM_PACKAGE,
"Cannot move system application");
}
final boolean isInternalStorage = VolumeInfo.ID_PRIVATE_INTERNAL.equals(volumeUuid);
final boolean allow3rdPartyOnInternal = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allow3rdPartyAppOnInternal);
if (isInternalStorage && !allow3rdPartyOnInternal) {
throw new PackageManagerException(MOVE_FAILED_3RD_PARTY_NOT_ALLOWED_ON_INTERNAL,
"3rd party apps are not allowed on internal storage");
}
currentVolumeUuid = ps.volumeUuid;
final File probe = new File(pkg.getCodePath());
final File probeOat = new File(probe, "oat");
if (!probe.isDirectory() || !probeOat.isDirectory()) {
throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
"Move only supported for modern cluster style installs");
}
if (Objects.equals(currentVolumeUuid, volumeUuid)) {
throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
"Package already moved to " + volumeUuid);
}
if (!pkg.isExternalStorage() && isPackageDeviceAdminOnAnyUser(packageName)) {
throw new PackageManagerException(MOVE_FAILED_DEVICE_ADMIN,
"Device admin cannot be moved");
}
if (mFrozenPackages.contains(packageName)) {
throw new PackageManagerException(MOVE_FAILED_OPERATION_PENDING,
"Failed to move already frozen package");
}
isCurrentLocationExternal = pkg.isExternalStorage();
codeFile = new File(pkg.getCodePath());
installSource = ps.installSource;
packageAbiOverride = ps.cpuAbiOverrideString;
appId = UserHandle.getAppId(pkg.getUid());
seinfo = AndroidPackageUtils.getSeInfo(pkg, ps);
label = String.valueOf(pm.getApplicationLabel(pkg.toAppInfoWithoutState()));
targetSdkVersion = pkg.getTargetSdkVersion();
freezer = freezePackage(packageName, "movePackageInternal");
installedUserIds = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
if (codeFile.getParentFile().getName().startsWith(RANDOM_DIR_PREFIX)) {
fromCodePath = codeFile.getParentFile().getAbsolutePath();
} else {
fromCodePath = codeFile.getAbsolutePath();
}
}
final Bundle extras = new Bundle();
extras.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
extras.putString(Intent.EXTRA_TITLE, label);
mMoveCallbacks.notifyCreated(moveId, extras);
int installFlags;
final boolean moveCompleteApp;
final File measurePath;
installFlags = INSTALL_INTERNAL;
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
moveCompleteApp = true;
measurePath = Environment.getDataAppDirectory(volumeUuid);
} else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
moveCompleteApp = false;
measurePath = storage.getPrimaryPhysicalVolume().getPath();
} else {
final VolumeInfo volume = storage.findVolumeByUuid(volumeUuid);
if (volume == null || volume.getType() != VolumeInfo.TYPE_PRIVATE
|| !volume.isMountedWritable()) {
freezer.close();
throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
"Move location not mounted private volume");
}
moveCompleteApp = true;
measurePath = Environment.getDataAppDirectory(volumeUuid);
}
// If we're moving app data around, we need all the users unlocked
if (moveCompleteApp) {
for (int userId : installedUserIds) {
if (StorageManager.isFileEncryptedNativeOrEmulated()
&& !StorageManager.isUserKeyUnlocked(userId)) {
throw new PackageManagerException(MOVE_FAILED_LOCKED_USER,
"User " + userId + " must be unlocked");
}
}
}
final PackageStats stats = new PackageStats(null, -1);
synchronized (mInstaller) {
for (int userId : installedUserIds) {
if (!getPackageSizeInfoLI(packageName, userId, stats)) {
freezer.close();
throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
"Failed to measure package size");
}
}
}
if (DEBUG_INSTALL) Slog.d(TAG, "Measured code size " + stats.codeSize + ", data size "
+ stats.dataSize);
final long startFreeBytes = measurePath.getUsableSpace();
final long sizeBytes;
if (moveCompleteApp) {
sizeBytes = stats.codeSize + stats.dataSize;
} else {
sizeBytes = stats.codeSize;
}
if (sizeBytes > storage.getStorageBytesUntilLow(measurePath)) {
freezer.close();
throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
"Not enough free space to move");
}
mMoveCallbacks.notifyStatusChanged(moveId, 10);
final CountDownLatch installedLatch = new CountDownLatch(1);
final IPackageInstallObserver2 installObserver = new IPackageInstallObserver2.Stub() {
@Override
public void onUserActionRequired(Intent intent) throws RemoteException {
throw new IllegalStateException();
}
@Override
public void onPackageInstalled(String basePackageName, int returnCode, String msg,
Bundle extras) throws RemoteException {
if (DEBUG_INSTALL) Slog.d(TAG, "Install result for move: "
+ PackageManager.installStatusToString(returnCode, msg));
installedLatch.countDown();
freezer.close();
final int status = PackageManager.installStatusToPublicStatus(returnCode);
switch (status) {
case PackageInstaller.STATUS_SUCCESS:
mMoveCallbacks.notifyStatusChanged(moveId,
PackageManager.MOVE_SUCCEEDED);
logAppMovedStorage(packageName, isCurrentLocationExternal);
break;
case PackageInstaller.STATUS_FAILURE_STORAGE:
mMoveCallbacks.notifyStatusChanged(moveId,
PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE);
break;
default:
mMoveCallbacks.notifyStatusChanged(moveId,
PackageManager.MOVE_FAILED_INTERNAL_ERROR);
break;
}
}
};
final MoveInfo move;
if (moveCompleteApp) {
// Kick off a thread to report progress estimates
new Thread(() -> {
while (true) {
try {
if (installedLatch.await(1, TimeUnit.SECONDS)) {
break;
}
} catch (InterruptedException ignored) {
}
final long deltaFreeBytes = startFreeBytes - measurePath.getUsableSpace();
final int progress = 10 + (int) MathUtils.constrain(
((deltaFreeBytes * 80) / sizeBytes), 0, 80);
mMoveCallbacks.notifyStatusChanged(moveId, progress);
}
}).start();
move = new MoveInfo(moveId, currentVolumeUuid, volumeUuid, packageName,
appId, seinfo, targetSdkVersion, fromCodePath);
} else {
move = null;
}
installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
final Message msg = mHandler.obtainMessage(INIT_COPY);
final OriginInfo origin = OriginInfo.fromExistingFile(codeFile);
final InstallParams params = new InstallParams(origin, move, installObserver, installFlags,
installSource, volumeUuid, null /*verificationInfo*/, user,
packageAbiOverride, null /*grantedPermissions*/,
null /*whitelistedRestrictedPermissions*/, MODE_DEFAULT /* autoRevokePermissions */,
PackageParser.SigningDetails.UNKNOWN,
PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST,
DataLoaderType.NONE);
params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "movePackage",
System.identityHashCode(msg.obj));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(msg.obj));
mHandler.sendMessage(msg);
}
/**
* Logs that an app has been moved from internal to external storage and vice versa.
* @param packageName The package that was moved.
*/
private void logAppMovedStorage(String packageName, boolean isPreviousLocationExternal) {
final AndroidPackage pkg;
synchronized (mLock) {
pkg = mPackages.get(packageName);
}
if (pkg == null) {
return;
}
final StorageManager storage = mInjector.getStorageManager();;
VolumeInfo volume = storage.findVolumeByUuid(pkg.getStorageUuid().toString());
int packageExternalStorageType = getPackageExternalStorageType(volume, pkg.isExternalStorage());
if (!isPreviousLocationExternal && pkg.isExternalStorage()) {
// Move from internal to external storage.
FrameworkStatsLog.write(FrameworkStatsLog.APP_MOVED_STORAGE_REPORTED,
packageExternalStorageType,
FrameworkStatsLog.APP_MOVED_STORAGE_REPORTED__MOVE_TYPE__TO_EXTERNAL,
packageName);
} else if (isPreviousLocationExternal && !pkg.isExternalStorage()) {
// Move from external to internal storage.
FrameworkStatsLog.write(FrameworkStatsLog.APP_MOVED_STORAGE_REPORTED,
packageExternalStorageType,
FrameworkStatsLog.APP_MOVED_STORAGE_REPORTED__MOVE_TYPE__TO_INTERNAL,
packageName);
}
}
@Override
public int movePrimaryStorage(String volumeUuid) throws RemoteException {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
final int realMoveId = mNextMoveId.getAndIncrement();
final Bundle extras = new Bundle();
extras.putString(VolumeRecord.EXTRA_FS_UUID, volumeUuid);
mMoveCallbacks.notifyCreated(realMoveId, extras);
final IPackageMoveObserver callback = new IPackageMoveObserver.Stub() {
@Override
public void onCreated(int moveId, Bundle extras) {
// Ignored
}
@Override
public void onStatusChanged(int moveId, int status, long estMillis) {
mMoveCallbacks.notifyStatusChanged(realMoveId, status, estMillis);
}
};
final StorageManager storage = mInjector.getStorageManager();
storage.setPrimaryStorageUuid(volumeUuid, callback);
return realMoveId;
}
@Override
public int getMoveStatus(int moveId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
return mMoveCallbacks.mLastStatus.get(moveId);
}
@Override
public void registerMoveCallback(IPackageMoveObserver callback) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
mMoveCallbacks.register(callback);
}
@Override
public void unregisterMoveCallback(IPackageMoveObserver callback) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
mMoveCallbacks.unregister(callback);
}
@Override
public boolean setInstallLocation(int loc) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
null);
if (getInstallLocation() == loc) {
return true;
}
if (loc == PackageHelper.APP_INSTALL_AUTO || loc == PackageHelper.APP_INSTALL_INTERNAL
|| loc == PackageHelper.APP_INSTALL_EXTERNAL) {
android.provider.Settings.Global.putInt(mContext.getContentResolver(),
android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION, loc);
return true;
}
return false;
}
@Override
public int getInstallLocation() {
// allow instant app access
return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION,
PackageHelper.APP_INSTALL_AUTO);
}
/** Called by UserManagerService */
void cleanUpUser(UserManagerService userManager, @UserIdInt int userId) {
synchronized (mLock) {
mDirtyUsers.remove(userId);
mUserNeedsBadging.delete(userId);
mSettings.removeUserLPw(userId);
mPendingBroadcasts.remove(userId);
mInstantAppRegistry.onUserRemovedLPw(userId);
removeUnusedPackagesLPw(userManager, userId);
}
}
/**
* We're removing userId and would like to remove any downloaded packages
* that are no longer in use by any other user.
* @param userId the user being removed
*/
@GuardedBy("mLock")
private void removeUnusedPackagesLPw(UserManagerService userManager, final int userId) {
final boolean DEBUG_CLEAN_APKS = false;
int [] users = userManager.getUserIds();
Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
if (ps.pkg == null) {
continue;
}
final String packageName = ps.pkg.getPackageName();
// Skip over if system app or static shared library
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0
|| !TextUtils.isEmpty(ps.pkg.getStaticSharedLibName())) {
continue;
}
if (DEBUG_CLEAN_APKS) {
Slog.i(TAG, "Checking package " + packageName);
}
boolean keep = shouldKeepUninstalledPackageLPr(packageName);
if (keep) {
if (DEBUG_CLEAN_APKS) {
Slog.i(TAG, " Keeping package " + packageName + " - requested by DO");
}
} else {
for (int i = 0; i < users.length; i++) {
if (users[i] != userId && ps.getInstalled(users[i])) {
keep = true;
if (DEBUG_CLEAN_APKS) {
Slog.i(TAG, " Keeping package " + packageName + " for user "
+ users[i]);
}
break;
}
}
}
if (!keep) {
if (DEBUG_CLEAN_APKS) {
Slog.i(TAG, " Removing package " + packageName);
}
//end run
mHandler.post(() -> deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
userId, 0));
}
}
}
/**
* Called by UserManagerService.
*
* @param userTypeInstallablePackages system packages that should be initially installed for
* this type of user, or {@code null} if all system packages
* should be installed
* @param disallowedPackages packages that should not be initially installed. Takes precedence
* over installablePackages.
*/
void createNewUser(int userId, @Nullable Set<String> userTypeInstallablePackages,
String[] disallowedPackages) {
synchronized (mInstallLock) {
mSettings.createNewUserLI(this, mInstaller, userId,
userTypeInstallablePackages, disallowedPackages);
}
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(userId);
scheduleWritePackageListLocked(userId);
primeDomainVerificationsLPw(userId);
mAppsFilter.onUsersChanged();
}
}
void onNewUserCreated(final int userId) {
mPermissionManager.onNewUserCreated(userId);
}
boolean readPermissionStateForUser(@UserIdInt int userId) {
synchronized (mPackages) {
mSettings.readPermissionStateForUserSyncLPr(userId);
return mPmInternal.isPermissionUpgradeNeeded(userId);
}
}
@Override
public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
"Only package verification agents can read the verifier device identity");
synchronized (mLock) {
return mSettings.getVerifierDeviceIdentityLPw();
}
}
@Override
public boolean isStorageLow() {
// allow instant applications
final long token = Binder.clearCallingIdentity();
try {
final DeviceStorageMonitorInternal
dsm = mInjector.getDeviceStorageMonitorInternal();
if (dsm != null) {
return dsm.isMemoryLow();
} else {
return false;
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public IPackageInstaller getPackageInstaller() {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
return mInstallerService;
}
@Override
public IArtManager getArtManager() {
return mArtManagerService;
}
private boolean userNeedsBadging(int userId) {
int index = mUserNeedsBadging.indexOfKey(userId);
if (index < 0) {
final UserInfo userInfo;
final long token = Binder.clearCallingIdentity();
try {
userInfo = mUserManager.getUserInfo(userId);
} finally {
Binder.restoreCallingIdentity(token);
}
final boolean b;
if (userInfo != null && userInfo.isManagedProfile()) {
b = true;
} else {
b = false;
}
mUserNeedsBadging.put(userId, b);
return b;
}
return mUserNeedsBadging.valueAt(index);
}
@Override
public KeySet getKeySetByAlias(String packageName, String alias) {
if (packageName == null || alias == null) {
return null;
}
synchronized(mLock) {
final AndroidPackage pkg = mPackages.get(packageName);
if (pkg == null) {
Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
throw new IllegalArgumentException("Unknown package: " + packageName);
}
final PackageSetting ps = getPackageSetting(pkg.getPackageName());
if (shouldFilterApplicationLocked(
ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
Slog.w(TAG, "KeySet requested for filtered package: " + packageName);
throw new IllegalArgumentException("Unknown package: " + packageName);
}
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
return new KeySet(ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias));
}
}
@Override
public KeySet getSigningKeySet(String packageName) {
if (packageName == null) {
return null;
}
synchronized (mLock) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final AndroidPackage pkg = mPackages.get(packageName);
if (pkg == null) {
Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
throw new IllegalArgumentException("Unknown package: " + packageName);
}
final PackageSetting ps = getPackageSetting(pkg.getPackageName());
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
// filter and pretend the package doesn't exist
Slog.w(TAG, "KeySet requested for filtered package: " + packageName
+ ", uid:" + callingUid);
throw new IllegalArgumentException("Unknown package: " + packageName);
}
if (pkg.getUid() != callingUid
&& Process.SYSTEM_UID != callingUid) {
throw new SecurityException("May not access signing KeySet of other apps.");
}
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
return new KeySet(ksms.getSigningKeySetByPackageNameLPr(packageName));
}
}
@Override
public boolean isPackageSignedByKeySet(String packageName, KeySet ks) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return false;
}
if (packageName == null || ks == null) {
return false;
}
synchronized(mLock) {
final AndroidPackage pkg = mPackages.get(packageName);
if (pkg == null
|| shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()),
callingUid, UserHandle.getUserId(callingUid))) {
Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
throw new IllegalArgumentException("Unknown package: " + packageName);
}
IBinder ksh = ks.getToken();
if (ksh instanceof KeySetHandle) {
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
return ksms.packageIsSignedByLPr(packageName, (KeySetHandle) ksh);
}
return false;
}
}
@Override
public boolean isPackageSignedByKeySetExactly(String packageName, KeySet ks) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return false;
}
if (packageName == null || ks == null) {
return false;
}
synchronized (mLock) {
final AndroidPackage pkg = mPackages.get(packageName);
if (pkg == null
|| shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()),
callingUid, UserHandle.getUserId(callingUid))) {
Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
throw new IllegalArgumentException("Unknown package: " + packageName);
}
IBinder ksh = ks.getToken();
if (ksh instanceof KeySetHandle) {
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
return ksms.packageIsSignedByExactlyLPr(packageName, (KeySetHandle) ksh);
}
return false;
}
}
@GuardedBy("mLock")
private void deletePackageIfUnusedLPr(final String packageName) {
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) {
return;
}
if (!ps.isAnyInstalled(mUserManager.getUserIds())) {
// TODO Implement atomic delete if package is unused
// It is currently possible that the package will be deleted even if it is installed
// after this method returns.
mHandler.post(() -> deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
0, PackageManager.DELETE_ALL_USERS));
}
}
/**
* Check and throw if the given before/after packages would be considered a
* downgrade.
*/
private static void checkDowngrade(AndroidPackage before, PackageInfoLite after)
throws PackageManagerException {
if (after.getLongVersionCode() < before.getLongVersionCode()) {
throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
"Update version code " + after.versionCode + " is older than current "
+ before.getLongVersionCode());
} else if (after.getLongVersionCode() == before.getLongVersionCode()) {
if (after.baseRevisionCode < before.getBaseRevisionCode()) {
throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
"Update base revision code " + after.baseRevisionCode
+ " is older than current " + before.getBaseRevisionCode());
}
if (!ArrayUtils.isEmpty(after.splitNames)) {
for (int i = 0; i < after.splitNames.length; i++) {
final String splitName = after.splitNames[i];
final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
if (j != -1) {
if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
"Update split " + splitName + " revision code "
+ after.splitRevisionCodes[i] + " is older than current "
+ before.getSplitRevisionCodes()[j]);
}
}
}
}
}
}
private static class MoveCallbacks extends Handler {
private static final int MSG_CREATED = 1;
private static final int MSG_STATUS_CHANGED = 2;
private final RemoteCallbackList<IPackageMoveObserver>
mCallbacks = new RemoteCallbackList<>();
private final SparseIntArray mLastStatus = new SparseIntArray();
public MoveCallbacks(Looper looper) {
super(looper);
}
public void register(IPackageMoveObserver callback) {
mCallbacks.register(callback);
}
public void unregister(IPackageMoveObserver callback) {
mCallbacks.unregister(callback);
}
@Override
public void handleMessage(Message msg) {
final SomeArgs args = (SomeArgs) msg.obj;
final int n = mCallbacks.beginBroadcast();
for (int i = 0; i < n; i++) {
final IPackageMoveObserver callback = mCallbacks.getBroadcastItem(i);
try {
invokeCallback(callback, msg.what, args);
} catch (RemoteException ignored) {
}
}
mCallbacks.finishBroadcast();
args.recycle();
}
private void invokeCallback(IPackageMoveObserver callback, int what, SomeArgs args)
throws RemoteException {
switch (what) {
case MSG_CREATED: {
callback.onCreated(args.argi1, (Bundle) args.arg2);
break;
}
case MSG_STATUS_CHANGED: {
callback.onStatusChanged(args.argi1, args.argi2, (long) args.arg3);
break;
}
}
}
private void notifyCreated(int moveId, Bundle extras) {
Slog.v(TAG, "Move " + moveId + " created " + extras.toString());
final SomeArgs args = SomeArgs.obtain();
args.argi1 = moveId;
args.arg2 = extras;
obtainMessage(MSG_CREATED, args).sendToTarget();
}
private void notifyStatusChanged(int moveId, int status) {
notifyStatusChanged(moveId, status, -1);
}
private void notifyStatusChanged(int moveId, int status, long estMillis) {
Slog.v(TAG, "Move " + moveId + " status " + status);
final SomeArgs args = SomeArgs.obtain();
args.argi1 = moveId;
args.argi2 = status;
args.arg3 = estMillis;
obtainMessage(MSG_STATUS_CHANGED, args).sendToTarget();
synchronized (mLastStatus) {
mLastStatus.put(moveId, status);
}
}
}
private final class PackageChangeObserverDeathRecipient implements IBinder.DeathRecipient {
private final IPackageChangeObserver mObserver;
PackageChangeObserverDeathRecipient(IPackageChangeObserver observer) {
mObserver = observer;
}
@Override
public void binderDied() {
synchronized (mPackageChangeObservers) {
mPackageChangeObservers.remove(mObserver);
Log.d(TAG, "Size of mPackageChangeObservers after removing dead observer is "
+ mPackageChangeObservers.size());
}
}
}
private class PackageManagerNative extends IPackageManagerNative.Stub {
@Override
public void registerPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
synchronized (mPackageChangeObservers) {
try {
observer.asBinder().linkToDeath(
new PackageChangeObserverDeathRecipient(observer), 0);
} catch (RemoteException e) {
Log.e(TAG, e.getMessage());
}
mPackageChangeObservers.add(observer);
Log.d(TAG, "Size of mPackageChangeObservers after registry is "
+ mPackageChangeObservers.size());
}
}
@Override
public void unregisterPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
synchronized (mPackageChangeObservers) {
mPackageChangeObservers.remove(observer);
Log.d(TAG, "Size of mPackageChangeObservers after unregistry is "
+ mPackageChangeObservers.size());
}
}
@Override
public String[] getAllPackages() {
return PackageManagerService.this.getAllPackages().toArray(new String[0]);
}
@Override
public String[] getNamesForUids(int[] uids) throws RemoteException {
final String[] results = PackageManagerService.this.getNamesForUids(uids);
// massage results so they can be parsed by the native binder
for (int i = results.length - 1; i >= 0; --i) {
if (results[i] == null) {
results[i] = "";
}
}
return results;
}
// NB: this differentiates between preloads and sideloads
@Override
public String getInstallerForPackage(String packageName) throws RemoteException {
final String installerName = getInstallerPackageName(packageName);
if (!TextUtils.isEmpty(installerName)) {
return installerName;
}
// differentiate between preload and sideload
int callingUser = UserHandle.getUserId(Binder.getCallingUid());
ApplicationInfo appInfo = getApplicationInfo(packageName,
/*flags*/ 0,
/*userId*/ callingUser);
if (appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
return "preload";
}
return "";
}
@Override
public long getVersionCodeForPackage(String packageName) throws RemoteException {
try {
int callingUser = UserHandle.getUserId(Binder.getCallingUid());
PackageInfo pInfo = getPackageInfo(packageName, 0, callingUser);
if (pInfo != null) {
return pInfo.getLongVersionCode();
}
} catch (Exception e) {
}
return 0;
}
@Override
public int getTargetSdkVersionForPackage(String packageName)
throws RemoteException {
int callingUser = UserHandle.getUserId(Binder.getCallingUid());
ApplicationInfo info = getApplicationInfo(packageName, 0, callingUser);
if (info == null) {
throw new RemoteException(
"Couldn't get ApplicationInfo for package " + packageName);
}
return info.targetSdkVersion;
}
@Override
public boolean[] isAudioPlaybackCaptureAllowed(String[] packageNames)
throws RemoteException {
int callingUser = UserHandle.getUserId(Binder.getCallingUid());
boolean[] results = new boolean[packageNames.length];
for (int i = results.length - 1; i >= 0; --i) {
ApplicationInfo appInfo = getApplicationInfo(packageNames[i], 0, callingUser);
results[i] = appInfo == null ? false : appInfo.isAudioPlaybackCaptureAllowed();
}
return results;
}
@Override
public int getLocationFlags(String packageName) throws RemoteException {
int callingUser = UserHandle.getUserId(Binder.getCallingUid());
ApplicationInfo appInfo = getApplicationInfo(packageName,
/*flags*/ 0,
/*userId*/ callingUser);
if (appInfo == null) {
throw new RemoteException(
"Couldn't get ApplicationInfo for package " + packageName);
}
return ((appInfo.isSystemApp() ? IPackageManagerNative.LOCATION_SYSTEM : 0)
| (appInfo.isVendor() ? IPackageManagerNative.LOCATION_VENDOR : 0)
| (appInfo.isProduct() ? IPackageManagerNative.LOCATION_PRODUCT : 0));
}
@Override
public String getModuleMetadataPackageName() throws RemoteException {
return PackageManagerService.this.mModuleInfoProvider.getPackageName();
}
}
private class PackageManagerInternalImpl extends PackageManagerInternal {
@Override
public List<ApplicationInfo> getInstalledApplications(int flags, int userId,
int callingUid) {
return PackageManagerService.this.getInstalledApplicationsListInternal(flags, userId,
callingUid);
}
@Override
public boolean isPlatformSigned(String packageName) {
PackageSetting packageSetting = mSettings.mPackages.get(packageName);
if (packageSetting == null) {
return false;
}
AndroidPackage pkg = packageSetting.pkg;
if (pkg == null) {
// May happen if package in on a removable sd card
return false;
}
return pkg.getSigningDetails().hasAncestorOrSelf(mPlatformPackage.getSigningDetails())
|| mPlatformPackage.getSigningDetails().checkCapability(pkg.getSigningDetails(),
PackageParser.SigningDetails.CertCapabilities.PERMISSION);
}
@Override
public boolean isDataRestoreSafe(byte[] restoringFromSigHash, String packageName) {
SigningDetails sd = getSigningDetails(packageName);
if (sd == null) {
return false;
}
return sd.hasSha256Certificate(restoringFromSigHash,
SigningDetails.CertCapabilities.INSTALLED_DATA);
}
@Override
public boolean isDataRestoreSafe(Signature restoringFromSig, String packageName) {
SigningDetails sd = getSigningDetails(packageName);
if (sd == null) {
return false;
}
return sd.hasCertificate(restoringFromSig,
SigningDetails.CertCapabilities.INSTALLED_DATA);
}
@Override
public boolean hasSignatureCapability(int serverUid, int clientUid,
@SigningDetails.CertCapabilities int capability) {
SigningDetails serverSigningDetails = getSigningDetails(serverUid);
SigningDetails clientSigningDetails = getSigningDetails(clientUid);
return serverSigningDetails.checkCapability(clientSigningDetails, capability)
|| clientSigningDetails.hasAncestorOrSelf(serverSigningDetails);
}
private SigningDetails getSigningDetails(@NonNull String packageName) {
synchronized (mLock) {
AndroidPackage p = mPackages.get(packageName);
if (p == null) {
return null;
}
return p.getSigningDetails();
}
}
private SigningDetails getSigningDetails(int uid) {
synchronized (mLock) {
final int appId = UserHandle.getAppId(uid);
final Object obj = mSettings.getSettingLPr(appId);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
return ((SharedUserSetting) obj).signatures.mSigningDetails;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
return ps.signatures.mSigningDetails;
}
}
return SigningDetails.UNKNOWN;
}
}
@Override
public boolean isInstantApp(String packageName, int userId) {
return PackageManagerService.this.isInstantApp(packageName, userId);
}
@Override
public String getInstantAppPackageName(int uid) {
return PackageManagerService.this.getInstantAppPackageName(uid);
}
@Override
public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
synchronized (mLock) {
PackageSetting ps = getPackageSetting(pkg.getPackageName());
return PackageManagerService.this.shouldFilterApplicationLocked(ps, callingUid,
userId);
}
}
@Override
public boolean filterAppAccess(String packageName, int callingUid, int userId) {
synchronized (mLock) {
PackageSetting ps = getPackageSetting(packageName);
return PackageManagerService.this.shouldFilterApplicationLocked(ps, callingUid,
userId);
}
}
@Override
public AndroidPackage getPackage(String packageName) {
synchronized (mLock) {
packageName = resolveInternalPackageNameLPr(
packageName, PackageManager.VERSION_CODE_HIGHEST);
return mPackages.get(packageName);
}
}
@Override
public AndroidPackage getPackage(int uid) {
synchronized (mLock) {
final String[] packageNames = getPackagesForUidInternal(uid, Process.SYSTEM_UID);
AndroidPackage pkg = null;
final int numPackages = packageNames == null ? 0 : packageNames.length;
for (int i = 0; pkg == null && i < numPackages; i++) {
pkg = mPackages.get(packageNames[i]);
}
return pkg;
}
}
@Nullable
@Override
public PackageSetting getPackageSetting(String packageName) {
return PackageManagerService.this.getPackageSetting(packageName);
}
@Override
public PackageList getPackageList(PackageListObserver observer) {
synchronized (mLock) {
final int N = mPackages.size();
final ArrayList<String> list = new ArrayList<>(N);
for (int i = 0; i < N; i++) {
list.add(mPackages.keyAt(i));
}
final PackageList packageList = new PackageList(list, observer);
if (observer != null) {
mPackageListObservers.add(packageList);
}
return packageList;
}
}
@Override
public void removePackageListObserver(PackageListObserver observer) {
synchronized (mLock) {
mPackageListObservers.remove(observer);
}
}
@Override
public PackageSetting getDisabledSystemPackage(@NonNull String packageName) {
synchronized (mLock) {
return mSettings.getDisabledSystemPkgLPr(packageName);
}
}
@Override
public @Nullable
String getDisabledSystemPackageName(@NonNull String packageName) {
PackageSetting disabledPkgSetting = (PackageSetting) getDisabledSystemPackage(
packageName);
AndroidPackage disabledPkg = disabledPkgSetting == null ? null : disabledPkgSetting.pkg;
return disabledPkg == null ? null : disabledPkg.getPackageName();
}
/**
* Only keep package names that refer to {@link PackageParser.Package#isSystem system}
* packages.
*
* @param pkgNames The packages to filter
*
* @return The filtered packages
*/
private @NonNull String[] filterOnlySystemPackages(@Nullable String... pkgNames) {
if (pkgNames == null) {
return ArrayUtils.emptyArray(String.class);
}
ArrayList<String> systemPackageNames = new ArrayList<>(pkgNames.length);
for (String pkgName: pkgNames) {
synchronized (mLock) {
if (pkgName == null) {
continue;
}
AndroidPackage pkg = getPackage(pkgName);
if (pkg == null) {
Log.w(TAG, "Could not find package " + pkgName);
continue;
}
if (!pkg.isSystem()) {
Log.w(TAG, pkgName + " is not system");
continue;
}
systemPackageNames.add(pkgName);
}
}
return systemPackageNames.toArray(new String[]{});
}
@Override
public @NonNull String[] getKnownPackageNames(int knownPackage, int userId) {
return dropNonSystemPackages(getKnownPackageNamesInternal(knownPackage, userId));
}
private String[] getKnownPackageNamesInternal(int knownPackage, int userId) {
switch (knownPackage) {
case PackageManagerInternal.PACKAGE_BROWSER:
return new String[]{mPermissionManager.getDefaultBrowser(userId)};
case PackageManagerInternal.PACKAGE_INSTALLER:
return filterOnlySystemPackages(mRequiredInstallerPackage);
case PackageManagerInternal.PACKAGE_SETUP_WIZARD:
return filterOnlySystemPackages(mSetupWizardPackage);
case PackageManagerInternal.PACKAGE_SYSTEM:
return new String[]{"android"};
case PackageManagerInternal.PACKAGE_VERIFIER:
return filterOnlySystemPackages(mRequiredVerifierPackage);
case PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER:
return filterOnlySystemPackages(
mDefaultTextClassifierPackage, mSystemTextClassifierPackageName);
case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
return filterOnlySystemPackages(mRequiredPermissionControllerPackage);
case PackageManagerInternal.PACKAGE_WELLBEING:
return filterOnlySystemPackages(mWellbeingPackage);
case PackageManagerInternal.PACKAGE_DOCUMENTER:
return filterOnlySystemPackages(mDocumenterPackage);
case PackageManagerInternal.PACKAGE_CONFIGURATOR:
return filterOnlySystemPackages(mConfiguratorPackage);
case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
return filterOnlySystemPackages(mIncidentReportApproverPackage);
case PackageManagerInternal.PACKAGE_APP_PREDICTOR:
return filterOnlySystemPackages(mAppPredictionServicePackage);
case PackageManagerInternal.PACKAGE_COMPANION:
return filterOnlySystemPackages("com.android.companiondevicemanager");
case PackageManagerInternal.PACKAGE_RETAIL_DEMO:
return TextUtils.isEmpty(mRetailDemoPackage)
? ArrayUtils.emptyArray(String.class)
: new String[] {mRetailDemoPackage};
default:
return ArrayUtils.emptyArray(String.class);
}
}
@Override
public boolean isResolveActivityComponent(ComponentInfo component) {
return mResolveActivity.packageName.equals(component.packageName)
&& mResolveActivity.name.equals(component.name);
}
@Override
public void setKeepUninstalledPackages(final List<String> packageList) {
Preconditions.checkNotNull(packageList);
List<String> removedFromList = null;
synchronized (mLock) {
if (mKeepUninstalledPackages != null) {
final int packagesCount = mKeepUninstalledPackages.size();
for (int i = 0; i < packagesCount; i++) {
String oldPackage = mKeepUninstalledPackages.get(i);
if (packageList != null && packageList.contains(oldPackage)) {
continue;
}
if (removedFromList == null) {
removedFromList = new ArrayList<>();
}
removedFromList.add(oldPackage);
}
}
mKeepUninstalledPackages = new ArrayList<>(packageList);
if (removedFromList != null) {
final int removedCount = removedFromList.size();
for (int i = 0; i < removedCount; i++) {
deletePackageIfUnusedLPr(removedFromList.get(i));
}
}
}
}
@Override
public boolean isPermissionsReviewRequired(String packageName, int userId) {
synchronized (mLock) {
final AndroidPackage pkg = mPackages.get(packageName);
if (pkg == null) {
return false;
}
return mPermissionManager.isPermissionsReviewRequired(pkg, userId);
}
}
@Override
public PackageInfo getPackageInfo(
String packageName, int flags, int filterCallingUid, int userId) {
return PackageManagerService.this
.getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
flags, filterCallingUid, userId);
}
@Override
public long getCeDataInode(String packageName, int userId) {
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
return ps.getCeDataInode(userId);
}
return 0;
}
}
@Override
public Bundle getSuspendedPackageLauncherExtras(String packageName, int userId) {
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
final Bundle allExtras = new Bundle();
if (ps != null) {
final PackageUserState pus = ps.readUserState(userId);
if (pus.suspended) {
for (int i = 0; i < pus.suspendParams.size(); i++) {
final PackageUserState.SuspendParams params =
pus.suspendParams.valueAt(i);
if (params != null && params.launcherExtras != null) {
allExtras.putAll(params.launcherExtras);
}
}
}
}
return (allExtras.size() > 0) ? allExtras : null;
}
}
@Override
public boolean isPackageSuspended(String packageName, int userId) {
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
return (ps != null) ? ps.getSuspended(userId) : false;
}
}
@Override
public void removeAllNonSystemPackageSuspensions(int userId) {
final String[] allPackages;
synchronized (mLock) {
allPackages = mPackages.keySet().toArray(new String[mPackages.size()]);
}
PackageManagerService.this.removeSuspensionsBySuspendingPackage(allPackages,
(suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage),
userId);
}
@Override
public void removeNonSystemPackageSuspensions(String packageName, int userId) {
PackageManagerService.this.removeSuspensionsBySuspendingPackage(
new String[]{packageName},
(suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage),
userId);
}
@Override
public void flushPackageRestrictions(int userId) {
synchronized (mLock) {
PackageManagerService.this.flushPackageRestrictionsAsUserInternalLocked(userId);
}
}
@Override
public void removeDistractingPackageRestrictions(String packageName, int userId) {
PackageManagerService.this.removeDistractingPackageRestrictions(
new String[]{packageName}, userId);
}
@Override
public void removeAllDistractingPackageRestrictions(int userId) {
PackageManagerService.this.removeAllDistractingPackageRestrictions(userId);
}
@Override
public String getSuspendingPackage(String suspendedPackage, int userId) {
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
if (ps != null) {
final PackageUserState pus = ps.readUserState(userId);
if (pus.suspended) {
String suspendingPackage = null;
for (int i = 0; i < pus.suspendParams.size(); i++) {
suspendingPackage = pus.suspendParams.keyAt(i);
if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
return suspendingPackage;
}
}
return suspendingPackage;
}
}
return null;
}
}
@Override
public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage,
String suspendingPackage, int userId) {
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
if (ps != null) {
final PackageUserState pus = ps.readUserState(userId);
if (pus.suspended) {
final PackageUserState.SuspendParams suspendParams =
pus.suspendParams.get(suspendingPackage);
return (suspendParams != null) ? suspendParams.dialogInfo : null;
}
}
}
return null;
}
@Override
public int getDistractingPackageRestrictions(String packageName, int userId) {
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
return (ps != null) ? ps.getDistractionFlags(userId) : RESTRICTION_NONE;
}
}
@Override
public int getPackageUid(String packageName, int flags, int userId) {
return PackageManagerService.this
.getPackageUid(packageName, flags, userId);
}
@Override
public int getPackageUidInternal(String packageName, int flags, int userId) {
return PackageManagerService.this
.getPackageUidInternal(packageName, flags, userId, Process.SYSTEM_UID);
}
@Override
public ApplicationInfo getApplicationInfo(
String packageName, int flags, int filterCallingUid, int userId) {
return PackageManagerService.this
.getApplicationInfoInternal(packageName, flags, filterCallingUid, userId);
}
@Override
public ActivityInfo getActivityInfo(
ComponentName component, int flags, int filterCallingUid, int userId) {
return PackageManagerService.this
.getActivityInfoInternal(component, flags, filterCallingUid, userId);
}
@Override
public List<ResolveInfo> queryIntentActivities(
Intent intent, String resolvedType, int flags, int filterCallingUid, int userId) {
return PackageManagerService.this
.queryIntentActivitiesInternal(intent, resolvedType, flags, 0, filterCallingUid,
userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
}
@Override
public List<ResolveInfo> queryIntentServices(
Intent intent, int flags, int callingUid, int userId) {
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
return PackageManagerService.this
.queryIntentServicesInternal(intent, resolvedType, flags, userId, callingUid,
false);
}
@Override
public ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
int userId) {
return PackageManagerService.this.getHomeActivitiesAsUser(allHomeCandidates, userId);
}
@Override
public ComponentName getDefaultHomeActivity(int userId) {
return PackageManagerService.this.getDefaultHomeActivity(userId);
}
@Override
public ComponentName getSystemUiServiceComponent() {
return ComponentName.unflattenFromString(mContext.getResources().getString(
com.android.internal.R.string.config_systemUIServiceComponent));
}
@Override
public void setDeviceAndProfileOwnerPackages(
int deviceOwnerUserId, String deviceOwnerPackage,
SparseArray<String> profileOwnerPackages) {
mProtectedPackages.setDeviceAndProfileOwnerPackages(
deviceOwnerUserId, deviceOwnerPackage, profileOwnerPackages);
final ArraySet<Integer> usersWithPoOrDo = new ArraySet<>();
if (deviceOwnerPackage != null) {
usersWithPoOrDo.add(deviceOwnerUserId);
}
final int sz = profileOwnerPackages.size();
for (int i = 0; i < sz; i++) {
if (profileOwnerPackages.valueAt(i) != null) {
usersWithPoOrDo.add(profileOwnerPackages.keyAt(i));
}
}
}
@Override
public void setDeviceOwnerProtectedPackages(List<String> packageNames) {
mProtectedPackages.setDeviceOwnerProtectedPackages(packageNames);
}
@Override
public boolean isPackageDataProtected(int userId, String packageName) {
return mProtectedPackages.isPackageDataProtected(userId, packageName);
}
@Override
public boolean isPackageStateProtected(String packageName, int userId) {
return mProtectedPackages.isPackageStateProtected(userId, packageName);
}
@Override
public boolean isPackageEphemeral(int userId, String packageName) {
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
return ps != null ? ps.getInstantApp(userId) : false;
}
}
@Override
public boolean wasPackageEverLaunched(String packageName, int userId) {
synchronized (mLock) {
return mSettings.wasPackageEverLaunchedLPr(packageName, userId);
}
}
@Override
public boolean isEnabledAndMatches(ParsedMainComponent component, int flags, int userId) {
synchronized (mLock) {
AndroidPackage pkg = getPackage(component.getPackageName());
return mSettings.isEnabledAndMatchLPr(pkg, component, flags, userId);
}
}
@Override
public boolean userNeedsBadging(int userId) {
synchronized (mLock) {
return PackageManagerService.this.userNeedsBadging(userId);
}
}
@Override
public String getNameForUid(int uid) {
return PackageManagerService.this.getNameForUid(uid);
}
@Override
public void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
Intent origIntent, String resolvedType, String callingPackage,
@Nullable String callingFeatureId, boolean isRequesterInstantApp,
Bundle verificationBundle, int userId) {
PackageManagerService.this.requestInstantAppResolutionPhaseTwo(responseObj, origIntent,
resolvedType, callingPackage, callingFeatureId, isRequesterInstantApp,
verificationBundle, userId);
}
@Override
public void grantImplicitAccess(int userId, Intent intent,
int recipientAppId, int visibleUid, boolean direct) {
synchronized (mLock) {
final AndroidPackage visiblePackage = getPackage(visibleUid);
final int recipientUid = UserHandle.getUid(userId, recipientAppId);
if (visiblePackage == null || getPackage(recipientUid) == null) {
return;
}
final boolean instantApp =
isInstantAppInternal(visiblePackage.getPackageName(), userId, visibleUid);
if (instantApp) {
if (!direct) {
// if the interaction that lead to this granting access to an instant app
// was indirect (i.e.: URI permission grant), do not actually execute the
// grant.
return;
}
mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
recipientAppId, UserHandle.getAppId(visibleUid) /*instantAppId*/);
} else {
mAppsFilter.grantImplicitAccess(recipientUid, visibleUid);
}
}
}
@Override
public boolean isInstantAppInstallerComponent(ComponentName component) {
synchronized (mLock) {
return mInstantAppInstallerActivity != null
&& mInstantAppInstallerActivity.getComponentName().equals(component);
}
}
@Override
public void pruneInstantApps() {
mInstantAppRegistry.pruneInstantApps();
}
@Override
public void pruneCachedApksInApex(@NonNull List<PackageInfo> apexPackages) {
if (mCacheDir == null) {
return;
}
final PackageCacher cacher = new PackageCacher(mCacheDir);
synchronized (mLock) {
for (int i = 0, size = apexPackages.size(); i < size; i++) {
final List<String> apkNames =
mApexManager.getApksInApex(apexPackages.get(i).packageName);
for (int j = 0, apksInApex = apkNames.size(); j < apksInApex; j++) {
final AndroidPackage pkg = getPackage(apkNames.get(j));
cacher.cleanCachedResult(new File(pkg.getCodePath()));
}
}
}
}
@Override
public String getSetupWizardPackageName() {
return mSetupWizardPackage;
}
public void setExternalSourcesPolicy(ExternalSourcesPolicy policy) {
if (policy != null) {
mExternalSourcesPolicy = policy;
}
}
@Override
public boolean isPackagePersistent(String packageName) {
synchronized (mLock) {
AndroidPackage pkg = mPackages.get(packageName);
return pkg != null && pkg.isSystem() && pkg.isPersistent();
}
}
@Override
public List<PackageInfo> getOverlayPackages(int userId) {
final ArrayList<PackageInfo> overlayPackages = new ArrayList<PackageInfo>();
synchronized (mLock) {
for (AndroidPackage p : mPackages.values()) {
if (p.getOverlayTarget() != null) {
PackageInfo pkg = generatePackageInfo(getPackageSetting(p.getPackageName()),
0, userId);
if (pkg != null) {
overlayPackages.add(pkg);
}
}
}
}
return overlayPackages;
}
@Override
public List<String> getTargetPackageNames(int userId) {
List<String> targetPackages = new ArrayList<>();
synchronized (mLock) {
for (AndroidPackage p : mPackages.values()) {
if (p.getOverlayTarget() == null) {
targetPackages.add(p.getPackageName());
}
}
}
return targetPackages;
}
@Override
public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName,
@Nullable List<String> overlayPackageNames,
@NonNull Collection<String> outUpdatedPackageNames) {
synchronized (mLock) {
final AndroidPackage targetPkg = mPackages.get(targetPackageName);
if (targetPackageName == null || targetPkg == null) {
Slog.e(TAG, "failed to find package " + targetPackageName);
return false;
}
ArrayList<String> overlayPaths = null;
if (overlayPackageNames != null && overlayPackageNames.size() > 0) {
final int N = overlayPackageNames.size();
overlayPaths = new ArrayList<>(N);
for (int i = 0; i < N; i++) {
final String packageName = overlayPackageNames.get(i);
final AndroidPackage pkg = mPackages.get(packageName);
if (pkg == null) {
Slog.e(TAG, "failed to find package " + packageName);
return false;
}
overlayPaths.add(pkg.getBaseCodePath());
}
}
ArraySet<String> updatedPackageNames = null;
if (targetPkg.getLibraryNames() != null) {
// Set the overlay paths for dependencies of the shared library.
updatedPackageNames = new ArraySet<>();
for (String libName : targetPkg.getLibraryNames()) {
final SharedLibraryInfo info = getSharedLibraryInfoLPr(libName,
SharedLibraryInfo.VERSION_UNDEFINED);
if (info == null) {
continue;
}
final List<VersionedPackage> dependents = getPackagesUsingSharedLibraryLPr(
info, 0, userId);
if (dependents == null) {
continue;
}
for (VersionedPackage dependent : dependents) {
final PackageSetting ps = mSettings.mPackages.get(
dependent.getPackageName());
if (ps == null) {
continue;
}
ps.setOverlayPathsForLibrary(libName, overlayPaths, userId);
updatedPackageNames.add(dependent.getPackageName());
}
}
}
final PackageSetting ps = mSettings.mPackages.get(targetPackageName);
ps.setOverlayPaths(overlayPaths, userId);
outUpdatedPackageNames.add(targetPackageName);
if (updatedPackageNames != null) {
outUpdatedPackageNames.addAll(updatedPackageNames);
}
}
PackageManager.invalidatePackageInfoCache();
return true;
}
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int privateResolveFlags, int userId, boolean resolveForStart,
int filterCallingUid) {
return resolveIntentInternal(
intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart,
filterCallingUid);
}
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType,
int flags, int userId, int callingUid) {
return resolveServiceInternal(intent, resolvedType, flags, userId, callingUid);
}
@Override
public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
return PackageManagerService.this.resolveContentProviderInternal(
name, flags, userId);
}
@Override
public void addIsolatedUid(int isolatedUid, int ownerUid) {
synchronized (mLock) {
mIsolatedOwners.put(isolatedUid, ownerUid);
}
}
@Override
public void removeIsolatedUid(int isolatedUid) {
synchronized (mLock) {
mIsolatedOwners.delete(isolatedUid);
}
}
@Override
public int getUidTargetSdkVersion(int uid) {
synchronized (mLock) {
return getUidTargetSdkVersionLockedLPr(uid);
}
}
@Override
public int getPackageTargetSdkVersion(String packageName) {
synchronized (mLock) {
return getPackageTargetSdkVersionLockedLPr(packageName);
}
}
@Override
public boolean canAccessInstantApps(int callingUid, int userId) {
return PackageManagerService.this.canViewInstantApps(callingUid, userId);
}
@Override
public boolean canAccessComponent(int callingUid, ComponentName component, int userId) {
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
return ps != null && !PackageManagerService.this.shouldFilterApplicationLocked(
ps, callingUid, component, TYPE_UNKNOWN, userId);
}
}
@Override
public boolean hasInstantApplicationMetadata(String packageName, int userId) {
synchronized (mLock) {
return mInstantAppRegistry.hasInstantApplicationMetadataLPr(packageName, userId);
}
}
@Override
public void notifyPackageUse(String packageName, int reason) {
synchronized (mLock) {
PackageManagerService.this.notifyPackageUseLocked(packageName, reason);
}
}
@Override
public SparseArray<String> getAppsWithSharedUserIds() {
synchronized (mLock) {
return getAppsWithSharedUserIdsLocked();
}
}
@Override
@NonNull
public String[] getSharedUserPackagesForPackage(String packageName, int userId) {
synchronized (mLock) {
return getSharedUserPackagesForPackageLocked(packageName, userId);
}
}
@Override
public ArrayMap<String, ProcessInfo> getProcessesForUid(int uid) {
synchronized (mLock) {
return getProcessesForUidLocked(uid);
}
}
@Override
public int[] getPermissionGids(String permissionName, int userId) {
synchronized (mLock) {
return getPermissionGidsLocked(permissionName, userId);
}
}
@Override
public boolean isOnlyCoreApps() {
return PackageManagerService.this.isOnlyCoreApps();
}
@Override
public void freeStorage(String volumeUuid, long bytes, int storageFlags)
throws IOException {
PackageManagerService.this.freeStorage(volumeUuid, bytes, storageFlags);
}
@Override
public void forEachPackage(Consumer<AndroidPackage> actionLocked) {
PackageManagerService.this.forEachPackage(actionLocked);
}
@Override
public void forEachPackageSetting(Consumer<PackageSetting> actionLocked) {
synchronized (mLock) {
for (int index = 0; index < mSettings.mPackages.size(); index++) {
actionLocked.accept(mSettings.mPackages.valueAt(index));
}
}
}
@Override
public void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> actionLocked,
@UserIdInt int userId) {
PackageManagerService.this.forEachInstalledPackage(actionLocked, userId);
}
@Override
public ArraySet<String> getEnabledComponents(String packageName, int userId) {
synchronized (mLock) {
PackageSetting setting = mSettings.getPackageLPr(packageName);
if (setting == null) {
return new ArraySet<>();
}
return setting.getEnabledComponents(userId);
}
}
@Override
public ArraySet<String> getDisabledComponents(String packageName, int userId) {
synchronized (mLock) {
PackageSetting setting = mSettings.getPackageLPr(packageName);
if (setting == null) {
return new ArraySet<>();
}
return setting.getDisabledComponents(userId);
}
}
@Override
public @PackageManager.EnabledState int getApplicationEnabledState(
String packageName, int userId) {
synchronized (mLock) {
PackageSetting setting = mSettings.getPackageLPr(packageName);
if (setting == null) {
return COMPONENT_ENABLED_STATE_DEFAULT;
}
return setting.getEnabled(userId);
}
}
@Override
public void setEnableRollbackCode(int token, int enableRollbackCode) {
PackageManagerService.this.setEnableRollbackCode(token, enableRollbackCode);
}
/**
* Ask the package manager to compile layouts in the given package.
*/
@Override
public boolean compileLayouts(String packageName) {
AndroidPackage pkg;
synchronized (mLock) {
pkg = mPackages.get(packageName);
if (pkg == null) {
return false;
}
}
return mArtManagerService.compileLayouts(pkg);
}
@Override
public void finishPackageInstall(int token, boolean didLaunch) {
PackageManagerService.this.finishPackageInstall(token, didLaunch);
}
@Nullable
@Override
public String removeLegacyDefaultBrowserPackageName(int userId) {
synchronized (mLock) {
return mSettings.removeDefaultBrowserPackageNameLPw(userId);
}
}
@Override
public boolean isApexPackage(String packageName) {
return PackageManagerService.this.mApexManager.isApexPackage(packageName);
}
@Override
public List<String> getApksInApex(String apexPackageName) {
return PackageManagerService.this.mApexManager.getApksInApex(apexPackageName);
}
@Override
public void uninstallApex(String packageName, long versionCode, int userId,
IntentSender intentSender, int flags) {
final int callerUid = Binder.getCallingUid();
if (callerUid != Process.ROOT_UID && callerUid != Process.SHELL_UID) {
throw new SecurityException("Not allowed to uninstall apexes");
}
PackageInstallerService.PackageDeleteObserverAdapter adapter =
new PackageInstallerService.PackageDeleteObserverAdapter(
PackageManagerService.this.mContext, intentSender, packageName,
false, userId);
if ((flags & PackageManager.DELETE_ALL_USERS) == 0) {
adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
"Can't uninstall an apex for a single user");
return;
}
final ApexManager am = PackageManagerService.this.mApexManager;
PackageInfo activePackage = am.getPackageInfo(packageName,
ApexManager.MATCH_ACTIVE_PACKAGE);
if (activePackage == null) {
adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
packageName + " is not an apex package");
return;
}
if (versionCode != PackageManager.VERSION_CODE_HIGHEST
&& activePackage.getLongVersionCode() != versionCode) {
adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
"Active version " + activePackage.getLongVersionCode()
+ " is not equal to " + versionCode + "]");
return;
}
if (!am.uninstallApex(activePackage.applicationInfo.sourceDir)) {
adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
"Failed to uninstall apex " + packageName);
} else {
adapter.onPackageDeleted(packageName, PackageManager.DELETE_SUCCEEDED,
null);
}
}
@Override
public void updateRuntimePermissionsFingerprint(@UserIdInt int userId) {
synchronized (mLock) {
mSettings.updateRuntimePermissionsFingerprintLPr(userId);
}
}
@Override
public void migrateLegacyObbData() {
try {
mInstaller.migrateLegacyObbData();
} catch (Exception e) {
Slog.wtf(TAG, e);
}
}
@Override
public void writeSettings(boolean async) {
synchronized (mLock) {
if (async) {
scheduleWriteSettingsLocked();
} else {
mSettings.writeLPr();
}
}
}
@Override
public void writePermissionSettings(int[] userIds, boolean async) {
synchronized (mLock) {
for (int userId : userIds) {
mSettings.writeRuntimePermissionsForUserLPr(userId, !async);
}
}
}
@Override
public boolean isCallerInstallerOfRecord(
@NonNull AndroidPackage pkg, int callingUid) {
synchronized (mLock) {
if (pkg == null) {
return false;
}
final PackageSetting packageSetting = getPackageSetting(pkg.getPackageName());
if (packageSetting == null) {
return false;
}
final PackageSetting installerPackageSetting =
mSettings.mPackages.get(packageSetting.installSource.installerPackageName);
return installerPackageSetting != null
&& UserHandle.isSameApp(installerPackageSetting.appId, callingUid);
}
}
@Override
public boolean isPermissionUpgradeNeeded(int userId) {
synchronized (mLock) {
return mSettings.isPermissionUpgradeNeededLPr(userId);
}
}
@Override
public void setReadExternalStorageEnforced(boolean enforced) {
synchronized (mLock) {
if (mSettings.mReadExternalStorageEnforced != null
&& mSettings.mReadExternalStorageEnforced == enforced) {
return;
}
mSettings.mReadExternalStorageEnforced = enforced ? Boolean.TRUE : Boolean.FALSE;
mSettings.writeLPr();
}
}
@Override
public void setIntegrityVerificationResult(int verificationId, int verificationResult) {
final Message msg = mHandler.obtainMessage(INTEGRITY_VERIFICATION_COMPLETE);
msg.arg1 = verificationId;
msg.obj = verificationResult;
mHandler.sendMessage(msg);
}
@Override
public List<String> getMimeGroup(String packageName, String mimeGroup) {
return PackageManagerService.this.getMimeGroup(packageName, mimeGroup);
}
@Override
public void setVisibilityLogging(String packageName, boolean enable) {
final PackageSetting pkg;
synchronized (mLock) {
pkg = mSettings.getPackageLPr(packageName);
}
if (pkg == null) {
throw new IllegalStateException("No package found for " + packageName);
}
mAppsFilter.getFeatureConfig().enableLogging(pkg.appId, enable);
}
@Override
public boolean isSystemPackage(@NonNull String packageName) {
return packageName.equals(
PackageManagerService.this.ensureSystemPackageName(packageName));
}
@Override
public void clearBlockUninstallForUser(@UserIdInt int userId) {
synchronized (mLock) {
mSettings.clearBlockUninstallLPw(userId);
mSettings.writePackageRestrictionsLPr(userId);
}
}
@Override
public void unsuspendForSuspendingPackage(final String packageName, int affectedUser) {
PackageManagerService.this.unsuspendForSuspendingPackage(packageName, affectedUser);
}
@Override
public boolean isSuspendingAnyPackages(String suspendingPackage, int userId) {
return PackageManagerService.this.isSuspendingAnyPackages(suspendingPackage, userId);
}
}
@GuardedBy("mLock")
private SparseArray<String> getAppsWithSharedUserIdsLocked() {
final SparseArray<String> sharedUserIds = new SparseArray<>();
synchronized (mLock) {
for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
sharedUserIds.put(UserHandle.getAppId(setting.userId), setting.name);
}
}
return sharedUserIds;
}
@GuardedBy("mLock")
@NonNull
private String[] getSharedUserPackagesForPackageLocked(String packageName, int userId) {
final PackageSetting packageSetting = mSettings.mPackages.get(packageName);
if (packageSetting == null || !packageSetting.isSharedUser()) {
return EmptyArray.STRING;
}
ArraySet<PackageSetting> packages = packageSetting.sharedUser.packages;
String[] res = new String[packages.size()];
final Iterator<PackageSetting> it = packages.iterator();
int i = 0;
while (it.hasNext()) {
PackageSetting ps = it.next();
if (ps.getInstalled(userId)) {
res[i++] = ps.name;
}
}
res = ArrayUtils.trimToSize(res, i);
return res != null ? res : EmptyArray.STRING;
}
@GuardedBy("mLock")
public ArrayMap<String, ProcessInfo> getProcessesForUidLocked(int uid) {
final int appId = UserHandle.getAppId(uid);
final SettingBase obj = mSettings.getSettingLPr(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return PackageInfoUtils.generateProcessInfo(sus.processes, 0);
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
return PackageInfoUtils.generateProcessInfo(ps.pkg.getProcesses(), 0);
}
return null;
}
@GuardedBy("mLock")
public int[] getPermissionGidsLocked(String permissionName, int userId) {
BasePermission perm
= mPermissionManager.getPermissionSettings().getPermission(permissionName);
if (perm != null) {
return perm.computeGids(userId);
}
return null;
}
@Override
public int getRuntimePermissionsVersion(@UserIdInt int userId) {
Preconditions.checkArgumentNonnegative(userId);
enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
"getRuntimePermissionVersion");
synchronized (mLock) {
return mSettings.getDefaultRuntimePermissionsVersionLPr(userId);
}
}
@Override
public void setRuntimePermissionsVersion(int version, @UserIdInt int userId) {
Preconditions.checkArgumentNonnegative(version);
Preconditions.checkArgumentNonnegative(userId);
enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
"setRuntimePermissionVersion");
synchronized (mLock) {
mSettings.setDefaultRuntimePermissionsVersionLPr(version, userId);
}
}
private void enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
@NonNull String message) {
if (mContext.checkCallingOrSelfPermission(
Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY)
!= PackageManager.PERMISSION_GRANTED
&& mContext.checkCallingOrSelfPermission(
Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(message + " requires "
+ Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " or "
+ Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS);
}
}
@Nullable
public PackageSetting getPackageSetting(String packageName) {
return getPackageSettingInternal(packageName, Binder.getCallingUid());
}
private PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
synchronized (mLock) {
packageName = resolveInternalPackageNameInternalLocked(
packageName, PackageManager.VERSION_CODE_HIGHEST, callingUid);
return mSettings.mPackages.get(packageName);
}
}
void forEachPackage(Consumer<AndroidPackage> actionLocked) {
synchronized (mLock) {
int numPackages = mPackages.size();
for (int i = 0; i < numPackages; i++) {
actionLocked.accept(mPackages.valueAt(i));
}
}
}
void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> actionLocked,
@UserIdInt int userId) {
synchronized (mLock) {
int numPackages = mPackages.size();
for (int i = 0; i < numPackages; i++) {
AndroidPackage pkg = mPackages.valueAt(i);
PackageSetting setting = mSettings.getPackageLPr(pkg.getPackageName());
if (setting == null || !setting.getInstalled(userId)) {
continue;
}
actionLocked.accept(pkg);
}
}
}
boolean isHistoricalPackageUsageAvailable() {
return mPackageUsage.isHistoricalPackageUsageAvailable();
}
/**
* Return a <b>copy</b> of the collection of packages known to the package manager.
* @return A copy of the values of mPackages.
*/
Collection<AndroidPackage> getPackages() {
synchronized (mLock) {
return new ArrayList<>(mPackages.values());
}
}
/**
* Logs process start information (including base APK hash) to the security log.
* @hide
*/
@Override
public void logAppProcessStartIfNeeded(String processName, int uid, String seinfo,
String apkFile, int pid) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return;
}
if (!SecurityLog.isLoggingEnabled()) {
return;
}
Bundle data = new Bundle();
data.putLong("startTimestamp", System.currentTimeMillis());
data.putString("processName", processName);
data.putInt("uid", uid);
data.putString("seinfo", seinfo);
data.putString("apkFile", apkFile);
data.putInt("pid", pid);
Message msg = mProcessLoggingHandler.obtainMessage(
ProcessLoggingHandler.LOG_APP_PROCESS_START_MSG);
msg.setData(data);
mProcessLoggingHandler.sendMessage(msg);
}
public CompilerStats.PackageStats getCompilerPackageStats(String pkgName) {
return mCompilerStats.getPackageStats(pkgName);
}
public CompilerStats.PackageStats getOrCreateCompilerPackageStats(AndroidPackage pkg) {
return getOrCreateCompilerPackageStats(pkg.getPackageName());
}
public CompilerStats.PackageStats getOrCreateCompilerPackageStats(String pkgName) {
return mCompilerStats.getOrCreatePackageStats(pkgName);
}
public void deleteCompilerPackageStats(String pkgName) {
mCompilerStats.deletePackageStats(pkgName);
}
@Override
public boolean isAutoRevokeWhitelisted(String packageName) {
int mode = mInjector.getAppOpsManager().checkOpNoThrow(
AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
Binder.getCallingUid(), packageName);
return mode == MODE_IGNORED;
}
@Override
public int getInstallReason(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"get install reason");
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
return PackageManager.INSTALL_REASON_UNKNOWN;
}
if (ps != null) {
return ps.getInstallReason(userId);
}
}
return PackageManager.INSTALL_REASON_UNKNOWN;
}
@Override
public boolean canRequestPackageInstalls(String packageName, int userId) {
return canRequestPackageInstallsInternal(packageName, 0, userId,
true /* throwIfPermNotDeclared*/);
}
private boolean canRequestPackageInstallsInternal(String packageName, int flags, int userId,
boolean throwIfPermNotDeclared) {
int callingUid = Binder.getCallingUid();
int uid = getPackageUid(packageName, 0, userId);
if (callingUid != uid && callingUid != Process.ROOT_UID
&& callingUid != Process.SYSTEM_UID) {
throw new SecurityException(
"Caller uid " + callingUid + " does not own package " + packageName);
}
ApplicationInfo info = getApplicationInfo(packageName, flags, userId);
if (info == null) {
return false;
}
if (info.targetSdkVersion < Build.VERSION_CODES.O) {
return false;
}
if (isInstantApp(packageName, userId)) {
return false;
}
String appOpPermission = Manifest.permission.REQUEST_INSTALL_PACKAGES;
String[] packagesDeclaringPermission =
mPermissionManager.getAppOpPermissionPackages(appOpPermission, callingUid);
if (!ArrayUtils.contains(packagesDeclaringPermission, packageName)) {
if (throwIfPermNotDeclared) {
throw new SecurityException("Need to declare " + appOpPermission
+ " to call this api");
} else {
Slog.e(TAG, "Need to declare " + appOpPermission + " to call this api");
return false;
}
}
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userId)
|| mUserManager.hasUserRestriction(
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, userId)) {
return false;
}
if (mExternalSourcesPolicy != null) {
int isTrusted = mExternalSourcesPolicy.getPackageTrustedToInstallApps(packageName, uid);
return isTrusted == PackageManagerInternal.ExternalSourcesPolicy.USER_TRUSTED;
}
return false;
}
@Override
public ComponentName getInstantAppResolverSettingsComponent() {
return mInstantAppResolverSettingsComponent;
}
@Override
public ComponentName getInstantAppInstallerComponent() {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
return mInstantAppInstallerActivity == null
? null : mInstantAppInstallerActivity.getComponentName();
}
@Override
public String getInstantAppAndroidId(String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_INSTANT_APPS,
"getInstantAppAndroidId");
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"getInstantAppAndroidId");
// Make sure the target is an Instant App.
if (!isInstantApp(packageName, userId)) {
return null;
}
synchronized (mLock) {
return mInstantAppRegistry.getInstantAppAndroidIdLPw(packageName, userId);
}
}
@Override
public void grantImplicitAccess(int recipientUid, String visibleAuthority) {
// This API is exposed temporarily to only the contacts provider. (b/158688602)
final int callingUid = Binder.getCallingUid();
ProviderInfo contactsProvider = resolveContentProviderInternal(
ContactsContract.AUTHORITY, 0, UserHandle.getUserId(callingUid));
if (contactsProvider == null || contactsProvider.applicationInfo == null
|| !UserHandle.isSameApp(contactsProvider.applicationInfo.uid, callingUid)) {
throw new SecurityException(callingUid + " is not allow to call grantImplicitAccess");
}
final int userId = UserHandle.getUserId(recipientUid);
final long token = Binder.clearCallingIdentity();
final ProviderInfo providerInfo;
try {
providerInfo = resolveContentProvider(visibleAuthority, 0 /*flags*/, userId);
} finally {
Binder.restoreCallingIdentity(token);
}
if (providerInfo == null) {
return;
}
int visibleUid = providerInfo.applicationInfo.uid;
mPmInternal.grantImplicitAccess(userId, null /*Intent*/, UserHandle.getAppId(recipientUid),
visibleUid, false);
}
boolean canHaveOatDir(String packageName) {
synchronized (mLock) {
AndroidPackage p = mPackages.get(packageName);
PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
if (p == null || pkgSetting == null) {
return false;
}
return AndroidPackageUtils.canHaveOatDir(p,
pkgSetting.getPkgState().isUpdatedSystemApp());
}
}
private String getOatDir(AndroidPackage pkg, @NonNull PackageSetting pkgSetting) {
if (!AndroidPackageUtils.canHaveOatDir(pkg,
pkgSetting.getPkgState().isUpdatedSystemApp())) {
return null;
}
File codePath = new File(pkg.getCodePath());
if (codePath.isDirectory()) {
return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
}
return null;
}
void deleteOatArtifactsOfPackage(String packageName) {
final String[] instructionSets;
final List<String> codePaths;
final String oatDir;
final AndroidPackage pkg;
final PackageSetting pkgSetting;
synchronized (mLock) {
pkg = mPackages.get(packageName);
pkgSetting = mSettings.getPackageLPr(packageName);
}
instructionSets = getAppDexInstructionSets(
AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting));
codePaths = AndroidPackageUtils.getAllCodePaths(pkg);
oatDir = getOatDir(pkg, pkgSetting);
for (String codePath : codePaths) {
for (String isa : instructionSets) {
try {
mInstaller.deleteOdex(codePath, isa, oatDir);
} catch (InstallerException e) {
Log.e(TAG, "Failed deleting oat files for " + codePath, e);
}
}
}
}
Set<String> getUnusedPackages(long downgradeTimeThresholdMillis) {
Set<String> unusedPackages = new HashSet<>();
long currentTimeInMillis = System.currentTimeMillis();
synchronized (mLock) {
for (AndroidPackage pkg : mPackages.values()) {
PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName());
if (ps == null) {
continue;
}
PackageDexUsage.PackageUseInfo packageUseInfo =
getDexManager().getPackageUseInfoOrDefault(pkg.getPackageName());
if (PackageManagerServiceUtils
.isUnusedSinceTimeInMillis(ps.firstInstallTime, currentTimeInMillis,
downgradeTimeThresholdMillis, packageUseInfo,
ps.getPkgState().getLatestPackageUseTimeInMills(),
ps.getPkgState().getLatestForegroundPackageUseTimeInMills())) {
unusedPackages.add(pkg.getPackageName());
}
}
}
return unusedPackages;
}
@Override
public void setHarmfulAppWarning(@NonNull String packageName, @Nullable CharSequence warning,
int userId) {
final int callingUid = Binder.getCallingUid();
final int callingAppId = UserHandle.getAppId(callingUid);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /*requireFullPermission*/, true /*checkShell*/, "setHarmfulAppInfo");
if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID &&
checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) {
throw new SecurityException("Caller must have the "
+ SET_HARMFUL_APP_WARNINGS + " permission.");
}
synchronized (mLock) {
mSettings.setHarmfulAppWarningLPw(packageName, warning, userId);
scheduleWritePackageRestrictionsLocked(userId);
}
}
@Nullable
@Override
public CharSequence getHarmfulAppWarning(@NonNull String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
final int callingAppId = UserHandle.getAppId(callingUid);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /*requireFullPermission*/, true /*checkShell*/, "getHarmfulAppInfo");
if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID &&
checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) {
throw new SecurityException("Caller must have the "
+ SET_HARMFUL_APP_WARNINGS + " permission.");
}
synchronized (mLock) {
return mSettings.getHarmfulAppWarningLPr(packageName, userId);
}
}
@Override
public boolean isPackageStateProtected(@NonNull String packageName, @UserIdInt int userId) {
final int callingUid = Binder.getCallingUid();
final int callingAppId = UserHandle.getAppId(callingUid);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, true /*checkShell*/, "isPackageStateProtected");
if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID
&& checkUidPermission(MANAGE_DEVICE_ADMINS, callingUid) != PERMISSION_GRANTED) {
throw new SecurityException("Caller must have the "
+ MANAGE_DEVICE_ADMINS + " permission.");
}
return mProtectedPackages.isPackageStateProtected(userId, packageName);
}
@Override
public void sendDeviceCustomizationReadyBroadcast() {
mContext.enforceCallingPermission(Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY,
"sendDeviceCustomizationReadyBroadcast");
final long ident = Binder.clearCallingIdentity();
try {
final Intent intent = new Intent(Intent.ACTION_DEVICE_CUSTOMIZATION_READY);
intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
final IActivityManager am = ActivityManager.getService();
final String[] requiredPermissions = {
Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY,
};
try {
am.broadcastIntentWithFeature(null, null, intent, null, null, 0, null, null,
requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false,
UserHandle.USER_ALL);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void applyMimeGroupChanges(String packageName, String mimeGroup) {
if (mComponentResolver.updateMimeGroup(packageName, mimeGroup)) {
Binder.withCleanCallingIdentity(() ->
clearPackagePreferredActivities(packageName, UserHandle.USER_ALL));
}
mPmInternal.writeSettings(false);
}
@Override
public void setMimeGroup(String packageName, String mimeGroup, List<String> mimeTypes) {
boolean changed = mSettings.mPackages.get(packageName)
.setMimeGroup(mimeGroup, mimeTypes);
if (changed) {
applyMimeGroupChanges(packageName, mimeGroup);
}
}
@Override
public List<String> getMimeGroup(String packageName, String mimeGroup) {
return mSettings.mPackages.get(packageName).getMimeGroup(mimeGroup);
}
static class ActiveInstallSession {
private final String mPackageName;
private final File mStagedDir;
private final IPackageInstallObserver2 mObserver;
private final int mSessionId;
private final PackageInstaller.SessionParams mSessionParams;
private final int mInstallerUid;
@NonNull private final InstallSource mInstallSource;
private final UserHandle mUser;
private final SigningDetails mSigningDetails;
ActiveInstallSession(String packageName, File stagedDir, IPackageInstallObserver2 observer,
int sessionId, PackageInstaller.SessionParams sessionParams, int installerUid,
InstallSource installSource, UserHandle user, SigningDetails signingDetails) {
mPackageName = packageName;
mStagedDir = stagedDir;
mObserver = observer;
mSessionId = sessionId;
mSessionParams = sessionParams;
mInstallerUid = installerUid;
mInstallSource = Preconditions.checkNotNull(installSource);
mUser = user;
mSigningDetails = signingDetails;
}
public String getPackageName() {
return mPackageName;
}
public File getStagedDir() {
return mStagedDir;
}
public IPackageInstallObserver2 getObserver() {
return mObserver;
}
public int getSessionId() {
return mSessionId;
}
public PackageInstaller.SessionParams getSessionParams() {
return mSessionParams;
}
public int getInstallerUid() {
return mInstallerUid;
}
@NonNull
public InstallSource getInstallSource() {
return mInstallSource;
}
public UserHandle getUser() {
return mUser;
}
public SigningDetails getSigningDetails() {
return mSigningDetails;
}
}
}
interface PackageSender {
/**
* @param userIds User IDs where the action occurred on a full application
* @param instantUserIds User IDs where the action occurred on an instant application
*/
void sendPackageBroadcast(final String action, final String pkg,
final Bundle extras, final int flags, final String targetPkg,
final IIntentReceiver finishedReceiver, final int[] userIds, int[] instantUserIds,
@Nullable SparseArray<int[]> broadcastWhitelist);
void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted,
boolean includeStopped, int appId, int[] userIds, int[] instantUserIds,
int dataLoaderType);
void notifyPackageAdded(String packageName, int uid);
void notifyPackageChanged(String packageName, int uid);
void notifyPackageRemoved(String packageName, int uid);
}