blob: 898437ddae415358f6a6cf4414437c10c89cfd0f [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.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.CATEGORY_HOME;
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.DELETE_KEEP_DATA;
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_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
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.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_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_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.PackageParser.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
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.LIB64_DIR_NAME;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
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.InstructionSets.getPrimaryInstructionSet;
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.verifySignatures;
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
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.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.AppsQueryHelper;
import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.ChangedPackages;
import android.content.pm.ComponentInfo;
import android.content.pm.FallbackCategoryProvider;
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
import android.content.pm.IOnPermissionsChangeListener;
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.InstantAppInfo;
import android.content.pm.InstantAppRequest;
import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageBackwardCompatibility;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageList;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
import android.content.pm.PackageManager.ModuleInfoFlags;
import android.content.pm.PackageManager.PermissionWhitelistFlags;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.CheckPermissionDelegate;
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ActivityIntentInfo;
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.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.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.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.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.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.provider.DeviceConfig;
import android.provider.MediaStore;
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.ByteStringUtils;
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.StatsLog;
import android.util.TimingsTraceLog;
import android.util.Xml;
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.app.ResolverActivity;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
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.ConcurrentUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.AttributeCache;
import com.android.server.DeviceIdleController;
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.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.permission.BasePermission;
import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
import com.android.server.pm.permission.PermissionsState;
import com.android.server.security.VerityUtils;
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import dalvik.system.CloseGuard;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
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.FilenameFilter;
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.concurrent.CountDownLatch;
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;
/**
* Keep track of all those APKs everywhere.
* <p>
* Internally there are two important locks:
* <ul>
* <li>{@link #mPackages} 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 #mPackages}.
* Conversely, it's safe to acquire {@link #mPackages} 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 #mPackages} for reading
* <li>fooLPw(): the caller must hold {@link #mPackages} 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;
private 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_CHECK_ONLY = 1 << 10;
static final int SCAN_DONT_KILL_APP = 1 << 11;
static final int SCAN_IGNORE_FROZEN = 1 << 12;
static final int SCAN_FIRST_BOOT_OR_UPGRADE = 1 << 13;
static final int SCAN_AS_INSTANT_APP = 1 << 14;
static final int SCAN_AS_FULL_APP = 1 << 15;
static final int SCAN_AS_VIRTUAL_PRELOAD = 1 << 16;
static final int SCAN_AS_SYSTEM = 1 << 17;
static final int SCAN_AS_PRIVILEGED = 1 << 18;
static final int SCAN_AS_OEM = 1 << 19;
static final int SCAN_AS_VENDOR = 1 << 20;
static final int SCAN_AS_PRODUCT = 1 << 21;
static final int SCAN_AS_PRODUCT_SERVICES = 1 << 22;
static final int SCAN_AS_ODM = 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_CHECK_ONLY,
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;
/**
* The default maximum time to wait for the verification agent to return in
* milliseconds.
*/
private static final long DEFAULT_VERIFICATION_TIMEOUT = 10 * 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;
public static final String PLATFORM_PACKAGE_NAME = "android";
private static final String KILL_APP_REASON_GIDS_CHANGED =
"permission grant or revoke changed gids";
private static final String KILL_APP_REASON_PERMISSIONS_REVOKED =
"permissions revoked";
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
private static final String PACKAGE_SCHEME = "package";
private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
private static final String PRODUCT_OVERLAY_DIR = "/product/overlay";
private static final String PRODUCT_SERVICES_OVERLAY_DIR = "/product_services/overlay";
private static final String ODM_OVERLAY_DIR = "/odm/overlay";
/** 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;
/**
* Whether the package parser cache is enabled.
*/
private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true;
/**
* 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 };
final ServiceThread mHandlerThread;
final PackageHandler 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("mPackages")
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. */
private 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 = new Object();
// ----------------------------------------------------------------
// Keys are String (package name), values are Package. This also serves
// as the lock for the global state. Methods that must be called with
// this lock held have the prefix "LP".
@GuardedBy("mPackages")
final ArrayMap<String, PackageParser.Package> mPackages = new ArrayMap<>();
// Keys are isolated uids and values are the uid of the application
// that created the isolated proccess.
@GuardedBy("mPackages")
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 system packages prior to receiving an OTA. Keys are package name.
*/
final private ArraySet<String> mExistingSystemPackages = new ArraySet<>();
/**
* Whether or not system app permissions should be promoted from install to runtime.
*/
boolean mPromoteSystemApps;
@GuardedBy("mPackages")
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("mPackages")
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("mPackages")
int mChangedPackagesSequenceNumber;
/**
* List of changed [installed, removed or updated] packages.
* mapping from user id -> sequence number -> package name
*/
@GuardedBy("mPackages")
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("mPackages")
final SparseArray<Map<String, Integer>> mChangedPackagesSequenceNumbers = new SparseArray<>();
@GuardedBy("mPackages")
final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>();
@GuardedBy("mPackages")
private final SparseIntArray mDefaultPermissionsGrantedUsers = new SparseIntArray();
private final ModuleInfoProvider mModuleInfoProvider;
private final ApexManager mApexManager;
class PackageParserCallback implements PackageParser.Callback {
@Override public final boolean hasFeature(String feature) {
return PackageManagerService.this.hasSystemFeature(feature, 0);
}
final List<PackageParser.Package> getStaticOverlayPackages(
Collection<PackageParser.Package> allPackages, String targetPackageName) {
if ("android".equals(targetPackageName)) {
// Static RROs targeting to "android", ie framework-res.apk, are already applied by
// native AssetManager.
return null;
}
List<PackageParser.Package> overlayPackages = null;
for (PackageParser.Package p : allPackages) {
if (targetPackageName.equals(p.mOverlayTarget) && p.mOverlayIsStatic) {
if (overlayPackages == null) {
overlayPackages = new ArrayList<>();
}
overlayPackages.add(p);
}
}
if (overlayPackages != null) {
Comparator<PackageParser.Package> cmp =
Comparator.comparingInt(p -> p.mOverlayPriority);
overlayPackages.sort(cmp);
}
return overlayPackages;
}
final String[] getStaticOverlayPaths(List<PackageParser.Package> overlayPackages,
String targetPath) {
if (overlayPackages == null || overlayPackages.isEmpty()) {
return null;
}
List<String> overlayPathList = null;
for (PackageParser.Package overlayPackage : overlayPackages) {
if (targetPath == null) {
if (overlayPathList == null) {
overlayPathList = new ArrayList<>();
}
overlayPathList.add(overlayPackage.baseCodePath);
continue;
}
try {
// Creates idmaps for system to parse correctly the Android manifest of the
// target package.
//
// OverlayManagerService will update each of them with a correct gid from its
// target package app id.
mInstaller.idmap(targetPath, overlayPackage.baseCodePath,
UserHandle.getSharedAppGid(
UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
if (overlayPathList == null) {
overlayPathList = new ArrayList<>();
}
overlayPathList.add(overlayPackage.baseCodePath);
} catch (InstallerException e) {
Slog.e(TAG, "Failed to generate idmap for " + targetPath + " and " +
overlayPackage.baseCodePath);
}
}
return overlayPathList == null ? null : overlayPathList.toArray(new String[0]);
}
String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
List<PackageParser.Package> overlayPackages;
synchronized (mInstallLock) {
synchronized (mPackages) {
overlayPackages = getStaticOverlayPackages(
mPackages.values(), targetPackageName);
}
// It is safe to keep overlayPackages without holding mPackages because static overlay
// packages can't be uninstalled or disabled.
return getStaticOverlayPaths(overlayPackages, targetPath);
}
}
@Override public final String[] getOverlayApks(String targetPackageName) {
return getStaticOverlayPaths(targetPackageName, null);
}
@Override public final String[] getOverlayPaths(String targetPackageName,
String targetPath) {
return getStaticOverlayPaths(targetPackageName, targetPath);
}
}
class ParallelPackageParserCallback extends PackageParserCallback {
List<PackageParser.Package> mOverlayPackages = null;
void findStaticOverlayPackages() {
synchronized (mPackages) {
for (PackageParser.Package p : mPackages.values()) {
if (p.mOverlayIsStatic) {
if (mOverlayPackages == null) {
mOverlayPackages = new ArrayList<>();
}
mOverlayPackages.add(p);
}
}
}
}
@Override
synchronized String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
// We can trust mOverlayPackages without holding mPackages because package uninstall
// can't happen while running parallel parsing.
// And we can call mInstaller inside getStaticOverlayPaths without holding mInstallLock
// because mInstallLock is held before running parallel parsing.
// Moreover holding mPackages or mInstallLock on each parsing thread causes dead-lock.
return mOverlayPackages == null ? null :
getStaticOverlayPaths(
getStaticOverlayPackages(mOverlayPackages, targetPackageName),
targetPath);
}
}
final PackageParser.Callback mPackageParserCallback = new PackageParserCallback();
final ParallelPackageParserCallback mParallelPackageParserCallback =
new ParallelPackageParserCallback();
// 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, PackageParser.Instrumentation> mInstrumentation =
new ArrayMap<>();
// Packages whose data we have transfered into another package, thus
// should no longer exist.
final ArraySet<String> mTransferedPackages = 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;
private final OnPermissionChangeListeners mOnPermissionChangeListeners;
// 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;
PackageParser.Package mPlatformPackage;
ComponentName mCustomResolverComponentName;
boolean mResolverReplaced = false;
private final @Nullable ComponentName mIntentFilterVerifierComponent;
private final @Nullable IntentFilterVerifier<ActivityIntentInfo> 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();
final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
= new SparseArray<>();
// TODO remove this and go through mPermissonManager directly
final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
private final PermissionManagerServiceInternal mPermissionManager;
private final ComponentResolver mComponentResolver;
// List of packages names to keep cached, even if they are uninstalled for all users
private List<String> mKeepUninstalledPackages;
private UserManagerInternal mUserManagerInternal;
private ActivityManagerInternal mActivityManagerInternal;
private ActivityTaskManagerInternal mActivityTaskManagerInternal;
private StorageManagerInternal mStorageManagerInternal;
private DeviceIdleController.LocalService mDeviceIdleController;
private File mCacheDir;
private Future<?> mPrepareAppDataFuture;
private static class IFVerificationParams {
PackageParser.Package pkg;
boolean replacing;
int userId;
int verifierUid;
public IFVerificationParams(PackageParser.Package _pkg, boolean _replacing,
int _userId, int _verifierUid) {
pkg = _pkg;
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);
}
@GuardedBy("mPackages")
private CheckPermissionDelegate mCheckPermissionDelegate;
@GuardedBy("mPackages")
private PackageManagerInternal.DefaultBrowserProvider mDefaultBrowserProvider;
@GuardedBy("mPackages")
private PackageManagerInternal.DefaultHomeProvider mDefaultHomeProvider;
private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> {
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<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
final int filterCount = filters.size();
ArraySet<String> domainsSet = new ArraySet<>();
for (int m=0; m<filterCount; m++) {
PackageParser.ActivityIntentInfo filter = filters.get(m);
domainsSet.addAll(filter.getHostsList());
}
synchronized (mPackages) {
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);
DeviceIdleController.LocalService idleController = getDeviceIdleController();
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<PackageParser.ActivityIntentInfo> 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++) {
PackageParser.ActivityIntentInfo 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 (mPackages) {
ivi = mSettings.getIntentFilterVerificationLPr(packageName);
}
if (ivi == null) {
Slog.w(TAG, "IntentFilterVerificationInfo not found for verificationId:"
+ verificationId + " packageName:" + packageName);
return;
}
synchronized (mPackages) {
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;
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG,
"Updating IntentFilterVerificationInfo for package " + packageName
+ " verificationId:" + verificationId
+ " verified=" + verified);
}
// 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,
ActivityIntentInfo 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 (mPackages) {
mIntentFilterVerificationStates.append(verificationId, ivs);
mCurrentIntentFilterVerifications.add(verificationId);
}
return ivs;
}
}
private static boolean hasValidDomains(ActivityIntentInfo 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.
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 = new PendingPackageBroadcasts();
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 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 */
static UserManagerService sUserManager;
// 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 {
public final InstallArgs args;
public final PackageInstalledInfo res;
public final Runnable mPostInstallRunnable;
PostInstallData(InstallArgs _a, PackageInstalledInfo _r, Runnable postInstallRunnable) {
args = _a;
res = _r;
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_REVOKE_ON_UPGRADE;
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 mSystemTextClassifierPackage;
final @Nullable String mWellbeingPackage;
final @Nullable String mDocumenterPackage;
final @Nullable String mConfiguratorPackage;
final @Nullable String mAppPredictionServicePackage;
final @Nullable String mIncidentReportApproverPackage;
final @NonNull String mServicesSystemSharedLibraryPackageName;
final @NonNull String mSharedSystemSharedLibraryPackageName;
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 (mPackages) {
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, components[i], uids[i]);
}
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.mPostInstallRunnable != null) {
data.mPostInstallRunnable.run();
} else if (data != 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.requestedPermissions : null;
// Handle the parent package
handlePackagePostInstall(parentRes, grantPermissions,
killApp, virtualPreload, grantedPermissions,
whitelistedRestrictedPermissions, didRestore,
args.installerPackageName, args.observer);
// Handle the child packages
final int childCount = (parentRes.addedChildPackages != null)
? parentRes.addedChildPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i);
handlePackagePostInstall(childRes, grantPermissions,
killApp, virtualPreload, grantedPermissions,
whitelistedRestrictedPermissions, false /*didRestore*/,
args.installerPackageName, args.observer);
}
// 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 WRITE_SETTINGS: {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mPackages) {
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 (mPackages) {
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 (mPackages) {
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.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);
mPendingVerification.remove(verificationId);
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, user);
} else {
broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_REJECT, user);
params.setReturnCode(
PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
}
Trace.asyncTraceEnd(
TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
params.handleVerificationFinished();
}
break;
}
case PACKAGE_VERIFIED: {
final int verificationId = msg.arg1;
final PackageVerificationState state = mPendingVerification.get(verificationId);
if (state == null) {
Slog.w(TAG, "Invalid verification token " + verificationId + " received");
break;
}
final PackageVerificationResponse response = (PackageVerificationResponse) msg.obj;
state.setVerifierResponse(response.callerUid, response.code);
if (state.isVerificationComplete()) {
mPendingVerification.remove(verificationId);
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, args.getUser());
} else {
params.setReturnCode(
PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
}
Trace.asyncTraceEnd(
TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
params.handleVerificationFinished();
}
break;
}
case START_INTENT_FILTER_VERIFICATIONS: {
IFVerificationParams params = (IFVerificationParams) msg.obj;
verifyIntentFiltersIfNeeded(params.userId, params.verifierUid,
params.replacing, params.pkg);
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 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();
}
break;
}
}
}
}
private PermissionCallback mPermissionCallback = new PermissionCallback() {
@Override
public void onGidsChanged(int appId, int userId) {
mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED));
}
@Override
public void onPermissionGranted(int uid, int userId) {
mOnPermissionChangeListeners.onPermissionsChanged(uid);
// Not critical; if this is lost, the application has to request again.
synchronized (mPackages) {
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}
}
@Override
public void onInstallPermissionGranted() {
synchronized (mPackages) {
scheduleWriteSettingsLocked();
}
}
@Override
public void onPermissionRevoked(int uid, int userId) {
mOnPermissionChangeListeners.onPermissionsChanged(uid);
synchronized (mPackages) {
// Critical; after this call the application should never have the permission
mSettings.writeRuntimePermissionsForUserLPr(userId, true);
}
final int appId = UserHandle.getAppId(uid);
killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
}
@Override
public void onInstallPermissionRevoked() {
synchronized (mPackages) {
scheduleWriteSettingsLocked();
}
}
@Override
public void onPermissionUpdated(int[] updatedUserIds, boolean sync) {
synchronized (mPackages) {
for (int userId : updatedUserIds) {
mSettings.writeRuntimePermissionsForUserLPr(userId, sync);
}
}
}
@Override
public void onInstallPermissionUpdated() {
synchronized (mPackages) {
scheduleWriteSettingsLocked();
}
}
@Override
public void onPermissionRemoved() {
synchronized (mPackages) {
mSettings.writeLPr();
}
}
};
private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
boolean killApp, boolean virtualPreload,
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
boolean launchedForRestore, String installerPackage,
IPackageInstallObserver2 installObserver) {
if (res.returnCode == PackageManager.INSTALL_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(), PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER,
mPermissionCallback);
}
// 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,
mPermissionCallback);
}
final boolean update = res.removedInfo != null
&& res.removedInfo.removedPackage != null;
final String installerPackageName =
res.installerPackageName != null
? res.installerPackageName
: res.removedInfo != null
? res.removedInfo.installerPackageName
: null;
// If this is the first time we have child packages for a disabled privileged
// app that had no children, we grant requested runtime permissions to the new
// children if the parent on the system image had them already granted.
if (res.pkg.parentPackage != null) {
final int callingUid = Binder.getCallingUid();
mPermissionManager.grantRuntimePermissionsGrantedToDisabledPackage(
res.pkg, callingUid, mPermissionCallback);
}
synchronized (mPackages) {
mInstantAppRegistry.onPackageInstalledLPw(res.pkg, res.newUsers);
}
final String packageName = res.pkg.applicationInfo.packageName;
// 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 = (PackageSetting) res.pkg.mExtras;
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.staticSharedLibName == null) {
mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(res.pkg.baseCodePath);
// 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.applicationInfo.isSystemApp();
sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,
virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds);
// 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);
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
extras, 0 /*flags*/,
null /*targetPackage*/, null /*finishedReceiver*/,
updateUserIds, instantUserIds);
if (installerPackageName != null) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
extras, 0 /*flags*/,
installerPackageName, null /*finishedReceiver*/,
updateUserIds, instantUserIds);
}
// 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);
}
// 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);
}
// 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);
if (installerPackageName != null) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
extras, 0 /*flags*/,
installerPackageName, null /*finishedReceiver*/,
updateUserIds, instantUserIds);
}
if (notifyVerifier) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
extras, 0 /*flags*/,
mRequiredVerifierPackage, null /*finishedReceiver*/,
updateUserIds, instantUserIds);
}
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null /*package*/, null /*extras*/, 0 /*flags*/,
packageName /*targetPackage*/,
null /*finishedReceiver*/, updateUserIds, instantUserIds);
} else if (launchedForRestore && !isSystemApp(res.pkg)) {
// 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 (isExternal(res.pkg)) {
if (!update) {
final StorageManager storage =
mContext.getSystemService(StorageManager.class);
VolumeInfo volume =
storage.findVolumeByUuid(
res.pkg.applicationInfo.storageUuid.toString());
int packageExternalStorageType =
getPackageExternalStorageType(volume, isExternal(res.pkg));
// If the package was installed externally, log it.
if (packageExternalStorageType != StorageEnums.UNKNOWN) {
StatsLog.write(StatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
packageExternalStorageType, res.pkg.packageName);
}
}
if (DEBUG_INSTALL) {
Slog.i(TAG, "upgrading pkg " + res.pkg + " is external");
}
final int[] uidArray = new int[]{res.pkg.applicationInfo.uid};
ArrayList<String> pkgList = new ArrayList<>(1);
pkgList.add(packageName);
sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);
}
}
// Work that needs to happen on first install within each user
if (firstUserIds != null && firstUserIds.length > 0) {
synchronized (mPackages) {
for (int userId : firstUserIds) {
// 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
final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting.getInstallReason(userId)
!= PackageManager.INSTALL_REASON_DEVICE_RESTORE) {
setDefaultBrowserAsyncLPw(null, 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.
updateDefaultHomeLPw(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
// We delete after a gc for applications on sdcard.
if (res.removedInfo != null && res.removedInfo.args != null) {
Runtime.getRuntime().gc();
synchronized (mInstallLock) {
res.removedInfo.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);
}
}
}
// If someone is watching installs - notify them
if (installObserver != null) {
try {
Bundle extras = extrasForInstallResult(res);
installObserver.onPackageInstalled(res.name, res.returnCode,
res.returnMsg, extras);
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
}
}
/**
* 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 StorageEnum#TYPE_UNKNOWN} if the package is not stored externally or the
* corresponding {@link StorageEnum} 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
sUserManager.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 (mPackages) {
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() {
if (!mHandler.hasMessages(WRITE_SETTINGS)) {
mHandler.sendEmptyMessageDelayed(WRITE_SETTINGS, WRITE_SETTINGS_DELAY);
}
}
void scheduleWritePackageListLocked(int userId) {
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) {
final int[] userIds = (userId == UserHandle.USER_ALL)
? sUserManager.getUserIds() : new int[]{userId};
for (int nextUserId : userIds) {
if (!sUserManager.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();
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
private void enableSystemUserPackages() {
if (!UserManager.isSplitSystemUser()) {
return;
}
// For system user, enable apps based on the following conditions:
// - app is whitelisted or belong to one of these groups:
// -- system app which has no launcher icons
// -- system app which has INTERACT_ACROSS_USERS permission
// -- system IME app
// - app is not in the blacklist
AppsQueryHelper queryHelper = new AppsQueryHelper(this);
Set<String> enableApps = new ArraySet<>();
enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_NON_LAUNCHABLE_APPS
| AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM
| AppsQueryHelper.GET_IMES, /* systemAppsOnly */ true, UserHandle.SYSTEM));
ArraySet<String> wlApps = SystemConfig.getInstance().getSystemUserWhitelistedApps();
enableApps.addAll(wlApps);
enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_REQUIRED_FOR_SYSTEM_USER,
/* systemAppsOnly */ false, UserHandle.SYSTEM));
ArraySet<String> blApps = SystemConfig.getInstance().getSystemUserBlacklistedApps();
enableApps.removeAll(blApps);
Log.i(TAG, "Applications installed for system user: " + enableApps);
List<String> allAps = queryHelper.queryApps(0, /* systemAppsOnly */ false,
UserHandle.SYSTEM);
final int allAppsSize = allAps.size();
synchronized (mPackages) {
for (int i = 0; i < allAppsSize; i++) {
String pName = allAps.get(i);
PackageSetting pkgSetting = mSettings.mPackages.get(pName);
// Should not happen, but we shouldn't be failing if it does
if (pkgSetting == null) {
continue;
}
boolean install = enableApps.contains(pName);
if (pkgSetting.getInstalled(UserHandle.USER_SYSTEM) != install) {
Log.i(TAG, (install ? "Installing " : "Uninstalling ") + pName
+ " for system user");
pkgSetting.setInstalled(install, UserHandle.USER_SYSTEM);
}
}
scheduleWritePackageRestrictionsLocked(UserHandle.USER_SYSTEM);
}
}
private static void getDefaultDisplayMetrics(Context context, DisplayMetrics metrics) {
DisplayManager displayManager = (DisplayManager) context.getSystemService(
Context.DISPLAY_SERVICE);
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");
}
}
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "create package manager");
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
SystemClock.uptimeMillis());
if (mSdkVersion <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
}
mContext = context;
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
mMetrics = new DisplayMetrics();
mInstaller = installer;
// Create sub-components that provide services / data. Order here is important.
synchronized (mInstallLock) {
synchronized (mPackages) {
// Expose private service for system components to use.
LocalServices.addService(
PackageManagerInternal.class, new PackageManagerInternalImpl());
sUserManager = new UserManagerService(context, this,
new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
mComponentResolver = new ComponentResolver(sUserManager,
LocalServices.getService(PackageManagerInternal.class),
mPackages);
mPermissionManager = PermissionManagerService.create(context,
new DefaultPermissionGrantedCallback() {
@Override
public void onDefaultRuntimePermissionsGranted(int userId) {
synchronized(mPackages) {
mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
mDefaultPermissionsGrantedUsers.put(userId, userId);
}
}
}, mPackages /*externalLock*/);
mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
mSettings = new Settings(Environment.getDataDirectory(),
mPermissionManager.getPermissionSettings(), mPackages);
}
}
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);
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(installer, mInstallLock, context,
"*dexopt*");
mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock);
mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mViewCompiler = new ViewCompiler(mInstallLock, mInstaller);
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
FgThread.get().getLooper());
getDefaultDisplayMetrics(context, mMetrics);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "get system config");
SystemConfig systemConfig = SystemConfig.getInstance();
mAvailableFeatures = systemConfig.getAvailableFeatures();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
mProtectedPackages = new ProtectedPackages(mContext);
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
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();
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "loadFallbacks");
FallbackCategoryProvider.loadFallbacks();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "read user settings");
mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// 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
// STOPSHIP: Remove next line when API level for Q is defined.
&& Build.VERSION.SDK_INT > Build.VERSION_CODES.P;
int preUpgradeSdkVersion = ver.sdkVersion;
// save off the names of pre-existing system packages prior to scanning; we don't
// want to automatically grant runtime permissions for new system apps
if (mPromoteSystemApps) {
Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
while (pkgSettingIter.hasNext()) {
PackageSetting ps = pkgSettingIter.next();
if (isSystemApp(ps)) {
mExistingSystemPackages.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;
}
// Collect vendor/product/product_services 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.
scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_VENDOR,
0);
scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR),
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT,
0);
scanDirTracedLI(new File(PRODUCT_SERVICES_OVERLAY_DIR),
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT_SERVICES,
0);
scanDirTracedLI(new File(ODM_OVERLAY_DIR),
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_ODM,
0);
mParallelPackageParserCallback.findStaticOverlayPackages();
// Find base frameworks (resource packages without code).
scanDirTracedLI(frameworkDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_NO_DEX
| SCAN_AS_SYSTEM
| SCAN_AS_PRIVILEGED,
0);
if (!mPackages.containsKey("android")) {
throw new IllegalStateException(
"Failed to load frameworks package; check log for warnings");
}
// Collect privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirTracedLI(privilegedAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRIVILEGED,
0);
// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirTracedLI(systemAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM,
0);
// Collect privileged vendor packages.
File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app");
try {
privilegedVendorAppDir = privilegedVendorAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(privilegedVendorAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_VENDOR
| SCAN_AS_PRIVILEGED,
0);
// Collect ordinary vendor packages.
File vendorAppDir = new File(Environment.getVendorDirectory(), "app");
try {
vendorAppDir = vendorAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(vendorAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_VENDOR,
0);
// Collect privileged odm packages. /odm is another vendor partition
// other than /vendor.
File privilegedOdmAppDir = new File(Environment.getOdmDirectory(),
"priv-app");
try {
privilegedOdmAppDir = privilegedOdmAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(privilegedOdmAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_VENDOR
| SCAN_AS_PRIVILEGED,
0);
// Collect ordinary odm packages. /odm is another vendor partition
// other than /vendor.
File odmAppDir = new File(Environment.getOdmDirectory(), "app");
try {
odmAppDir = odmAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(odmAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_VENDOR,
0);
// Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirTracedLI(oemAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_OEM,
0);
// Collected privileged /product packages.
File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app");
try {
privilegedProductAppDir = privilegedProductAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(privilegedProductAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT
| SCAN_AS_PRIVILEGED,
0);
// Collect ordinary /product packages.
File productAppDir = new File(Environment.getProductDirectory(), "app");
try {
productAppDir = productAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(productAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT,
0);
// Collected privileged /product_services packages.
File privilegedProductServicesAppDir =
new File(Environment.getProductServicesDirectory(), "priv-app");
try {
privilegedProductServicesAppDir =
privilegedProductServicesAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(privilegedProductServicesAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT_SERVICES
| SCAN_AS_PRIVILEGED,
0);
// Collect ordinary /product_services packages.
File productServicesAppDir = new File(Environment.getProductServicesDirectory(), "app");
try {
productServicesAppDir = productServicesAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(productServicesAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT_SERVICES,
0);
// 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<PackageParser.Package> pkgIterator = mPackages.values().iterator();
while (pkgIterator.hasNext()) {
final PackageParser.Package pkg = pkgIterator.next();
if (pkg.isStub) {
stubSystemApps.add(pkg.packageName);
}
}
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 PackageParser.Package 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");
// 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);
}
}
}
}
//delete tmp files
deleteTempPackageFiles();
final int cachedSystemApps = PackageParser.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) {
MetricsLogger.histogram(null, "ota_package_manager_system_app_avg_scan_time",
((int) systemScanTime) / systemPackagesCount);
}
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
// 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 (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
final String msg;
if (deletedPkg == null) {
// should have found an update, but, we didn't; remove everything
msg = "Updated system package " + deletedAppName
+ " 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 + " + deletedAppName
+ " no longer exists; revoking system privileges";
// Don't do anything if a stub is removed from the system image. If
// we were to remove the uncompressed version from the /data partition,
// this is where it'd be done.
final PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
}
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");
final @ParseFlags int reparseFlags;
final @ScanFlags int rescanFlags;
if (FileUtils.contains(privilegedAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRIVILEGED;
} else if (FileUtils.contains(systemAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM;
} else if (FileUtils.contains(privilegedVendorAppDir, scanFile)
|| FileUtils.contains(privilegedOdmAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_VENDOR
| SCAN_AS_PRIVILEGED;
} else if (FileUtils.contains(vendorAppDir, scanFile)
|| FileUtils.contains(odmAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_VENDOR;
} else if (FileUtils.contains(oemAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_OEM;
} else if (FileUtils.contains(privilegedProductAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT
| SCAN_AS_PRIVILEGED;
} else if (FileUtils.contains(productAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT;
} else if (FileUtils.contains(privilegedProductServicesAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT_SERVICES
| SCAN_AS_PRIVILEGED;
} else if (FileUtils.contains(productServicesAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT_SERVICES;
} else {
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.
decompressSystemApplications(stubSystemApps, scanFlags);
final int cachedNonSystemApps = PackageParser.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) {
MetricsLogger.histogram(null, "ota_package_manager_data_app_avg_scan_time",
((int) dataScanTime) / dataPackagesCount);
}
}
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 = getSetupWizardPackageName();
mComponentResolver.fixProtectedFilterPriorities();
mSystemTextClassifierPackage = getSystemTextClassifierPackageName();
mWellbeingPackage = getWellbeingPackageName();
mDocumenterPackage = getDocumenterPackageName();
mConfiguratorPackage =
mContext.getString(R.string.config_deviceConfiguratorPackageName);
mAppPredictionServicePackage = getAppPredictionServicePackageName();
mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
updateAllSharedLibrariesLocked(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 =
adjustCpuAbisForSharedUserLPw(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();
}
// Now that we know all the packages we are keeping,
// read and update their last usage times.
mPackageUsage.read(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, mPackages.values(),
mPermissionCallback);
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 (!onlyCore && (mPromoteSystemApps || mFirstBoot)) {
for (UserInfo user : sUserManager.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.get().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) {
PackageParser.Package pkg = null;
synchronized (mPackages) {
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 && !onlyCore) {
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,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
}
ver.fingerprint = Build.FINGERPRINT;
}
// Grandfather existing (installed before Q) non-system apps to hide
// their icons in launcher.
if (!onlyCore && 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
mExistingSystemPackages.clear();
mPromoteSystemApps = false;
// All the changes are done during package scanning.
ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;
// can downgrade to reader
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "write settings");
mSettings.writeLPr();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
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;
}
mServicesSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
PackageManager.SYSTEM_SHARED_LIBRARY_SERVICES,
SharedLibraryInfo.VERSION_UNDEFINED);
mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
PackageManager.SYSTEM_SHARED_LIBRARY_SHARED,
SharedLibraryInfo.VERSION_UNDEFINED);
} else {
mRequiredVerifierPackage = null;
mRequiredInstallerPackage = null;
mRequiredUninstallerPackage = null;
mIntentFilterVerifierComponent = null;
mIntentFilterVerifier = null;
mServicesSystemSharedLibraryPackageName = null;
mSharedSystemSharedLibraryPackageName = null;
}
// PermissionController hosts default permission granting and role management, so it's a
// critical part of the core system.
mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr();
// Initialize InstantAppRegistry's Instant App list for all users.
final int[] userIds = UserManagerService.getInstance().getUserIds();
for (PackageParser.Package pkg : mPackages.values()) {
if (pkg.isSystem()) {
continue;
}
for (int userId : userIds) {
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null || !ps.getInstantApp(userId) || !ps.getInstalled(userId)) {
continue;
}
mInstantAppRegistry.addInstantAppLPw(userId, ps.appId);
}
}
mApexManager = new ApexManager(context);
mInstallerService = new PackageInstallerService(context, this, mApexManager);
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) {
MetricsLogger.histogram(null, "ota_package_manager_init_time",
(int) (SystemClock.uptimeMillis() - startTime));
}
} // synchronized (mPackages)
} // synchronized (mInstallLock)
mModuleInfoProvider = new ModuleInfoProvider(mContext, this);
// Now after opening every single application zip, make sure they
// are all flushed. Not really needed, but keeps things nice and
// tidy.
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "GC");
Runtime.getRuntime().gc();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// 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(mPackages);
PackageParser.readConfigUseRoundIcon(mContext.getResources());
mServiceStartWithDelay = SystemClock.uptimeMillis() + (60 * 1000L);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
/**
* 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 decompressSystemApplications(@NonNull List<String> stubSystemApps, int scanFlags) {
for (int i = stubSystemApps.size() - 1; i >= 0; --i) {
final String pkgName = stubSystemApps.get(i);
// skip if the system package is already disabled
if (mSettings.isDisabledSystemPackageLPr(pkgName)) {
stubSystemApps.remove(i);
continue;
}
// skip if the package isn't installed (?!); this should never happen
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg == null) {
stubSystemApps.remove(i);
continue;
}
// skip if the package has been disabled by the user
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM);
if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
stubSystemApps.remove(i);
continue;
}
}
if (DEBUG_COMPRESSION) {
Slog.i(TAG, "Uncompressing system stub; pkg: " + pkgName);
}
// uncompress the binary to its eventual destination on /data
final File scanFile = decompressPackage(pkg);
if (scanFile == null) {
continue;
}
// install the package to replace the stub on /system
try {
mSettings.disableSystemPackageLPw(pkgName, true /*replaced*/);
removePackageLI(pkg, true /*chatty*/);
scanPackageTracedLI(scanFile, 0 /*reparseFlags*/, scanFlags, 0, null);
ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
UserHandle.USER_SYSTEM, "android");
stubSystemApps.remove(i);
continue;
} 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 = stubSystemApps.size() - 1; i >= 0; --i) {
final String pkgName = stubSystemApps.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);
}
}
/**
* 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(PackageParser.Package pkg) {
final File[] compressedFiles = getCompressedFiles(pkg.codePath);
if (compressedFiles == null || compressedFiles.length == 0) {
if (DEBUG_COMPRESSION) {
Slog.i(TAG, "No files to decompress: " + pkg.baseCodePath);
}
return null;
}
final File dstCodePath =
getNextCodePath(Environment.getDataAppDirectory(null), pkg.packageName);
int ret = PackageManager.INSTALL_SUCCEEDED;
try {
Os.mkdir(dstCodePath.getAbsolutePath(), 0755);
Os.chmod(dstCodePath.getAbsolutePath(), 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: " + pkg.packageName
+ ", file: " + dstFileName);
break;
}
}
} catch (ErrnoException e) {
logCriticalInfo(Log.ERROR, "Failed to decompress"
+ "; pkg: " + 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*/);
} catch (IOException e) {
logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
+ "; pkg: " + 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("mPackages")
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 (!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 = 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 (mPackages) {
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;
}
}
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 (mPackages) {
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("mPackages")
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();
final PackageSetting ps = mSettings.mPackages.get(rInfo.activityInfo.packageName);
if (ps != null) {
final PermissionsState permissionsState = ps.getPermissionsState();
if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0)
|| 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("mPackages")
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) {
PackageParser.Package 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 (PackageParser.Activity a : pkg.activities) {
for (ActivityIntentInfo filter : a.intents) {
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 (!sUserManager.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 (filterAppAccessLPr(ps, callingUid, userId)) {
return null;
}
if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0
&& ps.isSystem()) {
flags |= MATCH_ANY_USER;
}
final PackageUserState state = ps.readUserState(userId);
PackageParser.Package 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
final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions)
? Collections.emptySet() : permissionsState.getPermissions(userId);
PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags,
ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId);
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");
}
final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId);
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null || filterAppAccessLPr(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 && !ps.pkg.applicationInfo.isEncryptionAware()) {
throw new SecurityException("Package " + packageName + " is not encryption aware!");
}
}
}
@Override
public boolean isPackageAvailable(String packageName, int userId) {
if (!sUserManager.exists(userId)) return false;
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "is package available");
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
if (p != null) {
final PackageSetting ps = (PackageSetting) p.mExtras;
if (filterAppAccessLPr(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 (!sUserManager.exists(userId)) return null;
flags = updateFlagsForPackage(flags, userId, packageName);
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get package info");
// reader
synchronized (mPackages) {
// Normalize package name to handle renamed packages and static libs
packageName = resolveInternalPackageNameLPr(packageName, versionCode);
final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
if (matchFactoryOnly) {
final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
if (ps != null) {
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
}
if (filterAppAccessLPr(ps, filterCallingUid, userId)) {
return null;
}
return generatePackageInfo(ps, flags, userId);
}
// TODO(b/123680735): support MATCH_APEX|MATCH_FACTORY_ONLY case
}
PackageParser.Package p = mPackages.get(packageName);
if (matchFactoryOnly && p != null && !isSystemApp(p)) {
return null;
}
if (DEBUG_PACKAGE_INFO)
Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
if (p != null) {
final PackageSetting ps = (PackageSetting) p.mExtras;
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
}
if (ps != null && filterAppAccessLPr(ps, filterCallingUid, userId)) {
return null;
}
return generatePackageInfo((PackageSetting)p.mExtras, 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 (filterAppAccessLPr(ps, filterCallingUid, userId)) {
return null;
}
return generatePackageInfo(ps, flags, userId);
}
//
if (!matchFactoryOnly && (flags & MATCH_APEX) != 0) {
return mApexManager.getActivePackage(packageName);
}
}
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 PackageParser.Activity activity = mComponentResolver.getActivity(component);
if (activity == null) {
return false;
}
final boolean visibleToInstantApp =
(activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
final boolean explicitlyVisibleToInstantApp =
(activity.info.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
return visibleToInstantApp && explicitlyVisibleToInstantApp;
} else if (type == TYPE_RECEIVER) {
final PackageParser.Activity activity = mComponentResolver.getReceiver(component);
if (activity == null) {
return false;
}
final boolean visibleToInstantApp =
(activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
final boolean explicitlyVisibleToInstantApp =
(activity.info.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
return visibleToInstantApp && !explicitlyVisibleToInstantApp;
} else if (type == TYPE_SERVICE) {
final PackageParser.Service service = mComponentResolver.getService(component);
return service != null
? (service.info.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0
: false;
} else if (type == TYPE_PROVIDER) {
final PackageParser.Provider provider = mComponentResolver.getProvider(component);
return provider != null
? (provider.info.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0
: false;
} 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("mPackages")
private boolean filterAppAccessLPr(@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 PackageParser.Instrumentation instrumentation =
mInstrumentation.get(component);
if (instrumentation != null
&& isCallerSameApp(instrumentation.info.targetPackage, callingUid)) {
return false;
}
return !isComponentVisibleToInstantApp(component, componentType);
}
// request for application; if no components have been explicitly exposed, filter
return !ps.pkg.visibleToInstantApps;
}
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);
}
return false;
}
/**
* @see #filterAppAccessLPr(PackageSetting, int, ComponentName, int, int)
*/
@GuardedBy("mPackages")
private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid, int userId) {
return filterAppAccessLPr(ps, callingUid, null, TYPE_UNKNOWN, userId);
}
@GuardedBy("mPackages")
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.applicationInfo.isStaticSharedLibrary()) {
return false;
}
final SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(ps.pkg.staticSharedLibName,
ps.pkg.staticSharedLibVersion);
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.usesStaticLibrariesVersions[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 (mPackages) {
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 (mPackages) {
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 (!sUserManager.exists(userId)) return -1;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId, packageName);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid");
// reader
synchronized (mPackages) {
final PackageParser.Package p = mPackages.get(packageName);
if (p != null && p.isMatch(flags)) {
PackageSetting ps = (PackageSetting) p.mExtras;
if (filterAppAccessLPr(ps, callingUid, userId)) {
return -1;
}
return UserHandle.getUid(userId, p.applicationInfo.uid);
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null && ps.isMatch(flags)
&& !filterAppAccessLPr(ps, callingUid, userId)) {
return UserHandle.getUid(userId, ps.appId);
}
}
}
return -1;
}
/**
* Check if any package sharing/holding a uid has a low enough target SDK.
*
* @param uid The uid of the packages
* @param higherTargetSDK The target SDK that might be higher than the searched package
*
* @return {@code true} if there is a package sharing/holding the uid with
* {@code package.targetSDK < higherTargetSDK}
*/
private boolean hasTargetSdkInUidLowerThan(int uid, int higherTargetSDK) {
int userId = UserHandle.getUserId(uid);
synchronized (mPackages) {
Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
if (obj == null) {
return false;
}
if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (!ps.getInstalled(userId)) {
return false;
}
return ps.pkg.applicationInfo.targetSdkVersion < higherTargetSDK;
} else if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
final int numPkgs = sus.packages.size();
for (int i = 0; i < numPkgs; i++) {
final PackageSetting ps = sus.packages.valueAt(i);
if (!ps.getInstalled(userId)) {
continue;
}
if (ps.pkg.applicationInfo.targetSdkVersion < higherTargetSDK) {
return true;
}
}
return false;
} else {
return false;
}
}
}
@Override
public int[] getPackageGids(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId, packageName);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids");
// reader
synchronized (mPackages) {
final PackageParser.Package p = mPackages.get(packageName);
if (p != null && p.isMatch(flags)) {
PackageSetting ps = (PackageSetting) p.mExtras;
if (filterAppAccessLPr(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)
&& !filterAppAccessLPr(ps, callingUid, userId)) {
return ps.getPermissionsState().computeGids(userId);
}
}
}
return null;
}
@Override
public PermissionInfo getPermissionInfo(String name, String packageName, int flags) {
return mPermissionManager.getPermissionInfo(name, packageName, flags, getCallingUid());
}
@Override
public @Nullable ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String groupName,
int flags) {
final List<PermissionInfo> permissionList =
mPermissionManager.getPermissionInfoByGroup(groupName, flags, getCallingUid());
return (permissionList == null) ? null : new ParceledListSlice<>(permissionList);
}
@Override
public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) {
return mPermissionManager.getPermissionGroupInfo(groupName, flags, getCallingUid());
}
@Override
public @NonNull ParceledListSlice<PermissionGroupInfo> getAllPermissionGroups(int flags) {
final List<PermissionGroupInfo> permissionList =
mPermissionManager.getAllPermissionGroups(flags, getCallingUid());
return (permissionList == null)
? ParceledListSlice.emptyList() : new ParceledListSlice<>(permissionList);
}
@GuardedBy("mPackages")
private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
int filterCallingUid, int userId) {
if (!sUserManager.exists(userId)) return null;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
}
if (filterAppAccessLPr(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 = PackageParser.generateApplicationInfo(ps.pkg, flags,
ps.readUserState(userId), userId);
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 (!sUserManager.exists(userId)) return null;
flags = updateFlagsForApplication(flags, userId, packageName);
if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */,
"get application info");
}
// writer
synchronized (mPackages) {
// Normalize package name to handle renamed packages and static libs
packageName = resolveInternalPackageNameLPr(packageName,
PackageManager.VERSION_CODE_HIGHEST);
PackageParser.Package 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 (filterAppAccessLPr(ps, filterCallingUid, userId)) {
return null;
}
// Note: isEnabledLP() does not apply here - always return info
ApplicationInfo ai = PackageParser.generateApplicationInfo(
p, flags, ps.readUserState(userId), userId);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(p);
}
return ai;
}
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("mPackages")
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 = mContext.getSystemService(StorageManager.class);
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 = mContext.getSystemService(StorageManager.class);
final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL);
List<VersionedPackage> packagesToDelete = null;
final long now = System.currentTimeMillis();
synchronized (mPackages) {
final int[] allUsers = sUserManager.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 (getUserManagerInternal().isUserUnlockingOrUnlocked(userId)) {
flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
} else {
flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE;
}
}
return flags;
}
private UserManagerInternal getUserManagerInternal() {
if (mUserManagerInternal == null) {
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
}
return mUserManagerInternal;
}
private ActivityManagerInternal getActivityManagerInternal() {
if (mActivityManagerInternal == null) {
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
}
return mActivityManagerInternal;
}
private ActivityTaskManagerInternal getActivityTaskManagerInternal() {
if (mActivityTaskManagerInternal == null) {
mActivityTaskManagerInternal =
LocalServices.getService(ActivityTaskManagerInternal.class);
}
return mActivityTaskManagerInternal;
}
private DeviceIdleController.LocalService getDeviceIdleController() {
if (mDeviceIdleController == null) {
mDeviceIdleController =
LocalServices.getService(DeviceIdleController.LocalService.class);
}
return mDeviceIdleController;
}
private StorageManagerInternal getStorageManagerInternal() {
if (mStorageManagerInternal == null) {
mStorageManagerInternal = LocalServices.getService(StorageManagerInternal.class);
}
return mStorageManagerInternal;
}
/**
* Update given flags when being used to request {@link PackageInfo}.
*/
private int updateFlagsForPackage(int flags, int userId, Object cookie) {
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
&& sUserManager.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, Object cookie) {
return updateFlagsForPackage(flags, userId, cookie);
}
/**
* Update given flags when being used to request {@link ComponentInfo}.
*/
private int updateFlagsForComponent(int flags, int userId, Object cookie) {
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, Intent intent, int callingUid) {
return updateFlagsForResolve(flags, userId, intent, callingUid,
false /*wantInstantApps*/, false /*onlyExposedExplicitly*/);
}
int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid,
boolean wantInstantApps) {
return updateFlagsForResolve(flags, userId, intent, callingUid,
wantInstantApps, false /*onlyExposedExplicitly*/);
}
int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid,
boolean wantInstantApps, boolean onlyExposedExplicitly) {
// Safe mode means we shouldn't match any third-party components
if (mSafeMode) {
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, intent /*cookie*/);
}
@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 (!sUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId, component);
if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get activity info");
}
synchronized (mPackages) {
PackageParser.Activity a = mComponentResolver.getActivity(component);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
if (filterAppAccessLPr(ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
return null;
}
return PackageParser.generateActivityInfo(
a, flags, ps.readUserState(userId), userId);
}
if (mResolveComponentName.equals(component)) {
return PackageParser.generateActivityInfo(
mResolveActivity, flags, new PackageUserState(), userId);
}
}
return null;
}
private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) {
if (!getActivityTaskManagerInternal().isCallerRecents(callingUid)) {
return false;
}
final long token = Binder.clearCallingIdentity();
try {
final int callingUserId = UserHandle.getUserId(callingUid);
if (ActivityManager.getCurrentUser() != callingUserId) {
return false;
}
return sUserManager.isSameProfileGroup(callingUserId, targetUserId);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public boolean activitySupportsIntent(ComponentName component, Intent intent,
String resolvedType) {
synchronized (mPackages) {
if (component.equals(mResolveComponentName)) {
// The resolver supports EVERYTHING!
return true;
}
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
PackageParser.Activity a = mComponentResolver.getActivity(component);
if (a == null) {
return false;
}
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) {
return false;
}
if (filterAppAccessLPr(ps, callingUid, component, TYPE_ACTIVITY, callingUserId)) {
return false;
}
for (int i=0; i<a.intents.size(); i++) {
if (a.intents.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 (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId, component);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get receiver info");
synchronized (mPackages) {
PackageParser.Activity a = mComponentResolver.getReceiver(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getReceiverInfo " + component + ": " + a);
if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
if (filterAppAccessLPr(ps, callingUid, component, TYPE_RECEIVER, userId)) {
return null;
}
return PackageParser.generateActivityInfo(
a, flags, ps.readUserState(userId), userId);
}
}
return null;
}
@Override
public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(String packageName,
int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0");
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
flags = updateFlagsForPackage(flags, userId, null);
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 (mPackages) {
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 (!sUserManager.exists(userId)) {
return null;
}
if (getInstantAppPackageName(callingUid) != null) {
return null;
}
synchronized (mPackages) {
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("mPackages")
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.applicationInfo.isStaticSharedLibrary()) {
dependentPackageName = ps.pkg.manifestPackageName;
}
versionedPackages.add(new VersionedPackage(dependentPackageName, ps.versionCode));
} else if (ps.pkg != null) {
if (ArrayUtils.contains(ps.pkg.usesLibraries, libName)
|| ArrayUtils.contains(ps.pkg.usesOptionalLibraries, 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 (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId, component);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get service info");
synchronized (mPackages) {
PackageParser.Service s = mComponentResolver.getService(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getServiceInfo " + component + ": " + s);
if (s != null && mSettings.isEnabledAndMatchLPr(s.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
if (filterAppAccessLPr(ps, callingUid, component, TYPE_SERVICE, userId)) {
return null;
}
return PackageParser.generateServiceInfo(
s, flags, ps.readUserState(userId), userId);
}
}
return null;
}
@Override
public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId, component);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get provider info");
synchronized (mPackages) {
PackageParser.Provider p = mComponentResolver.getProvider(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getProviderInfo " + component + ": " + p);
if (p != null && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) {
return null;
}
return PackageParser.generateProviderInfo(
p, flags, ps.readUserState(userId), userId);
}
}
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 (mPackages) {
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 (mPackages) {
return mServicesSystemSharedLibraryPackageName;
}
}
@Override
public @NonNull String getSharedSystemSharedLibraryPackageName() {
// allow instant applications
synchronized (mPackages) {
return mSharedSystemSharedLibraryPackageName;
}
}
@GuardedBy("mPackages")
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) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
synchronized (mPackages) {
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;
}
}
}
@Override
public int checkPermission(String permName, String pkgName, int userId) {
final CheckPermissionDelegate checkPermissionDelegate;
synchronized (mPackages) {
if (mCheckPermissionDelegate == null) {
return checkPermissionImpl(permName, pkgName, userId);
}
checkPermissionDelegate = mCheckPermissionDelegate;
}
return checkPermissionDelegate.checkPermission(permName, pkgName, userId,
PackageManagerService.this::checkPermissionImpl);
}
private int checkPermissionImpl(String permName, String pkgName, int userId) {
return mPermissionManager.checkPermission(permName, pkgName, getCallingUid(), userId);
}
@Override
public int checkUidPermission(String permName, int uid) {
final CheckPermissionDelegate checkPermissionDelegate;
synchronized (mPackages) {
if (mCheckPermissionDelegate == null) {
return checkUidPermissionImpl(permName, uid);
}
checkPermissionDelegate = mCheckPermissionDelegate;
}
return checkPermissionDelegate.checkUidPermission(permName, uid,
PackageManagerService.this::checkUidPermissionImpl);
}
private int checkUidPermissionImpl(String permName, int uid) {
synchronized (mPackages) {
final String[] packageNames = getPackagesForUid(uid);
final PackageParser.Package pkg = (packageNames != null && packageNames.length > 0)
? mPackages.get(packageNames[0])
: null;
return mPermissionManager.checkUidPermission(permName, pkg, uid, getCallingUid());
}
}
@Override
public boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId) {
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"isPermissionRevokedByPolicy for user " + userId);
}
if (checkPermission(permission, packageName, userId)
== PackageManager.PERMISSION_GRANTED) {
return false;
}
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
if (!isCallerSameApp(packageName, callingUid)) {
return false;
}
} else {
if (isInstantApp(packageName, userId)) {
return false;
}
}
final long identity = Binder.clearCallingIdentity();
try {
final int flags = getPermissionFlags(permission, packageName, userId);
return (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public String getPermissionControllerPackageName() {
synchronized (mPackages) {
return mRequiredPermissionControllerPackage;
}
}
String getPackageInstallerPackageName() {
synchronized (mPackages) {
return mRequiredInstallerPackage;
}
}
private boolean addDynamicPermission(PermissionInfo info, final boolean async) {
return mPermissionManager.addDynamicPermission(
info, async, getCallingUid(), new PermissionCallback() {
@Override
public void onPermissionChanged() {
if (!async) {
mSettings.writeLPr();
} else {
scheduleWriteSettingsLocked();
}
}
});
}
@Override
public boolean addPermission(PermissionInfo info) {
synchronized (mPackages) {
return addDynamicPermission(info, false);
}
}
@Override
public boolean addPermissionAsync(PermissionInfo info) {
synchronized (mPackages) {
return addDynamicPermission(info, true);
}
}
@Override
public void removePermission(String permName) {
mPermissionManager.removeDynamicPermission(permName, getCallingUid(), mPermissionCallback);
}
@Override
public void grantRuntimePermission(String packageName, String permName, final int userId) {
boolean overridePolicy = (checkUidPermission(
Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid())
== PackageManager.PERMISSION_GRANTED);
mPermissionManager.grantRuntimePermission(permName, packageName, overridePolicy,
getCallingUid(), userId, mPermissionCallback);
}
@Override
public void revokeRuntimePermission(String packageName, String permName, int userId) {
boolean overridePolicy = (checkUidPermission(
Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid())
== PackageManager.PERMISSION_GRANTED);
mPermissionManager.revokeRuntimePermission(permName, packageName, overridePolicy,
userId, mPermissionCallback);
}
@Override
public void resetRuntimePermissions() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
"revokeRuntimePermission");
int callingUid = Binder.getCallingUid();
if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"resetRuntimePermissions");
}
synchronized (mPackages) {
mPermissionManager.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(),
mPermissionCallback);
for (int userId : UserManagerService.getInstance().getUserIds()) {
final int packageCount = mPackages.size();
for (int i = 0; i < packageCount; i++) {
PackageParser.Package pkg = mPackages.valueAt(i);
if (!(pkg.mExtras instanceof PackageSetting)) {
continue;
}
PackageSetting ps = (PackageSetting) pkg.mExtras;
resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, userId);
}
}
}
}
@Override
public int getPermissionFlags(String permName, String packageName, int userId) {
return mPermissionManager.getPermissionFlags(
permName, packageName, getCallingUid(), userId);
}
@Override
public void updatePermissionFlags(String permName, String packageName, int flagMask,
int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
int callingUid = getCallingUid();
boolean overridePolicy = false;
if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) {
long callingIdentity = Binder.clearCallingIdentity();
try {
if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0) {
if (checkAdjustPolicyFlagPermission) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
"Need " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY
+ " to change policy flags");
} else if (!hasTargetSdkInUidLowerThan(callingUid, Build.VERSION_CODES.Q)) {
throw new IllegalArgumentException(
Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " needs "
+ " to be checked for packages targeting "
+ Build.VERSION_CODES.Q + " or later when changing policy "
+ "flags");
}
overridePolicy = true;
}
} finally {
Binder.restoreCallingIdentity(callingIdentity);
}
}
mPermissionManager.updatePermissionFlags(
permName, packageName, flagMask, flagValues, callingUid, userId,
overridePolicy, mPermissionCallback);
}
/**
* Update the permission flags for all packages and runtime permissions of a user in order
* to allow device or profile owner to remove POLICY_FIXED.
*/
@Override
public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) {
synchronized (mPackages) {
final boolean changed = mPermissionManager.updatePermissionFlagsForAllApps(
flagMask, flagValues, getCallingUid(), userId, mPackages.values(),
mPermissionCallback);
if (changed) {
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}
}
}
@Override
public @Nullable List<String> getWhitelistedRestrictedPermissions(@NonNull String packageName,
@PermissionWhitelistFlags int whitelistFlags, @UserIdInt int userId) {
Preconditions.checkNotNull(packageName);
Preconditions.checkFlagsArgument(whitelistFlags,
PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
| PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
| PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
Preconditions.checkArgumentNonNegative(userId, null);
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS,
"getWhitelistedRestrictedPermissions for user " + userId);
}
synchronized (mPackages) {
final PackageSetting packageSetting = mSettings.mPackages.get(packageName);
if (packageSetting == null) {
Slog.w(TAG, "Unknown package: " + packageName);
return null;
}
final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission(
Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS)
== PackageManager.PERMISSION_GRANTED;
final PackageSetting installerPackageSetting = mSettings.mPackages.get(
packageSetting.installerPackageName);
final boolean isCallerInstallerOnRecord = installerPackageSetting != null
&& UserHandle.isSameApp(installerPackageSetting.appId, Binder.getCallingUid());
if ((whitelistFlags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0
&& !isCallerPrivileged) {
throw new SecurityException("Querying system whitelist requires "
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS);
}
if ((whitelistFlags & (PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
| PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) != 0) {
if (!isCallerPrivileged && !isCallerInstallerOnRecord) {
throw new SecurityException("Querying upgrade or installer whitelist"
+ " requires being installer on record or "
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS);
}
}
if (filterAppAccessLPr(packageSetting, Binder.getCallingUid(),
UserHandle.getCallingUserId())) {
return null;
}
final long identity = Binder.clearCallingIdentity();
try {
return mPermissionManager.getWhitelistedRestrictedPermissions(
packageSetting.pkg, whitelistFlags, userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@Override
public boolean addWhitelistedRestrictedPermission(@NonNull String packageName,
@NonNull String permission, @PermissionWhitelistFlags int whitelistFlags,
@UserIdInt int userId) {
// Other argument checks are done in get/setWhitelistedRestrictedPermissions
Preconditions.checkNotNull(permission);
List<String> permissions = getWhitelistedRestrictedPermissions(packageName,
whitelistFlags, userId);
if (permissions == null) {
permissions = new ArrayList<>(1);
}
if (permissions.indexOf(permission) < 0) {
permissions.add(permission);
setWhitelistedRestrictedPermissions(packageName, permissions,
whitelistFlags, userId);
return true;
}
return false;
}
@Override
public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName,
@NonNull String permission, @PermissionWhitelistFlags int whitelistFlags,
@UserIdInt int userId) {
// Other argument checks are done in get/setWhitelistedRestrictedPermissions
Preconditions.checkNotNull(permission);
final List<String> permissions = getWhitelistedRestrictedPermissions(packageName,
whitelistFlags, userId);
if (permissions != null && permissions.remove(permission)) {
setWhitelistedRestrictedPermissions(packageName, permissions,
whitelistFlags, userId);
return true;
}
return false;
}
private void setWhitelistedRestrictedPermissions(@NonNull String packageName,
@Nullable List<String> permissions, @PermissionWhitelistFlags int whitelistFlag,
@UserIdInt int userId) {
Preconditions.checkNotNull(packageName);
Preconditions.checkFlagsArgument(whitelistFlag,
PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
| PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
| PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
Preconditions.checkArgument(Integer.bitCount(whitelistFlag) == 1);
Preconditions.checkArgumentNonNegative(userId, null);
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS,
"setWhitelistedRestrictedPermissions for user " + userId);
}
synchronized (mPackages) {
final PackageSetting packageSetting = mSettings.mPackages.get(packageName);
if (packageSetting == null) {
Slog.w(TAG, "Unknown package: " + packageName);
return;
}
final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission(
Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS)
== PackageManager.PERMISSION_GRANTED;
final PackageSetting installerPackageSetting = mSettings.mPackages.get(
packageSetting.installerPackageName);
final boolean isCallerInstallerOnRecord = installerPackageSetting != null
&& UserHandle.isSameApp(installerPackageSetting.appId, Binder.getCallingUid());
if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0
&& !isCallerPrivileged) {
throw new SecurityException("Modifying system whitelist requires "
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS);
}
if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) {
if (!isCallerPrivileged && !isCallerInstallerOnRecord) {
throw new SecurityException("Modifying upgrade whitelist requires"
+ " being installer on record or "
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS);
}
final List<String> whitelistedPermissions = getWhitelistedRestrictedPermissions(
packageName, whitelistFlag, userId);
if (permissions == null || permissions.isEmpty()) {
if (whitelistedPermissions == null || whitelistedPermissions.isEmpty()) {
return;
}
} else {
// Only the system can add and remove while the installer can only remove.
final int permissionCount = permissions.size();
for (int i = 0; i < permissionCount; i++) {
if ((whitelistedPermissions == null
|| !whitelistedPermissions.contains(permissions.get(i)))
&& !isCallerPrivileged) {
throw new SecurityException("Adding to upgrade whitelist requires"
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS);
}
}
}
}
if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) {
if (!isCallerPrivileged && !isCallerInstallerOnRecord) {
throw new SecurityException("Modifying installer whitelist requires"
+ " being installer on record or "
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS);
}
}
if (filterAppAccessLPr(packageSetting, Binder.getCallingUid(),
UserHandle.getCallingUserId())) {
return;
}
final long identity = Binder.clearCallingIdentity();
try {
mPermissionManager.setWhitelistedRestrictedPermissions(packageSetting.pkg,
new int[]{userId}, permissions, Process.myUid(), whitelistFlag,
mPermissionCallback);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@Override
public boolean shouldShowRequestPermissionRationale(String permissionName,
String packageName, int userId) {
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"canShowRequestPermissionRationale for user " + userId);
}
final int uid = getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId);
if (UserHandle.getAppId(getCallingUid()) != UserHandle.getAppId(uid)) {
return false;
}
if (checkPermission(permissionName, packageName, userId)
== PackageManager.PERMISSION_GRANTED) {
return false;
}
final int flags;
final long identity = Binder.clearCallingIdentity();
try {
flags = getPermissionFlags(permissionName,
packageName, userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
| PackageManager.FLAG_PERMISSION_POLICY_FIXED
| PackageManager.FLAG_PERMISSION_USER_FIXED;
if ((flags & fixedFlags) != 0) {
return false;
}
return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
}
@Override
public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS,
"addOnPermissionsChangeListener");
synchronized (mPackages) {
mOnPermissionChangeListeners.addListenerLocked(listener);
}
}
@Override
public void removeOnPermissionsChangeListener(IOnPermissionsChangeListener listener) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
throw new SecurityException("Instant applications don't have access to this method");
}
synchronized (mPackages) {
mOnPermissionChangeListeners.removeListenerLocked(listener);
}
}
@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 (mPackages) {
final PackageParser.Package p1 = mPackages.get(pkg1);
final PackageParser.Package p2 = mPackages.get(pkg2);
if (p1 == null || p1.mExtras == null
|| p2 == null || p2.mExtras == null) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final PackageSetting ps1 = (PackageSetting) p1.mExtras;
final PackageSetting ps2 = (PackageSetting) p2.mExtras;
if (filterAppAccessLPr(ps1, callingUid, callingUserId)
|| filterAppAccessLPr(ps2, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
return compareSignatures(p1.mSigningDetails.signatures, p2.mSigningDetails.signatures);
}
}
@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 (mPackages) {
Signature[] s1;
Signature[] s2;
Object obj = mSettings.getSettingLPr(appId1);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
s1 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
s1 = ps.signatures.mSigningDetails.signatures;
} 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;
}
s2 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
s2 = ps.signatures.mSigningDetails.signatures;
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
return compareSignatures(s1, s2);
}
}
@Override
public boolean hasSigningCertificate(
String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
synchronized (mPackages) {
final PackageParser.Package p = mPackages.get(packageName);
if (p == null || p.mExtras == null) {
return false;
}
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final PackageSetting ps = (PackageSetting) p.mExtras;
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return false;
}
switch (type) {
case CERT_INPUT_RAW_X509:
return p.mSigningDetails.hasCertificate(certificate);
case CERT_INPUT_SHA256:
return p.mSigningDetails.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 (mPackages) {
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 (filterAppAccessLPr(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;
}
}
}
/**
* This method should typically only be used when granting or revoking
* permissions, since the app may immediately restart after this call.
* <p>
* If you're doing surgery on app code/data, use {@link PackageFreezer} to
* guard your work against the app being relaunched.
*/
private void killUid(int appId, int userId, String reason) {
final long identity = Binder.clearCallingIdentity();
try {
IActivityManager am = ActivityManager.getService();
if (am != null) {
try {
am.killUid(appId, userId, reason);
} catch (RemoteException e) {
/* ignore - same process */
}
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
* 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(PackageParser.Package scannedPkg) {
return isCompatSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg));
}
private static boolean isCompatSignatureUpdateNeeded(VersionInfo ver) {
return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY;
}
private boolean isRecoverSignatureUpdateNeeded(PackageParser.Package scannedPkg) {
return isRecoverSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg));
}
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 (mPackages) {
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 (PackageParser.Package pkg : mPackages.values()) {
if (!pkg.visibleToInstantApps) {
continue;
}
result.add(pkg.packageName);
}
} else {
// caller is a normal application; filter instant applications
for (PackageParser.Package pkg : mPackages.values()) {
final PackageSetting ps =
pkg.mExtras != null ? (PackageSetting) pkg.mExtras : null;
if (ps != null
&& ps.getInstantApp(callingUserId)
&& !mInstantAppRegistry.isInstantAccessGranted(
callingUserId, UserHandle.getAppId(callingUid), ps.appId)) {
continue;
}
result.add(pkg.packageName);
}
}
return result;
}
}
@Override
public String[] getPackagesForUid(int uid) {
final int callingUid = Binder.getCallingUid();
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
final int userId = UserHandle.getUserId(uid);
final int appId = UserHandle.getAppId(uid);
// reader
synchronized (mPackages) {
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;
} else {
res = ArrayUtils.removeElement(String.class, res, res[i]);
}
}
return res;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (ps.getInstalled(userId) && !filterAppAccessLPr(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 (mPackages) {
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 (filterAppAccessLPr(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 (mPackages) {
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 (filterAppAccessLPr(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 (mPackages) {
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 (mPackages) {
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 (filterAppAccessLPr(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 (mPackages) {
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 (filterAppAccessLPr(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 (mPackages) {
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;
}
@Override
public String[] getAppOpPermissionPackages(String permName) {
return mPermissionManager.getAppOpPermissionPackages(permName);
}
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
return resolveIntentInternal(intent, resolvedType, flags, 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, int userId, boolean resolveForStart, int filterCallingUid) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart);
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, filterCallingUid, userId, resolveForStart, true /*allowDynamicSplits*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
final ResolveInfo bestChoice =
chooseBestActivity(intent, resolvedType, flags, query, userId);
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 (!sUserManager.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, intent, callingUid, false /*includeInstantApps*/);
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
userId);
synchronized (mPackages) {
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
findPreferredActivity(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 findPreferredActivity(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 (mPackages) {
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,
Bundle verificationBundle, int userId) {
final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO,
new InstantAppRequest(responseObj, origIntent, resolvedType,
callingPackage, userId, verificationBundle, false /*resolveForStart*/));
mHandler.sendMessage(msg);
}
private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
int flags, List<ResolveInfo> query, int userId) {
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 = findPreferredActivity(intent, resolvedType,
flags, query, r0.priority, true, false, debug, userId);
if (ri != null) {
return ri;
}
// If we have an ephemeral app, use it
for (int i = 0; i < N; i++) {
ri = query.get(i);
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;
}
}
}
ri = new ResolveInfo(mResolveInfo);
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;
}
@GuardedBy("mPackages")
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);
}
// TODO: handle preferred activities missing while user has amnesia
ResolveInfo findPreferredActivity(Intent intent, String resolvedType, int flags,
List<ResolveInfo> query, int priority, boolean always,
boolean removeMatches, boolean debug, int userId) {
if (!sUserManager.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, intent, callingUid, false /*includeInstantApps*/);
intent = updateIntentForResolve(intent);
// writer
synchronized (mPackages) {
// 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;
if (ai == null) {
// Do not remove launcher's preferred activity during SetupWizard
// due to it may not install yet
if (excludeSetupWizardHomeActivity) {
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) {
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 (!excludeSetupWizardHomeActivity) {
// 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 for launcher"
+ " during SetupWizard");
}
}
} else {
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);
synchronized(mPackages) {
int flags = updateFlagsForResolve(0, parent.id, intent, callingUid,
false /*includeInstantApps*/);
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 sUserManager.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 (mPackages) {
// 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.packageName : null;
}
}
return null;
}
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, int userId) {
return queryIntentActivitiesInternal(
intent, resolvedType, flags, Binder.getCallingUid(), userId,
false /*resolveForStart*/, true /*allowDynamicSplits*/);
}
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, int filterCallingUid, int userId,
boolean resolveForStart, boolean allowDynamicSplits) {
if (!sUserManager.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, intent, filterCallingUid, resolveForStart,
comp != null || pkgName != null /*onlyExposedExplicitly*/);
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 blockResolution =
!isTargetSameInstantApp
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
if (!blockResolution) {
final ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
}
return applyPostResolutionFilter(
list, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart,
userId, intent);
}
// reader
boolean sortResult = false;
boolean addInstant = false;
List<ResolveInfo> result;
synchronized (mPackages) {
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 PackageParser.Package pkg = mPackages.get(pkgName);
result = null;
if (pkg != null) {
result = filterIfNotSystemUser(mComponentResolver.queryActivities(
intent, resolvedType, flags, pkg.activities, 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) {
result = maybeAddInstantAppInstaller(
result, intent, resolvedType, flags, userId, resolveForStart);
}
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) {
// 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");
final InstantAppRequest requestObject = new InstantAppRequest(
null /*responseObj*/, intent /*origIntent*/, resolvedType,
null /*callingPackage*/, userId, null /*verificationBundle*/,
resolveForStart);
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 (!sUserManager.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 = sUserManager.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(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;
}
// caller is a full app, don't need to apply any other filtering
if (ephemeralPkgName == null) {
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;
}
// allow activities that have been explicitly exposed to ephemeral apps
final boolean isEphemeralApp = info.activityInfo.applicationInfo.isInstantApp();
if (!isEphemeralApp
&& ((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) {
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*/, 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 (mPackages) {
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 = getDefaultBrowserPackageName(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 = sUserManager.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 (!sUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForResolve(flags, userId, intent, callingUid,
false /*includeInstantApps*/);
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 (!sUserManager.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, intent, callingUid,
false /*includeInstantApps*/);
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 (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
final List<ResolveInfo> result =
mComponentResolver.queryReceivers(intent, resolvedType, flags, userId);
return applyPostResolutionFilter(
result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
intent);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
final List<ResolveInfo> result = mComponentResolver.queryReceivers(
intent, resolvedType, flags, pkg.receivers, userId);
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 (!sUserManager.exists(userId)) return null;
flags = updateFlagsForResolve(
flags, userId, intent, callingUid, false /*includeInstantApps*/);
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 (!sUserManager.exists(userId)) return Collections.emptyList();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps);
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 blockResolution =
!isTargetSameInstantApp
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
if (!blockResolution) {
final ResolveInfo ri = new ResolveInfo();
ri.serviceInfo = si;
list.add(ri);
}
}
return list;
}
// reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
return applyPostServiceResolutionFilter(
mComponentResolver.queryServices(intent, resolvedType, flags, userId),
instantAppPkgName);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
return applyPostServiceResolutionFilter(
mComponentResolver.queryServices(intent, resolvedType, flags, pkg.services,
userId),
instantAppPkgName);
}
return Collections.emptyList();
}
}
private List<ResolveInfo> applyPostServiceResolutionFilter(List<ResolveInfo> resolveInfos,
String instantAppPkgName) {
if (instantAppPkgName == null) {
return resolveInfos;
}
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
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 (!sUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, intent, callingUid,
false /*includeInstantApps*/);
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));
if (!blockResolution) {
final ResolveInfo ri = new ResolveInfo();
ri.providerInfo = pi;
list.add(ri);
}
}
return list;
}
// reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
return applyPostContentProviderResolutionFilter(
mComponentResolver.queryProviders(intent, resolvedType, flags, userId),
instantAppPkgName);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
return applyPostContentProviderResolutionFilter(
mComponentResolver.queryProviders(intent, resolvedType, flags,
pkg.providers, userId),
instantAppPkgName);
}
return Collections.emptyList();
}
}
private List<ResolveInfo> applyPostContentProviderResolutionFilter(
List<ResolveInfo> resolveInfos, String instantAppPkgName) {
if (instantAppPkgName == null) {
return resolveInfos;
}
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
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 (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForPackage(flags, userId, null);
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
final boolean listApex = (flags & MATCH_APEX) != 0;
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */,
"get installed packages");
// writer
synchronized (mPackages) {
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 (filterAppAccessLPr(ps, callingUid, userId)) {
continue;
}
final PackageInfo pi = generatePackageInfo(ps, flags, userId);
if (pi != null) {
list.add(pi);
}
}
} else {
list = new ArrayList<>(mPackages.size());
for (PackageParser.Package p : mPackages.values()) {
final PackageSetting ps = (PackageSetting) p.mExtras;
if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
continue;
}
if (filterAppAccessLPr(ps, callingUid, userId)) {
continue;
}
final PackageInfo pi = generatePackageInfo((PackageSetting)
p.mExtras, flags, userId);
if (pi != null) {
list.add(pi);
}
}
}
if (listApex) {
// TODO(b/119767311): include uninstalled/inactive APEX if
// MATCH_UNINSTALLED_PACKAGES is set.
list.addAll(mApexManager.getActivePackages());
}
return new ParceledListSlice<>(list);
}
}
private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageSetting ps,
String[] permissions, boolean[] tmp, int flags, int userId) {
int numMatch = 0;
final PermissionsState permissionsState = ps.getPermissionsState();
for (int i=0; i<permissions.length; i++) {
final String permission = permissions[i];
if (permissionsState.hasPermission(permission, userId)) {
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 (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForPackage(flags, userId, permissions);
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"get packages holding permissions");
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
// writer
synchronized (mPackages) {
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 (PackageParser.Package pkg : mPackages.values()) {
PackageSetting ps = (PackageSetting)pkg.mExtras;
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 (!sUserManager.exists(userId)) return Collections.emptyList();
flags = updateFlagsForApplication(flags, userId, null);
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
mPermissionManager.enforceCrossUserPermission(
callingUid,
userId,
false /* requireFullPermission */,
false /* checkShell */,
"get installed application info");
// writer
synchronized (mPackages) {
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 (filterAppAccessLPr(ps, callingUid, userId)) {
continue;
}
ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags,
ps.readUserState(userId), userId);
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,
callingUid, effectiveFlags, userId);
}
if (ai != null) {
list.add(ai);
}
}
} else {
list = new ArrayList<>(mPackages.size());
for (PackageParser.Package p : mPackages.values()) {
if (p.mExtras != null) {
PackageSetting ps = (PackageSetting) p.mExtras;
if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
continue;
}
if (filterAppAccessLPr(ps, callingUid, userId)) {
continue;
}
ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
ps.readUserState(userId), userId);
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 (mPackages) {
List<InstantAppInfo> instantApps = mInstantAppRegistry
.getInstantAppsLPr(userId);
if (instantApps != null) {
return new ParceledListSlice<>(instantApps);
}
}
return null;
}
@Override
public boolean isInstantApp(String packageName, int userId) {
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"isInstantApp");
if (HIDE_EPHEMERAL_APIS) {
return false;
}
synchronized (mPackages) {
int callingUid = Binder.getCallingUid();
if (Process.isIsolated(callingUid)) {
callingUid = mIsolatedOwners.get(callingUid);
}
final PackageSetting ps = mSettings.mPackages.get(packageName);
PackageParser.Package pkg = 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 (mPackages) {
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 (mPackages) {
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 (mPackages) {
return mInstantAppRegistry.getInstantAppIconLPw(
packageName, userId);
}
}
private boolean isCallerSameApp(String packageName, int uid) {
PackageParser.Package pkg = mPackages.get(packageName);
return pkg != null
&& UserHandle.getAppId(uid) == pkg.applicationInfo.uid;
}
@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 (mPackages) {
final Iterator<PackageParser.Package> i = mPackages.values().iterator();
final int userId = UserHandle.getCallingUserId();
while (i.hasNext()) {
final PackageParser.Package p = i.next();
if (p.applicationInfo == null) continue;
final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0)
&& !p.applicationInfo.isDirectBootAware();
final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0)
&& p.applicationInfo.isDirectBootAware();
if ((p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0
&& (!mSafeMode || isSystemApp(p))
&& (matchesUnaware || matchesAware)) {
PackageSetting ps = mSettings.mPackages.get(p.packageName);
if (ps != null) {
ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
ps.readUserState(userId), userId);
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 (!sUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId, name);
final int callingUid = Binder.getCallingUid();
final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId);
if (providerInfo == null) {
return null;
}
if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) {
return null;
}
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName);
final ComponentName component =
new ComponentName(providerInfo.packageName, providerInfo.name);
if (filterAppAccessLPr(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 (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForComponent(flags, userId, processName);
ArrayList<ProviderInfo> finalList = null;
final List<ProviderInfo> matchList =
mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId);
final int listSize = (matchList == null ? 0 : matchList.size());
synchronized (mPackages) {
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 (filterAppAccessLPr(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 (mPackages) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
if (filterAppAccessLPr(ps, callingUid, component, TYPE_UNKNOWN, callingUserId)) {
return null;
}
final PackageParser.Instrumentation i = mInstrumentation.get(component);
return PackageParser.generateInstrumentationInfo(i, flags);
}
}
@Override
public @NonNull ParceledListSlice<InstrumentationInfo> queryInstrumentation(
String targetPackage, int flags) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final PackageSetting ps = mSettings.mPackages.get(targetPackage);
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return ParceledListSlice.emptyList();
}
return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags));
}
private @NonNull List<InstrumentationInfo> queryInstrumentationInternal(String targetPackage,
int flags) {
ArrayList<InstrumentationInfo> finalList = new ArrayList<>();
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Instrumentation> i = mInstrumentation.values().iterator();
while (i.hasNext()) {
final PackageParser.Instrumentation p = i.next();
if (targetPackage == null
|| targetPackage.equals(p.info.targetPackage)) {
InstrumentationInfo ii = PackageParser.generateInstrumentationInfo(p,
flags);
if (ii != null) {
finalList.add(ii);
}
}
}
}
return finalList;
}
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
scanDirLI(scanDir, parseFlags, scanFlags, currentTime);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
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));
}
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
mParallelPackageParserCallback)) {
// 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.pkg.applicationInfo.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(parseResult.pkg);
}
try {
scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
currentTime, null);
} catch (PackageManagerException e) {
errorCode = e.error;
Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
}
} else if (throwable instanceof PackageParser.PackageParserException) {
PackageParser.PackageParserException e = (PackageParser.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);
}
// 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, PackageParser.Package pkg,
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(pkg.codePath).lastModified() : getLastModifiedTime(pkg);
final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(pkg);
if (ps != null && !forceCollect
&& ps.codePathString.equals(pkg.codePath)
&& 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.
pkg.mSigningDetails =
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, pkg.codePath + " changed; collecting certs" +
(forceCollect ? " (forced)" : ""));
}
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
PackageParser.collectCertificates(pkg, 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 PackageParser.Package currentPkg) {
if (originalPkgSetting == null || !isDeviceUpgrading()) {
return;
}
if (originalPkgSetting.versionCode == currentPkg.mVersionCode) {
return;
}
clearAppProfilesLIF(currentPkg, UserHandle.USER_ALL);
if (DEBUG_INSTALL) {
Slog.d(TAG, originalPkgSetting.name
+ " clear profile due to version change "
+ originalPkgSetting.versionCode + " != "
+ currentPkg.mVersionCode);
}
}
/**
* Traces a package scan.
* @see #scanPackageLI(File, int, int, long, UserHandle)
*/
@GuardedBy({"mInstallLock", "mPackages"})
private PackageParser.Package 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", "mPackages"})
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final PackageParser.Package pkg;
try {
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
// Static shared libraries have synthetic package names
if (pkg.applicationInfo.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(pkg);
}
return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
}
/**
* Scans a package and returns the newly parsed package.
* @throws PackageManagerException on a parse error.
*/
@GuardedBy({"mInstallLock", "mPackages"})
private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
throws PackageManagerException {
// If the package has children and this is the first dive in the function
// we scan the package with the SCAN_CHECK_ONLY flag set to see whether all
// packages (parent and children) would be successfully scanned before the
// actual scan since scanning mutates internal state and we want to atomically
// install the package and its children.
if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
scanFlags |= SCAN_CHECK_ONLY;
}
} else {
scanFlags &= ~SCAN_CHECK_ONLY;
}
// Scan the parent
PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags,
scanFlags, currentTime, user);
// Scan the children
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPackage = pkg.childPackages.get(i);
addForInitLI(childPackage, parseFlags, scanFlags,
currentTime, user);
}
if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
}
return scannedPkg;
}
/**
* Returns if forced apk verification can be skipped for the whole package, including splits.
*/
private boolean canSkipForcedPackageVerification(PackageParser.Package pkg) {
if (!canSkipForcedApkVerification(pkg.baseCodePath)) {
return false;
}
// TODO: Allow base and splits to be verified individually.
if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
for (int i = 0; i < pkg.splitCodePaths.length; i++) {
if (!canSkipForcedApkVerification(pkg.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", "mPackages"})
private PackageParser.Package addForInitLI(PackageParser.Package pkg,
@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;
// NOTE: installPackageLI() has the same code to setup the package's
// application info. This probably should be done lower in the call
// stack [such as scanPackageOnly()]. However, we verify the application
// info prior to that [in scanPackageNew()] and thus have to setup
// the application info early.
pkg.setApplicationVolumeUuid(pkg.volumeUuid);
pkg.setApplicationInfoCodePath(pkg.codePath);
pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
pkg.setApplicationInfoResourcePath(pkg.codePath);
pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
synchronized (mPackages) {
renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
final String realPkgName = getRealPackageName(pkg, renamedPkgName);
if (realPkgName != null) {
ensurePackageRenamed(pkg, renamedPkgName);
}
final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName);
final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName);
pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
pkgAlreadyExists = pkgSetting != null;
final String disabledPkgName = pkgAlreadyExists ? pkgSetting.name : pkg.packageName;
disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName);
isSystemPkgUpdated = disabledPkgSetting != null;
if (DEBUG_INSTALL && isSystemPkgUpdated) {
Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
}
final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null)
? mSettings.getSharedUserLPw(pkg.mSharedUserId,
0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
: null;
if (DEBUG_PACKAGE_SCANNING
&& (parseFlags & PackageParser.PARSE_CHATTY) != 0
&& sharedUserSetting != null) {
Log.d(TAG, "Shared UserID " + pkg.mSharedUserId
+ " (uid=" + sharedUserSetting.userId + "):"
+ " packages=" + sharedUserSetting.packages);
}
if (scanSystemPartition) {
// Potentially prune child packages. If the application on the /system
// partition has been updated via OTA, but, is still disabled by a
// version on /data, cycle through all of its children packages and
// remove children that are no longer defined.
if (isSystemPkgUpdated) {
final int scannedChildCount = (pkg.childPackages != null)
? pkg.childPackages.size() : 0;
final int disabledChildCount = disabledPkgSetting.childPackageNames != null
? disabledPkgSetting.childPackageNames.size() : 0;
for (int i = 0; i < disabledChildCount; i++) {
String disabledChildPackageName =
disabledPkgSetting.childPackageNames.get(i);
boolean disabledPackageAvailable = false;
for (int j = 0; j < scannedChildCount; j++) {
PackageParser.Package childPkg = pkg.childPackages.get(j);
if (childPkg.packageName.equals(disabledChildPackageName)) {
disabledPackageAvailable = true;
break;
}
}
if (!disabledPackageAvailable) {
mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName);
}
}
// we're updating the disabled package, so, scan it as the package setting
final ScanRequest request = new ScanRequest(pkg, sharedUserSetting, null,
disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */,
null /* originalPkgSetting */, null, parseFlags, scanFlags,
(pkg == mPlatformPackage), user);
applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L);
if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) {
scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting);
}
}
}
}
final boolean newPkgChangedPaths =
pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath);
final boolean newPkgVersionGreater =
pkgAlreadyExists && pkg.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 (mPackages) {
// just remove the loaded entries from package lists
mPackages.remove(pkgSetting.name);
}
logCriticalInfo(Log.WARN,
"System package updated;"
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
final InstallArgs args = createInstallArgsForExisting(
pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
args.cleanUpResourcesLI();
synchronized (mPackages) {
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 " + pkg.packageName + " at "
+ pkg.codePath + " ignored: updated version " + pkgSetting.versionCode
+ " better than this " + pkg.getLongVersionCode());
}
// Verify certificates against what was last scanned. If there was an upgrade and this is an
// app in a system partition, or if this is an updated priv app, we will force re-collecting
// certificate.
final boolean forceCollect = (mIsUpgrade && scanSystemPartition)
|| PackageManagerServiceUtils.isApkVerificationForced(disabledPkgSetting);
// 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.
final boolean skipVerify = scanSystemPartition
|| (forceCollect && canSkipForcedPackageVerification(pkg));
collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);
// Reset profile if the application version is changed
maybeClearProfilesForUpgradesLI(pkgSetting, pkg);
/*
* 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 (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails,
PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
&& !pkgSetting.signatures.mSigningDetails.checkCapability(
pkg.mSigningDetails,
PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
logCriticalInfo(Log.WARN,
"System package signature mismatch;"
+ " name: " + pkgSetting.name);
try (PackageFreezer freezer = freezePackage(pkg.packageName,
"scanPackageInternalLI")) {
deletePackageLIF(pkg.packageName, 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 + " --> " + pkg.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
InstallArgs args = createInstallArgsForExisting(
pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
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: " + pkg.codePath + " @ " + pkg.codePath);
}
}
final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
if (scanResult.success) {
synchronized (mPackages) {
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(pkg)),
Collections.singletonMap(pkgName,
getSharedLibLatestVersionSetting(scanResult))),
mSettings.mKeySetManagerService);
prepareScanResultLocked(scanResult);
commitReconciledScanResultLocked(
reconcileResult.get(pkgName));
} catch (PackageManagerException e) {
unprepareScanResultLocked(scanResult);
throw e;
}
}
}
if (shouldHideSystemApp) {
synchronized (mPackages) {
mSettings.disableSystemPackageLPw(pkg.packageName, true);
}
}
return scanResult.pkgSetting.pkg;
}
private static void renameStaticSharedLibraryPackage(PackageParser.Package pkg) {
// Derive the new package synthetic package name
pkg.setPackageName(pkg.packageName + STATIC_SHARED_LIB_DELIMITER
+ pkg.staticSharedLibVersion);
}
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 (mPackages) {
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<PackageParser.Package> pkgs;
synchronized (mPackages) {
pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
}
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(PackageParser.Package pkg) {
return pkg.baseCodePath + ".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<PackageParser.Package> 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 (PackageParser.Package pkg : pkgs) {
numberOfPackagesVisited++;
boolean useProfileForDexopt = false;
if ((isFirstBoot() || isDeviceUpgrading()) && isSystemApp(pkg)) {
// 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.applicationInfo.uid, pkg.packageName,
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.packageName);
// 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.applicationInfo.uid, pkg.packageName,
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 of non-optimizable app " + pkg.packageName);
}
numberOfPackagesSkipped++;
continue;
}
if (DEBUG_DEXOPT) {
Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " +
numberOfPackagesToDexopt + ": " + pkg.packageName);
}
if (showDialog) {
try {
ActivityManager.getService().showBootMessage(
mContext.getResources().getString(R.string.android_upgrading_apk,
numberOfPackagesVisited, numberOfPackagesToDexopt), true);
} catch (RemoteException e) {
}
synchronized (mPackages) {
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.packageName,
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 (mPackages) {
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("mPackages")
public CheckPermissionDelegate getCheckPermissionDelegateLocked() {
return mCheckPermissionDelegate;
}
@GuardedBy("mPackages")
public void setCheckPermissionDelegateLocked(CheckPermissionDelegate delegate) {
mCheckPermissionDelegate = delegate;
}
@GuardedBy("mPackages")
private void notifyPackageUseLocked(String packageName, int reason) {
final PackageParser.Package p = mPackages.get(packageName);
if (p == null) {
return;
}
p.mLastPackageUsageTimeInMills[reason] = System.currentTimeMillis();
}
@Override
public void notifyDexLoad(String loadingPackageName, List<String> classLoaderNames,
List<String> classPaths, String loaderIsa) {
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, classLoaderNames, classPaths, 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) {
PackageParser.Package pkg;
synchronized (mPackages) {
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) {
PackageParser.Package p;
synchronized (mPackages) {
p = mPackages.get(options.getPackageName());
if (p == null) {
// Package could not be found. Report failure.
return PackageDexOptimizer.DEX_OPT_FAILED;
}
mPackageUsage.maybeWriteAsync(mPackages);
mCompilerStats.maybeWriteAsync();
}
long callingId = Binder.clearCallingIdentity();
try {
synchronized (mInstallLock) {
return performDexOptInternalWithDependenciesLI(p, options);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
public ArraySet<String> getOptimizablePackages() {
ArraySet<String> pkgs = new ArraySet<>();
synchronized (mPackages) {
for (PackageParser.Package p : mPackages.values()) {
if (PackageDexOptimizer.canOptimizePackage(p)) {
pkgs.add(p.packageName);
}
}
}
return pkgs;
}
private int performDexOptInternalWithDependenciesLI(PackageParser.Package p,
DexoptOptions 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(p);
final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
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) {
PackageParser.Package depPackage = null;
synchronized (mPackages) {
depPackage = mPackages.get(info.getPackageName());
}
if (depPackage != null) {
// TODO: Analyze and investigate if we (should) profile libraries.
pdo.performDexOpt(depPackage, instructionSets,
getOrCreateCompilerPackageStats(depPackage),
mDexManager.getPackageUseInfoOrDefault(depPackage.packageName),
libraryOptions);
} else {
// TODO(ngeoffray): Support dexopting system shared libraries.
}
}
}
return pdo.performDexOpt(p, instructionSets,
getOrCreateCompilerPackageStats(p),
mDexManager.getPackageUseInfoOrDefault(p.packageName), 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(PackageParser.Package p) {
if (p.usesLibraryInfos != null) {
ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
Set<String> collectedNames = new HashSet<>();
for (SharedLibraryInfo info : p.usesLibraryInfos) {
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<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package pkg) {
List<SharedLibraryInfo> deps = findSharedLibraries(pkg);
if (!deps.isEmpty()) {
ArrayList<PackageParser.Package> retValue = new ArrayList<>();
synchronized (mPackages) {
for (SharedLibraryInfo info : deps) {
PackageParser.Package depPackage = mPackages.get(info.getPackageName());
if (depPackage != null) {
retValue.add(depPackage);
}
}
}
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(PackageParser.Package pkg) {
LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
pkg.staticSharedLibName);
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.staticSharedLibVersion) {
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 (mPackages) {
final SharedLibraryInfo latestSharedLibraVersionLPr =
getLatestSharedLibraVersionLPr(scanResult.pkgSetting.pkg);
if (latestSharedLibraVersionLPr != null) {
sharedLibPackage = mSettings.getPackageLPr(
latestSharedLibraVersionLPr.getPackageName());
}
}
return sharedLibPackage;
}
public void shutdown() {
mPackageUsage.writeNow(mPackages);
mCompilerStats.writeNow();
mDexManager.writePackageDexUsageNow();
PackageWatchdog.getInstance(mContext).writeNow();
// This is the last chance to write out pending restriction settings
synchronized (mPackages) {
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) {
PackageParser.Package pkg;
synchronized (mPackages) {
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.applicationInfo.uid) {
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");
PackageParser.Package pkg;
synchronized (mPackages) {
pkg = mPackages.get(packageName);
if (pkg == 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,
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("mPackages")
private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) {
if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
Slog.w(TAG, "Unable to update from " + oldPkg.name
+ " to " + newPkg.packageName
+ ": 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.packageName
+ ": old package still exists");
return false;
}
return true;
}
@GuardedBy("mInstallLock")
void removeCodePathLI(File codePath) {
if (codePath.isDirectory()) {
try {
mInstaller.rmPackageDir(codePath.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) ? sUserManager.getUserIds() : new int[] { userId };
}
private void clearAppDataLIF(PackageParser.Package pkg, int userId, int flags) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
clearAppDataLeafLIF(pkg, userId, flags);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
clearAppDataLeafLIF(pkg.childPackages.get(i), userId, flags);
}
clearAppProfilesLIF(pkg, UserHandle.USER_ALL);
}
private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
final PackageSetting ps;
synchronized (mPackages) {
ps = mSettings.mPackages.get(pkg.packageName);
}
for (int realUserId : resolveUserIds(userId)) {
final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0;
try {
mInstaller.clearAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags,
ceDataInode);
} catch (InstallerException e) {
Slog.w(TAG, String.valueOf(e));
}
}
}
private void destroyAppDataLIF(PackageParser.Package pkg, int userId, int flags) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
destroyAppDataLeafLIF(pkg, userId, flags);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
destroyAppDataLeafLIF(pkg.childPackages.get(i), userId, flags);
}
}
private void destroyAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
final PackageSetting ps;
synchronized (mPackages) {
ps = mSettings.mPackages.get(pkg.packageName);
}
for (int realUserId : resolveUserIds(userId)) {
final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0;
try {
mInstaller.destroyAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags,
ceDataInode);
} catch (InstallerException e) {
Slog.w(TAG, String.valueOf(e));
}
// If this package doesn't have a sharedUserId or there are no other packages
// present with same sharedUserId, then delete the sandbox data too.
try {
final SharedUserSetting sharedUserSetting = mSettings.getSharedUserLPw(
pkg.mSharedUserId, 0 /* pkgFlags */,
0 /* pkgPrivateFlags */, false /* create */);
boolean deleteSandboxData = true;
if (sharedUserSetting != null && sharedUserSetting.packages != null) {
for (int i = sharedUserSetting.packages.size() - 1; i >= 0; --i) {
final PackageSetting packageSetting = sharedUserSetting.packages.valueAt(i);
if (!packageSetting.name.equals(pkg.packageName)
&& packageSetting.readUserState(realUserId).isAvailable(
MATCH_UNINSTALLED_PACKAGES)) {
deleteSandboxData = false;
break;
}
}
}
if (deleteSandboxData && getStorageManagerInternal() != null) {
getStorageManagerInternal().destroySandboxForApp(pkg.packageName,
pkg.mSharedUserId, realUserId);
}
} catch (PackageManagerException e) {
// Should not happen
}
mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId);
}
}
private void destroyAppProfilesLIF(PackageParser.Package pkg, int userId) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
destroyAppProfilesLeafLIF(pkg);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
destroyAppProfilesLeafLIF(pkg.childPackages.get(i));
}
}
private void destroyAppProfilesLeafLIF(PackageParser.Package pkg) {
try {
mInstaller.destroyAppProfiles(pkg.packageName);
} catch (InstallerException e) {
Slog.w(TAG, String.valueOf(e));
}
}
private void clearAppProfilesLIF(PackageParser.Package pkg, int userId) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
mArtManagerService.clearAppProfiles(pkg);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
mArtManagerService.clearAppProfiles(pkg.childPackages.get(i));
}
}
private void setInstallAndUpdateTime(PackageParser.Package pkg, long firstInstallTime,
long lastUpdateTime) {
// Set parent install/update time
PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps != null) {
ps.firstInstallTime = firstInstallTime;
ps.lastUpdateTime = lastUpdateTime;
}
// Set children install/update time
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPkg = pkg.childPackages.get(i);
ps = (PackageSetting) childPkg.mExtras;
if (ps != null) {
ps.firstInstallTime = firstInstallTime;
ps.lastUpdateTime = lastUpdateTime;
}
}
}
@GuardedBy("mPackages")
private void applyDefiningSharedLibraryUpdateLocked(
PackageParser.Package 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 (pkg.isLibrary()) {
if (pkg.staticSharedLibName != null) {
SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr(
pkg.staticSharedLibName, pkg.staticSharedLibVersion);
if (definedLibrary != null) {
action.accept(definedLibrary, libInfo);
}
} else {
for (String libraryName : pkg.libraryNames) {
SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr(
libraryName, SharedLibraryInfo.VERSION_UNDEFINED);
if (definedLibrary != null) {
action.accept(definedLibrary, libInfo);
}
}
}
}
}
@GuardedBy("mPackages")
private void addSharedLibraryLPr(PackageParser.Package pkg, Set<String> usesLibraryFiles,
SharedLibraryInfo libInfo, PackageParser.Package changingLib) {
if (libInfo.getPath() != null) {
usesLibraryFiles.add(libInfo.getPath());
return;
}
PackageParser.Package p = mPackages.get(libInfo.getPackageName());
if (changingLib != null && changingLib.packageName.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 (p == null || p.packageName.equals(changingLib.packageName)) {
p = changingLib;
}
}
if (p != null) {
usesLibraryFiles.addAll(p.getAllCodePaths());
// If the package provides libraries, add the dependency to them.
applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, (definingLibrary, dependency) -> {
definingLibrary.addDependency(dependency);
});
if (p.usesLibraryFiles != null) {
Collections.addAll(usesLibraryFiles, p.usesLibraryFiles);
}
}
}
@GuardedBy("mPackages")
private void updateSharedLibrariesLocked(PackageParser.Package pkg,
PackageParser.Package changingLib, Map<String, PackageParser.Package> availablePackages)
throws PackageManagerException {
final ArrayList<SharedLibraryInfo> sharedLibraryInfos =
collectSharedLibraryInfos(pkg, availablePackages, mSharedLibraries, null);
executeSharedLibrariesUpdateLPr(pkg, changingLib, sharedLibraryInfos);
}
private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(PackageParser.Package pkg,
Map<String, PackageParser.Package> 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.usesLibraries != null) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.usesLibraries, null, null,
pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, null,
availablePackages, existingLibraries, newLibraries);
}
if (pkg.usesStaticLibraries != null) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.usesStaticLibraries,
pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests,
pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos,
availablePackages, existingLibraries, newLibraries);
}
if (pkg.usesOptionalLibraries != null) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.usesOptionalLibraries,
null, null, pkg.packageName, false, pkg.applicationInfo.targetSdkVersion,
usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
}
return usesLibraryInfos;
}
private void executeSharedLibrariesUpdateLPr(PackageParser.Package pkg,
PackageParser.Package changingLib, ArrayList<SharedLibraryInfo> usesLibraryInfos) {
// 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) {
pkg.usesLibraryInfos = 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);
}
pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[usesLibraryFiles.size()]);
} else {
pkg.usesLibraryInfos = null;
pkg.usesLibraryFiles = null;
}
}
@GuardedBy("mPackages")
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, PackageParser.Package> 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!");
}
PackageParser.Package libPkg =
availablePackages.get(libraryInfo.getPackageName());
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.mSigningDetails.signatures)
: PackageUtils.computeSignaturesSha256Digests(
new Signature[]{libPkg.mSigningDetails.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
if (!libPkg.mSigningDetails.hasSha256Certificate(
ByteStringUtils.fromHexToByteArray(expectedCertDigests[0]))) {
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("mPackages")
private ArrayList<PackageParser.Package> updateAllSharedLibrariesLocked(
PackageParser.Package updatedPkg,
Map<String, PackageParser.Package> availablePackages) {
ArrayList<PackageParser.Package> 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
ArrayList<PackageParser.Package> needsUpdating = null;
if (updatedPkg != null) {
needsUpdating = new ArrayList<>(1);
needsUpdating.add(updatedPkg);
}
do {
final PackageParser.Package changingPkg =
(needsUpdating == null) ? null : needsUpdating.remove(0);
for (int i = mPackages.size() - 1; i >= 0; --i) {
final PackageParser.Package pkg = mPackages.valueAt(i);
if (changingPkg != null
&& !hasString(pkg.usesLibraries, changingPkg.libraryNames)
&& !hasString(pkg.usesOptionalLibraries, changingPkg.libraryNames)
&& !ArrayUtils.contains(pkg.usesStaticLibraries,
changingPkg.staticSharedLibName)) {
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.packageName)) {
descendants.add(pkg.packageName);
needsUpdating.add(pkg);
}
}
try {
updateSharedLibrariesLocked(pkg, changingPkg, 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() || pkg.isUpdatedSystemApp()) {
final int flags = pkg.isUpdatedSystemApp()
? PackageManager.DELETE_KEEP_DATA : 0;
deletePackageLIF(pkg.packageName, null, true, sUserManager.getUserIds(),
flags , null, true, null);
}
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
}
}
} while (needsUpdating != null && needsUpdating.size() > 0);
return resultList;
}
@GuardedBy({"mInstallLock", "mPackages"})
private List<ScanResult> scanPackageTracedLI(PackageParser.Package pkg,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
// If the package has children and this is the first dive in the function
// we recursively scan the package with the SCAN_CHECK_ONLY flag set to see
// whether all packages (parent and children) would be successfully scanned
// before the actual scan since scanning mutates internal state and we want
// to atomically install the package and its children.
if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
scanFlags |= SCAN_CHECK_ONLY;
}
} else {
scanFlags &= ~SCAN_CHECK_ONLY;
}
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
final List<ScanResult> scanResults = new ArrayList<>(1 + childCount);
try {
// Scan the parent
scanResults.add(scanPackageNewLI(pkg, parseFlags, scanFlags, currentTime, user));
// Scan the children
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPkg = pkg.childPackages.get(i);
scanResults.add(scanPackageNewLI(childPkg, parseFlags,
scanFlags, currentTime, user));
}
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
return scanPackageTracedLI(pkg, parseFlags, scanFlags, currentTime, user);
}
return scanResults;
}
/** The result of a package scan. */
private 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 */
private static class ScanRequest {
/** The parsed package */
@NonNull public final PackageParser.Package pkg;
/** The package this package replaces */
@Nullable public final PackageParser.Package 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;
public ScanRequest(
@NonNull PackageParser.Package pkg,
@Nullable SharedUserSetting sharedUserSetting,
@Nullable PackageParser.Package oldPkg,
@Nullable PackageSetting pkgSetting,
@Nullable PackageSetting disabledPkgSetting,
@Nullable PackageSetting originalPkgSetting,
@Nullable String realPkgName,
@ParseFlags int parseFlags,
@ScanFlags int scanFlags,
boolean isPlatformPackage,
@Nullable UserHandle user) {
this.pkg = pkg;
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;
}
}
/**
* 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_PRODUCT_SERVICES}</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,
PackageParser.Package 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_PRODUCT_SERVICES) != 0) {
scanFlags |= SCAN_AS_PRODUCT_SERVICES;
}
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)
&& SystemProperties.getInt("ro.vndk.version", 28) < 28;
if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
&& !pkg.isPrivileged()
&& (pkg.mSharedUserId != null)
&& !skipVendorPrivilegeScan) {
SharedUserSetting sharedUserSetting = null;
try {
sharedUserSetting = mSettings.getSharedUserLPw(pkg.mSharedUserId, 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 (mPackages) {
PackageSetting platformPkgSetting = mSettings.mPackages.get("android");
if ((compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures,
pkg.mSigningDetails.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", "mPackages"})
private ScanResult scanPackageNewLI(@NonNull PackageParser.Package pkg,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
final String renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
final String realPkgName = getRealPackageName(pkg, renamedPkgName);
if (realPkgName != null) {
ensurePackageRenamed(pkg, renamedPkgName);
}
final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName);
final PackageSetting pkgSetting = mSettings.getPackageLPr(pkg.packageName);
final PackageSetting disabledPkgSetting =
mSettings.getDisabledSystemPkgLPr(pkg.packageName);
if (mTransferedPackages.contains(pkg.packageName)) {
Slog.w(TAG, "Package " + pkg.packageName
+ " was transferred to another, but its .apk remains");
}
scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, pkg);
synchronized (mPackages) {
applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
assertPackageIsValid(pkg, parseFlags, scanFlags);
SharedUserSetting sharedUserSetting = null;
if (pkg.mSharedUserId != null) {
// SIDE EFFECTS; may potentially allocate a new shared user
sharedUserSetting = mSettings.getSharedUserLPw(
pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
if (DEBUG_PACKAGE_SCANNING) {
if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
Log.d(TAG, "Shared UserID " + pkg.mSharedUserId
+ " (uid=" + sharedUserSetting.userId + "):"
+ " packages=" + sharedUserSetting.packages);
}
}
final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
originalPkgSetting, realPkgName, parseFlags, scanFlags,
(pkg == mPlatformPackage), user);
return scanPackageOnlyLI(request, mFactoryTest, currentTime);
}
}
/** Prepares the system to commit a {@link ScanResult} in a way that will not fail. */
private void prepareScanResultLocked(@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
mSettings.registerAppIdLPw(result.pkgSetting);
}
}
/**
* Reverts any changes to the system that were made by
* {@link #prepareScanResultLocked(ScanResult)}
*/
private void unprepareScanResultLocked(@NonNull ScanResult result) {
if (!result.existingSettingCopied) {
// 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({"mPackages", "mInstallLock"})
private void commitReconciledScanResultLocked(@NonNull ReconciledPackage reconciledPkg) {
final ScanResult result = reconciledPkg.scanResult;
final ScanRequest request = result.request;
final PackageParser.Package pkg = request.pkg;
final PackageParser.Package 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 (result.existingSettingCopied) {
pkgSetting = request.pkgSetting;
pkgSetting.updateFrom(result.pkgSetting);
pkg.mExtras = pkgSetting;
if (pkgSetting.sharedUser != null
&& pkgSetting.sharedUser.removePackage(result.pkgSetting)) {
pkgSetting.sharedUser.addPackage(pkgSetting);
}
} else {
pkgSetting = result.pkgSetting;
if (originalPkgSetting != null) {
mSettings.addRenamedPackageLPw(pkg.packageName, originalPkgSetting.name);
}
if (originalPkgSetting != null && (scanFlags & SCAN_CHECK_ONLY) == 0) {
mTransferedPackages.add(originalPkgSetting.name);
}
}
// 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.
pkg.applicationInfo.uid = pkgSetting.appId;
mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
if ((scanFlags & SCAN_CHECK_ONLY) == 0 && realPkgName != null) {
mTransferedPackages.add(pkg.packageName);
}
if (reconciledPkg.collectedSharedLibraryInfos != null) {
executeSharedLibrariesUpdateLPr(pkg, null, reconciledPkg.collectedSharedLibraryInfos);
}
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
if (reconciledPkg.removeAppKeySetData) {
ksms.removeAppKeySetDataLPw(pkg.packageName);
}
if (reconciledPkg.sharedUserSignaturesChanged) {
pkgSetting.sharedUser.signaturesChanged = Boolean.TRUE;
pkgSetting.sharedUser.signatures.mSigningDetails = reconciledPkg.signingDetails;
}
pkgSetting.signatures.mSigningDetails = reconciledPkg.signingDetails;
if ((scanFlags & SCAN_CHECK_ONLY) == 0 && pkg.mAdoptPermissions != null) {
// This package wants to adopt ownership of permissions from
// another package.
for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {
final String origName = pkg.mAdoptPermissions.get(i);
final PackageSetting orig = mSettings.getPackageLPr(origName);
if (orig != null) {
if (verifyPackageUpdateLPr(orig, pkg)) {
Slog.i(TAG, "Adopting permissions from " + origName + " to "
+ pkg.packageName);
mSettings.mPermissions.transferPermissions(origName, pkg.packageName);
}
}
}
}
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) {
}
}
}
if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
if (oldPkgSetting != null) {
synchronized (mPackages) {
mSettings.mPackages.put(oldPkgSetting.name, oldPkgSetting);
}
}
} else {
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);
}
}
}
/**
* 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 PackageParser.Package pkg,
@Nullable String renamedPkgName) {
if (isPackageRenamed(pkg, renamedPkgName)) {
return pkg.mRealPackage;
}
return null;
}
/** Returns {@code true} if the package has been renamed. Otherwise, {@code false}. */
private static boolean isPackageRenamed(@NonNull PackageParser.Package pkg,
@Nullable String renamedPkgName) {
return pkg.mOriginalPackages != null && pkg.mOriginalPackages.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("mPackages")
private @Nullable PackageSetting getOriginalPackageLocked(@NonNull PackageParser.Package pkg,
@Nullable String renamedPkgName) {
if (!isPackageRenamed(pkg, renamedPkgName)) {
return null;
}
for (int i = pkg.mOriginalPackages.size() - 1; i >= 0; --i) {
final PackageSetting originalPs =
mSettings.getPackageLPr(pkg.mOriginalPackages.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.mSharedUserId)) {
// the shared user id is incompatible with the original
Slog.w(TAG, "Unable to migrate data from " + originalPs.name
+ " to " + pkg.packageName + ": old uid "
+ originalPs.sharedUser.name
+ " differs from " + pkg.mSharedUserId);
continue;
}
// TODO: Add case when shared user id is added [b/28144775]
} else {
if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
+ pkg.packageName + " 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 PackageParser.Package pkg,
@NonNull String renamedPackageName) {
if (pkg.mOriginalPackages == null
|| !pkg.mOriginalPackages.contains(renamedPackageName)
|| pkg.packageName.equals(renamedPackageName)) {
return;
}
pkg.setPackageName(renamedPackageName);
}
/**
* 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 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")
private static @NonNull ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
boolean isUnderFactoryTest, long currentTime)
throws PackageManagerException {
final PackageParser.Package pkg = request.pkg;
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 " + pkg.packageName);
}
// Initialize package source and resource directories
final File scanFile = new File(pkg.codePath);
final File destCodeFile = new File(pkg.applicationInfo.getCodePath());
final File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
// 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) {
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 " + pkg.packageName + " 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 (pkg.usesStaticLibraries != null) {
usesStaticLibraries = new String[pkg.usesStaticLibraries.size()];
pkg.usesStaticLibraries.toArray(usesStaticLibraries);
}
final boolean createNewPackage = (pkgSetting == null);
if (createNewPackage) {
final String parentPackageName = (pkg.parentPackage != null)
? pkg.parentPackage.packageName : null;
final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
// REMOVE SharedUserSetting from method; update in a separate call
pkgSetting = Settings.createNewSetting(pkg.packageName, originalPkgSetting,
disabledPkgSetting, realPkgName, sharedUserSetting, destCodeFile,
destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi,
pkg.mVersionCode, pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
user, true /*allowInstall*/, instantApp, virtualPreload,
parentPackageName, pkg.getChildPackageNames(),
UserManagerService.getInstance(), usesStaticLibraries,
pkg.usesStaticLibrariesVersions);
} else {
// make a deep copy to avoid modifying any existing system state.
pkgSetting = new PackageSetting(pkgSetting);
pkgSetting.pkg = pkg;
// 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, pkg.applicationInfo.nativeLibraryDir,
pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi,
pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
pkg.getChildPackageNames(), UserManagerService.getInstance(),
usesStaticLibraries, pkg.usesStaticLibrariesVersions);
}
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.
pkg.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(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())) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
// Apps which share a sharedUserId must be placed in the same selinux domain. If this
// package is the first app installed as this shared user, set seInfoTargetSdkVersion to its
// targetSdkVersion. These are later adjusted in PackageManagerService's constructor to be
// the lowest targetSdkVersion of all apps within the shared user, which corresponds to the
// least restrictive selinux domain.
// NOTE: As new packages are installed / updated, the shared user's seinfoTargetSdkVersion
// will NOT be modified until next boot, even if a lower targetSdkVersion is used. This
// ensures that all packages continue to run in the same selinux domain.
final int targetSdkVersion =
((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) ?
sharedUserSetting.seInfoTargetSdkVersion : pkg.applicationInfo.targetSdkVersion;
// TODO(b/71593002): isPrivileged for sharedUser and appInfo should never be out of sync.
// They currently can be if the sharedUser apps are signed with the platform key.
final boolean isPrivileged = (sharedUserSetting != null) ?
sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
pkg.applicationInfo.seInfo = SELinuxMMAC.getSeInfo(pkg, isPrivileged,
pkg.applicationInfo.targetSandboxVersion, targetSdkVersion);
pkg.applicationInfo.seInfoUser = SELinuxUtil.assignSeinfoUser(pkgSetting.readUserState(
userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId));
pkg.mExtras = pkgSetting;
pkg.applicationInfo.processName = fixProcessName(
pkg.applicationInfo.packageName,
pkg.applicationInfo.processName);
if (!isPlatformPackage) {
// Get all of our default paths setup
pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
}
final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
if (needToDeriveAbi) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
final boolean extractNativeLibs = !pkg.isLibrary();
derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs);
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.
if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
pkg.applicationInfo.primaryCpuAbi == null) {
setBundledAppAbisAndRoots(pkg, pkgSetting);
setNativeLibraryPaths(pkg, sAppLib32InstallDir);
}
} 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.
pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings;
pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings;
setNativeLibraryPaths(pkg, sAppLib32InstallDir);
if (DEBUG_ABI_SELECTION) {
Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
pkg.packageName + " " + pkg.applicationInfo.primaryCpuAbi + ", " +
pkg.applicationInfo.secondaryCpuAbi);
}
}
} 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.
pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;
pkg.applicationInfo.secondaryCpuAbi = 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).
setNativeLibraryPaths(pkg, sAppLib32InstallDir);
}
// 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) {
pkg.applicationInfo.primaryCpuAbi = 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 && pkg.packageName != null) {
Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
" for package " + pkg.packageName);
}
}
pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
// Copy the derived override back to the parsed package, so that we can
// update the package settings accordingly.
pkg.cpuAbiOverride = cpuAbiOverride;
if (DEBUG_ABI_SELECTION) {
Slog.d(TAG, "Resolved nativeLibraryRoot for " + pkg.packageName
+ " to root=" + pkg.applicationInfo.nativeLibraryRootDir + ", isa="
+ pkg.applicationInfo.nativeLibraryRootRequiresIsa);
}
// Push the derived path down into PackageSettings so we know what to
// clean up at uninstall time.
pkgSetting.legacyNativeLibraryPathString = pkg.applicationInfo.nativeLibraryRootDir;
if (DEBUG_ABI_SELECTION) {
Log.d(TAG, "Abis for package[" + pkg.packageName + "] are" +
" primary=" + pkg.applicationInfo.primaryCpuAbi +
" secondary=" + pkg.applicationInfo.secondaryCpuAbi);
}
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 =
adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, pkg);
}
if (isUnderFactoryTest && pkg.requestedPermissions.contains(
android.Manifest.permission.FACTORY_TEST)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
}
if (isSystemApp(pkg)) {
pkgSetting.isOrphaned = true;
}
// Take care of first install / last update times.
final long scanFileTime = getLastModifiedTime(pkg);
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);
pkgSetting.pkg = pkg;
pkgSetting.pkgFlags = pkg.applicationInfo.flags;
if (pkg.getLongVersionCode() != pkgSetting.versionCode) {
pkgSetting.versionCode = pkg.getLongVersionCode();
}
// Update volume if needed
final String volumeUuid = pkg.applicationInfo.volumeUuid;
if (!Objects.equals(volumeUuid, pkgSetting.volumeUuid)) {
Slog.i(PackageManagerService.TAG,
"Update" + (pkgSetting.isSystem() ? " system" : "")
+ " package " + pkg.packageName
+ " volume from " + pkgSetting.volumeUuid
+ " to " + volumeUuid);
pkgSetting.volumeUuid = volumeUuid;
}
SharedLibraryInfo staticSharedLibraryInfo = null;
if (!TextUtils.isEmpty(pkg.staticSharedLibName)) {
staticSharedLibraryInfo = SharedLibraryInfo.createForStatic(pkg);
}
List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
if (!ArrayUtils.isEmpty(pkg.libraryNames)) {
dynamicSharedLibraryInfos = new ArrayList<>(pkg.libraryNames.size());
for (String name : pkg.libraryNames) {
dynamicSharedLibraryInfos.add(SharedLibraryInfo.createForDynamic(pkg, 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(PackageParser.Package pkg)
throws PackageManagerException {
final boolean shouldHaveCode =
(pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0;
if (shouldHaveCode && !apkHasCode(pkg.baseCodePath)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Package " + pkg.baseCodePath + " code is missing");
}
if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
for (int i = 0; i < pkg.splitCodePaths.length; i++) {
final boolean splitShouldHaveCode =
(pkg.splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0;
if (splitShouldHaveCode && !apkHasCode(pkg.splitCodePaths[i])) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Package " + pkg.splitCodePaths[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(PackageParser.Package pkg, final @ParseFlags int parseFlags,
final @ScanFlags int scanFlags, PackageParser.Package platformPkg) {
if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
if (pkg.applicationInfo.isDirectBootAware()) {
// we're direct boot aware; set for all components
for (PackageParser.Service s : pkg.services) {
s.info.encryptionAware = s.info.directBootAware = true;
}
for (PackageParser.Provider p : pkg.providers) {
p.info.encryptionAware = p.info.directBootAware = true;
}
for (PackageParser.Activity a : pkg.activities) {
a.info.encryptionAware = a.info.directBootAware = true;
}
for (PackageParser.Activity r : pkg.receivers) {
r.info.encryptionAware = r.info.directBootAware = true;
}
}
if (compressedFileExists(pkg.codePath)) {
pkg.isStub = true;
}
} else {
// non system apps can't be flagged as core
pkg.coreApp = false;
// clear flags not applicable to regular apps
pkg.applicationInfo.flags &=
~ApplicationInfo.FLAG_PERSISTENT;
pkg.applicationInfo.privateFlags &=
~ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE;
pkg.applicationInfo.privateFlags &=
~ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
// cap permission priorities
if (pkg.permissionGroups != null && pkg.permissionGroups.size() > 0) {
for (int i = pkg.permissionGroups.size() - 1; i >= 0; --i) {
pkg.permissionGroups.get(i).info.priority = 0;
}
}
}
if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
// clear protected broadcasts
pkg.protectedBroadcasts = null;
// ignore export request for single user receivers
if (pkg.receivers != null) {
for (int i = pkg.receivers.size() - 1; i >= 0; --i) {
final PackageParser.Activity receiver = pkg.receivers.get(i);
if ((receiver.info.flags & ActivityInfo.FLAG_SINGLE_USER) != 0) {
receiver.info.exported = false;
}
}
}
// ignore export request for single user services
if (pkg.services != null) {
for (int i = pkg.services.size() - 1; i >= 0; --i) {
final PackageParser.Service service = pkg.services.get(i);
if ((service.info.flags & ServiceInfo.FLAG_SINGLE_USER) != 0) {
service.info.exported = false;
}
}
}
// ignore export request for single user providers
if (pkg.providers != null) {
for (int i = pkg.providers.size() - 1; i >= 0; --i) {
final PackageParser.Provider provider = pkg.providers.get(i);
if ((provider.info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0) {
provider.info.exported = false;
}
}
}
}
if ((scanFlags & SCAN_AS_PRIVILEGED) != 0) {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
if ((scanFlags & SCAN_AS_OEM) != 0) {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_OEM;
}
if ((scanFlags & SCAN_AS_VENDOR) != 0) {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_VENDOR;
}
if ((scanFlags & SCAN_AS_PRODUCT) != 0) {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRODUCT;
}
if ((scanFlags & SCAN_AS_PRODUCT_SERVICES) != 0) {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES;
}
if ((scanFlags & SCAN_AS_ODM) != 0) {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ODM;
}
// Check if the package is signed with the same key as the platform package.
if (PLATFORM_PACKAGE_NAME.equals(pkg.packageName) ||
(platformPkg != null && compareSignatures(
platformPkg.mSigningDetails.signatures,
pkg.mSigningDetails.signatures) == PackageManager.SIGNATURE_MATCH)) {
pkg.applicationInfo.privateFlags |=
ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY;
}
if (!isSystemApp(pkg)) {
// Only system apps can use these features.
pkg.mOriginalPackages = null;
pkg.mRealPackage = null;
pkg.mAdoptPermissions = null;
}
PackageBackwardCompatibility.modifySharedLibraries(pkg);
}
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;
}
/**
* 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(PackageParser.Package pkg, final @ParseFlags int parseFlags,
final @ScanFlags int scanFlags)
throws PackageManagerException {
if ((parseFlags & PackageParser.PARSE_ENFORCE_CODE) != 0) {
assertCodePolicy(pkg);
}
if (pkg.applicationInfo.getCodePath() == null ||
pkg.applicationInfo.getResourcePath() == 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");
}
// Make sure we're not adding any bogus keyset info
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
ksms.assertScannedPackageValid(pkg);
synchronized (mPackages) {
// The special "android" package can only be defined once
if (pkg.packageName.equals("android")) {
if (mAndroidApplication != null) {
Slog.w(TAG, "*************************************************");
Slog.w(TAG, "Core android package being redefined. Skipping.");
Slog.w(TAG, " codePath=" + pkg.codePath);
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.packageName)) {
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
"Application package " + pkg.packageName
+ " already installed. Skipping duplicate.");
}
if (pkg.applicationInfo.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.manifestPackageName)) {
throw new PackageManagerException(
"Duplicate static shared lib provider package");
}
// Static shared libraries should have at least O target SDK
if (pkg.applicationInfo.targetSdkVersion < 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.mOriginalPackages)) {
throw new PackageManagerException(
"Packages declaring static-shared libs cannot be renamed");
}
// Package declaring static a shared lib cannot declare child packages
if (!ArrayUtils.isEmpty(pkg.childPackages)) {
throw new PackageManagerException(
"Packages declaring static-shared libs cannot have child packages");
}
// Package declaring static a shared lib cannot declare dynamic libs
if (!ArrayUtils.isEmpty(pkg.libraryNames)) {
throw new PackageManagerException(
"Packages declaring static-shared libs cannot declare dynamic libs");
}
// Package declaring static a shared lib cannot declare shared users
if (pkg.mSharedUserId != null) {
throw new PackageManagerException(
"Packages declaring static-shared libs cannot declare shared users");
}
// Static shared libs cannot declare activities
if (!pkg.activities.isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare activities");
}
// Static shared libs cannot declare services
if (!pkg.services.isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare services");
}
// Static shared libs cannot declare providers
if (!pkg.providers.isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare content providers");
}
// Static shared libs cannot declare receivers
if (!pkg.receivers.isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare broadcast receivers");
}
// Static shared libs cannot declare permission groups
if (!pkg.permissionGroups.isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare permission groups");
}
// Static shared libs cannot declare permissions
if (!pkg.permissions.isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare permissions");
}
// Static shared libs cannot declare protected broadcasts
if (pkg.protectedBroadcasts != null) {
throw new PackageManagerException(
"Static shared libs cannot declare protected broadcasts");
}
// Static shared libs cannot be overlay targets
if (pkg.mOverlayTarget != 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.staticSharedLibName);
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.staticSharedLibVersion) {
minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
} else if (libInfo.getLongVersion() > pkg.staticSharedLibVersion) {
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");
}
}
// Only privileged apps and updated privileged apps can add child packages.
if (pkg.childPackages != null && !pkg.childPackages.isEmpty()) {
if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
throw new PackageManagerException("Only privileged apps can add child "
+ "packages. Ignoring package " + pkg.packageName);
}
final int childCount = pkg.childPackages.size();
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPkg = pkg.childPackages.get(i);
if (mSettings.hasOtherDisabledSystemPkgWithChildLPr(pkg.packageName,
childPkg.packageName)) {
throw new PackageManagerException("Can't override child of "
+ "another disabled app. Ignoring package " + pkg.packageName);
}
}
}
// 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.packageName)) {
logCriticalInfo(Log.WARN,
"Relax SCAN_REQUIRE_KNOWN requirement for package " + pkg.packageName);
} else {
PackageSetting known = mSettings.getPackageLPr(pkg.packageName);
if (known != null) {
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Examining " + pkg.codePath
+ " and requiring known paths " + known.codePathString
+ " & " + known.resourcePathString);
}
if (!pkg.applicationInfo.getCodePath().equals(known.codePathString)
|| !pkg.applicationInfo.getResourcePath().equals(
known.resourcePathString)) {
throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
"Application package " + pkg.packageName
+ " found at " + pkg.applicationInfo.getCodePath()
+ " but expected at " + known.codePathString
+ "; ignoring.");
}
} else {
throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Application package " + pkg.packageName
+ " 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);
}
// Verify that packages sharing a user with a privileged app are marked as privileged.
if (!pkg.isPrivileged() && (pkg.mSharedUserId != null)) {
SharedUserSetting sharedUserSetting = null;
try {
sharedUserSetting = mSettings.getSharedUserLPw(pkg.mSharedUserId, 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.mSigningDetails.signatures)
!= PackageManager.SIGNATURE_MATCH)) {
throw new PackageManagerException("Apps that share a user with a " +
"privileged app must themselves be marked as privileged. " +
pkg.packageName + " shares privileged user " +
pkg.mSharedUserId + ".");
}
}
}
// Apply policies specific for runtime resource overlays (RROs).
if (pkg.mOverlayTarget != 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.
final PackageSetting previousPkg = assertNotNull(
mSettings.getPackageLPr(pkg.packageName),
"previous package state not present");
// previousPkg.pkg may be null: the package will be not be scanned if the
// package manager knows there is a newer version on /data.
// TODO[b/79435695]: Find a better way to keep track of the "static"
// property for RROs instead of having to parse packages on /system
PackageParser.Package ppkg = previousPkg.pkg;
if (ppkg == null) {
try {
final PackageParser pp = new PackageParser();
ppkg = pp.parsePackage(previousPkg.codePath,
parseFlags | PackageParser.PARSE_IS_SYSTEM_DIR);
} catch (PackageParserException e) {
Slog.w(TAG, "failed to parse " + previousPkg.codePath, e);
}
}
// Static overlays cannot be updated.
if (ppkg != null && ppkg.mOverlayIsStatic) {
throw new PackageManagerException("Overlay " + pkg.packageName +
" is static and cannot be upgraded.");
// Non-static overlays cannot be converted to static overlays.
} else if (pkg.mOverlayIsStatic) {
throw new PackageManagerException("Overlay " + pkg.packageName +
" cannot be upgraded into a static overlay.");
}
}
} else {
// The overlay is a non-system overlay. Non-system overlays cannot be static.
if (pkg.mOverlayIsStatic) {
throw new PackageManagerException("Overlay " + pkg.packageName +
" is static but not pre-installed.");
}
// 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.applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q) {
final PackageSetting platformPkgSetting =
mSettings.getPackageLPr("android");
if ((platformPkgSetting.signatures.mSigningDetails
!= PackageParser.SigningDetails.UNKNOWN)
&& (compareSignatures(
platformPkgSetting.signatures.mSigningDetails.signatures,
pkg.mSigningDetails.signatures)
!= PackageManager.SIGNATURE_MATCH)) {
throw new PackageManagerException("Overlay " + pkg.packageName
+ " 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.mOverlayTargetName == null) {
final PackageSetting targetPkgSetting =
mSettings.getPackageLPr(pkg.mOverlayTarget);
if (targetPkgSetting != null) {
if ((targetPkgSetting.signatures.mSigningDetails
!= PackageParser.SigningDetails.UNKNOWN)
&& (compareSignatures(
targetPkgSetting.signatures.mSigningDetails.signatures,
pkg.mSigningDetails.signatures)
!= PackageManager.SIGNATURE_MATCH)) {
throw new PackageManagerException("Overlay " + pkg.packageName
+ " and target " + pkg.mOverlayTarget + " signed with"
+ " different certificates, and the overlay lacks"
+ " <overlay android:targetName>");
}
}
}
}
}
}
}
@GuardedBy("mPackages")
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("mPackages")
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("mPackages")
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);
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(PackageParser.Package pkg,
@Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting,
final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) {
final String pkgName = pkg.packageName;
if (mCustomResolverComponentName != null &&
mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
setUpCustomResolverActivity(pkg);
}
if (pkg.packageName.equals("android")) {
synchronized (mPackages) {
if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
// Set up information for our fall-back user intent resolution activity.
mPlatformPackage = pkg;
pkg.mVersionCode = mSdkVersion;
pkg.mVersionCodeMajor = 0;
mAndroidApplication = pkg.applicationInfo;
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<PackageParser.Package> clientLibPkgs = null;
// writer
synchronized (mPackages) {
if (!ArrayUtils.isEmpty(reconciledPkg.allowedSharedLibraryInfos)) {
for (SharedLibraryInfo info : reconciledPkg.allowedSharedLibraryInfos) {
commitSharedLibraryInfoLocked(info);
}
final Map<String, PackageParser.Package> combinedPackages =
reconciledPkg.getCombinedPackages();
try {
// Shared libraries for the package need to be updated.
updateSharedLibrariesLocked(pkg, null, combinedPackages);
} 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, combinedPackages);
}
}
}
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++) {
PackageParser.Package clientPkg = clientLibPkgs.get(i);
killApplication(clientPkg.applicationInfo.packageName,
clientPkg.applicationInfo.uid, "update lib");
}
}
// writer
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
synchronized (mPackages) {
// 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.applicationInfo.packageName, pkg);
// Add the package's KeySets to the global KeySetManagerService
KeySetManagerService ksms = mSettings.mKeySetManagerService;
ksms.addScannedPackageLPw(pkg);
mComponentResolver.addAllComponents(pkg, chatty);
// 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.packageName
+ " ignored: instant apps cannot define new permission groups.");
} else {
mPermissionManager.addAllPermissionGroups(pkg, chatty);
}
// Don't allow ephemeral applications to define new permissions.
if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
Slog.w(TAG, "Permissions from package " + pkg.packageName
+ " ignored: instant apps cannot define new permissions.");
} else {
mPermissionManager.addAllPermissions(pkg, chatty);
}
int collectionSize = pkg.instrumentation.size();
StringBuilder r = null;
int i;
for (i = 0; i < collectionSize; i++) {
PackageParser.Instrumentation a = pkg.instrumentation.get(i);
a.info.packageName = pkg.applicationInfo.packageName;
a.info.sourceDir = pkg.applicationInfo.sourceDir;
a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir;
a.info.splitNames = pkg.splitNames;
a.info.splitSourceDirs = pkg.applicationInfo.splitSourceDirs;
a.info.splitPublicSourceDirs = pkg.applicationInfo.splitPublicSourceDirs;
a.info.splitDependencies = pkg.applicationInfo.splitDependencies;
a.info.dataDir = pkg.applicationInfo.dataDir;
a.info.deviceProtectedDataDir = pkg.applicationInfo.deviceProtectedDataDir;
a.info.credentialProtectedDataDir = pkg.applicationInfo.credentialProtectedDataDir;
a.info.primaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
a.info.secondaryCpuAbi = pkg.applicationInfo.secondaryCpuAbi;
a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
a.info.secondaryNativeLibraryDir = pkg.applicationInfo.secondaryNativeLibraryDir;
mInstrumentation.put(a.getComponentName(), a);
if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(a.info.name);
}
}
if (r != null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Instrumentation: " + r);
}
if (pkg.protectedBroadcasts != null) {
collectionSize = pkg.protectedBroadcasts.size();
synchronized (mProtectedBroadcasts) {
for (i = 0; i < collectionSize; i++) {
mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
}
}
}
if (oldPkg != null) {
// 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(() ->
mPermissionManager.revokeRuntimePermissionsIfGroupChanged(pkg, oldPkg,
allPackageNames, mPermissionCallback));
}
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
/**
* Derive the ABI of a non-system package located at {@code scanFile}. This information
* is derived purely on the basis of the contents of {@code scanFile} and
* {@code cpuAbiOverride}.
*
* If {@code extractLibs} is true, native libraries are extracted from the app if required.
*/
private static void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride,
boolean extractLibs)
throws PackageManagerException {
// Give ourselves some initial paths; we'll come back for another
// pass once we've determined ABI below.
setNativeLibraryPaths(pkg, sAppLib32InstallDir);
// We shouldn't attempt to extract libs from system app when it was not updated.
if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
extractLibs = false;
}
final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir;
final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa;
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(pkg);
// TODO(multiArch): This can be null for apps that didn't go through the
// usual installation process. We can calculate it again, like we
// do during install time.
//
// TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally
// unnecessary.
final File nativeLibraryRoot = new File(nativeLibraryRootStr);
// Null out the abis so that they can be recalculated.
pkg.applicationInfo.primaryCpuAbi = null;
pkg.applicationInfo.secondaryCpuAbi = null;
if (isMultiArch(pkg.applicationInfo)) {
// Warn if we've set an abiOverride for multi-lib packages..
// By definition, we need to copy both 32 and 64 bit libraries for
// such packages.
if (pkg.cpuAbiOverride != null
&& !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {
Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
}
int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
if (extractLibs) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
useIsaSpecificSubdirs);
} else {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
// Shared library native code should be in the APK zip aligned
if (abi32 >= 0 && pkg.isLibrary() && extractLibs) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Shared library native lib extraction not supported");
}
maybeThrowExceptionForMultiArchCopy(
"Error unpackaging 32 bit native libs for multiarch app.", abi32);
if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
if (extractLibs) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
useIsaSpecificSubdirs);
} else {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
maybeThrowExceptionForMultiArchCopy(
"Error unpackaging 64 bit native libs for multiarch app.", abi64);
if (abi64 >= 0) {
// Shared library native libs should be in the APK zip aligned
if (extractLibs && pkg.isLibrary()) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Shared library native lib extraction not supported");
}
pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
}
if (abi32 >= 0) {
final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
if (abi64 >= 0) {
if (pkg.use32bitAbi) {
pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
pkg.applicationInfo.primaryCpuAbi = abi;
} else {
pkg.applicationInfo.secondaryCpuAbi = abi;
}
} else {
pkg.applicationInfo.primaryCpuAbi = abi;
}
}
} else {
String[] abiList = (cpuAbiOverride != null) ?
new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
// Enable gross and lame hacks for apps that are built with old
// SDK tools. We must scan their APKs for renderscript bitcode and
// not launch them if it's present. Don't bother checking on devices
// that don't have 64 bit support.
boolean needsRenderScriptOverride = false;
if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
abiList = Build.SUPPORTED_32_BIT_ABIS;
needsRenderScriptOverride = true;
}
final int copyRet;
if (extractLibs) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
} else {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Error unpackaging native libs for app, errorCode=" + copyRet);
}
if (copyRet >= 0) {
// Shared libraries that have native libs must be multi-architecture
if (pkg.isLibrary()) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Shared library with native libs must be multiarch");
}
pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];
} else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) {
pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
} else if (needsRenderScriptOverride) {
pkg.applicationInfo.primaryCpuAbi = abiList[0];
}
}
} catch (IOException ioe) {
Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
} finally {
IoUtils.closeQuietly(handle);
}
// Now that we've calculated the ABIs and determined if it's an internal app,
// we will go ahead and populate the nativeLibraryPath.
setNativeLibraryPaths(pkg, sAppLib32InstallDir);
}
/**
* Adjusts ABIs for a set of packages belonging to a shared user so that they all match.
* i.e, so that all packages can be run inside a single process if required.
*
* Optionally, callers can pass in a parsed package via {@code newPackage} in which case
* this function will either try and make the ABI for all packages in {@code packagesForUser}
* match {@code scannedPackage} or will update the ABI of {@code scannedPackage} to match
* the ABI selected for {@code packagesForUser}. This variant is used when installing or
* updating a package that belongs to a shared user.
*
* NOTE: We currently only match for the primary CPU abi string. Matching the secondary
* adds unnecessary complexity.
*/
private static @Nullable List<String> adjustCpuAbisForSharedUserLPw(
Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage) {
List<String> changedAbiCodePath = null;
String requiredInstructionSet = null;
if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) {
requiredInstructionSet = VMRuntime.getInstructionSet(
scannedPackage.applicationInfo.primaryCpuAbi);
}
PackageSetting requirer = null;
for (PackageSetting ps : packagesForUser) {
// If packagesForUser contains scannedPackage, we skip it. This will happen
// when scannedPackage is an update of an existing package. Without this check,
// we will never be able to change the ABI of any package belonging to a shared
// user, even if it's compatible with other packages.
if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) {
if (ps.primaryCpuAbiString == null) {
continue;
}
final String instructionSet = VMRuntime.getInstructionSet(ps.primaryCpuAbiString);
if (requiredInstructionSet != null && !instructionSet.equals(requiredInstructionSet)) {
// We have a mismatch between instruction sets (say arm vs arm64) warn about
// this but there's not much we can do.
String errorMessage = "Instruction set mismatch, "
+ ((requirer == null) ? "[caller]" : requirer)
+ " requires " + requiredInstructionSet + " whereas " + ps
+ " requires " + instructionSet;
Slog.w(TAG, errorMessage);
}
if (requiredInstructionSet == null) {
requiredInstructionSet = instructionSet;
requirer = ps;
}
}
}
if (requiredInstructionSet != null) {
String adjustedAbi;
if (requirer != null) {
// requirer != null implies that either scannedPackage was null or that scannedPackage
// did not require an ABI, in which case we have to adjust scannedPackage to match
// the ABI of the set (which is the same as requirer's ABI)
adjustedAbi = requirer.primaryCpuAbiString;
if (scannedPackage != null) {
scannedPackage.applicationInfo.primaryCpuAbi = adjustedAbi;
}
} else {
// requirer == null implies that we're updating all ABIs in the set to
// match scannedPackage.
adjustedAbi = scannedPackage.applicationInfo.primaryCpuAbi;
}
for (PackageSetting ps : packagesForUser) {
if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) {
if (ps.primaryCpuAbiString != null) {
continue;
}
ps.primaryCpuAbiString = adjustedAbi;
if (ps.pkg != null && ps.pkg.applicationInfo != null &&
!TextUtils.equals(adjustedAbi, ps.pkg.applicationInfo.primaryCpuAbi)) {
ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
if (DEBUG_ABI_SELECTION) {
Slog.i(TAG, "Adjusting ABI for " + ps.name + " to " + adjustedAbi
+ " (requirer="
+ (requirer != null ? requirer.pkg : "null")
+ ", scannedPackage="
+ (scannedPackage != null ? scannedPackage : "null")
+ ")");
}
if (changedAbiCodePath == null) {
changedAbiCodePath = new ArrayList<>();
}
changedAbiCodePath.add(ps.codePathString);
}
}
}
}
return changedAbiCodePath;
}
private void setUpCustomResolverActivity(PackageParser.Package pkg) {
synchronized (mPackages) {
mResolverReplaced = true;
// Set up information for custom user intent resolution activity.
mResolveActivity.applicationInfo = pkg.applicationInfo;
mResolveActivity.name = mCustomResolverComponentName.getClassName();
mResolveActivity.packageName = pkg.applicationInfo.packageName;
mResolveActivity.processName = pkg.applicationInfo.packageName;
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 static String calculateBundledApkRoot(final String codePathString) {
final File codePath = new File(codePathString);
final File codeRoot;
if (FileUtils.contains(Environment.getRootDirectory(), codePath)) {
codeRoot = Environment.getRootDirectory();
} else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) {
codeRoot = Environment.getOemDirectory();
} else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) {
codeRoot = Environment.getVendorDirectory();
} else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
codeRoot = Environment.getOdmDirectory();
} else if (FileUtils.contains(Environment.getProductDirectory(), codePath)) {
codeRoot = Environment.getProductDirectory();
} else if (FileUtils.contains(Environment.getProductServicesDirectory(), codePath)) {
codeRoot = Environment.getProductServicesDirectory();
} else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
codeRoot = Environment.getOdmDirectory();
} else {
// Unrecognized code path; take its top real segment as the apk root:
// e.g. /something/app/blah.apk => /something
try {
File f = codePath.getCanonicalFile();
File parent = f.getParentFile(); // non-null because codePath is a file
File tmp;
while ((tmp = parent.getParentFile()) != null) {
f = parent;
parent = tmp;
}
codeRoot = f;
Slog.w(TAG, "Unrecognized code path "
+ codePath + " - using " + codeRoot);
} catch (IOException e) {
// Can't canonicalize the code path -- shenanigans?
Slog.w(TAG, "Can't canonicalize code path " + codePath);
return Environment.getRootDirectory().getPath();
}
}
return codeRoot.getPath();
}
/**
* Derive and set the location of native libraries for the given package,
* which varies depending on where and how the package was installed.
*/
private static void setNativeLibraryPaths(PackageParser.Package pkg, File appLib32InstallDir) {
final ApplicationInfo info = pkg.applicationInfo;
final String codePath = pkg.codePath;
final File codeFile = new File(codePath);
final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
info.nativeLibraryRootDir = null;
info.nativeLibraryRootRequiresIsa = false;
info.nativeLibraryDir = null;
info.secondaryNativeLibraryDir = null;
if (isApkFile(codeFile)) {
// Monolithic install
if (bundledApp) {
// If "/system/lib64/apkname" exists, assume that is the per-package
// native library directory to use; otherwise use "/system/lib/apkname".
final String apkRoot = calculateBundledApkRoot(info.sourceDir);
final boolean is64Bit = VMRuntime.is64BitInstructionSet(
getPrimaryInstructionSet(info));
// This is a bundled system app so choose the path based on the ABI.
// if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
// is just the default path.
final String apkName = deriveCodePathName(codePath);
final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,
apkName).getAbsolutePath();
if (info.secondaryCpuAbi != null) {
final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;
info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
secondaryLibDir, apkName).getAbsolutePath();
}
} else {
final String apkName = deriveCodePathName(codePath);
info.nativeLibraryRootDir = new File(appLib32InstallDir, apkName)
.getAbsolutePath();
}
info.nativeLibraryRootRequiresIsa = false;
info.nativeLibraryDir = info.nativeLibraryRootDir;
} else {
// Cluster install
info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
info.nativeLibraryRootRequiresIsa = true;
info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
getPrimaryInstructionSet(info)).getAbsolutePath();
if (info.secondaryCpuAbi != null) {
info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir,
VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath();
}
}
}
/**
* Calculate the abis and roots for a bundled app. These can uniquely
* be determined from the contents of the system partition, i.e whether
* it contains 64 or 32 bit shared libraries etc. We do not validate any
* of this information, and instead assume that the system was built
* sensibly.
*/
private static void setBundledAppAbisAndRoots(PackageParser.Package pkg,
PackageSetting pkgSetting) {
final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
// If "/system/lib64/apkname" exists, assume that is the per-package
// native library directory to use; otherwise use "/system/lib/apkname".
final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir);
setBundledAppAbi(pkg, apkRoot, apkName);
// pkgSetting might be null during rescan following uninstall of updates
// to a bundled app, so accommodate that possibility. The settings in
// that case will be established later from the parsed package.
//
// If the settings aren't null, sync them up with what we've just derived.
// note that apkRoot isn't stored in the package settings.
if (pkgSetting != null) {
pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
}
}
/**
* Deduces the ABI of a bundled app and sets the relevant fields on the
* parsed pkg object.
*
* @param apkRoot the root of the installed apk, something like {@code /system} or {@code /oem}
* under which system libraries are installed.
* @param apkName the name of the installed package.
*/
private static void setBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) {
final File codeFile = new File(pkg.codePath);
final boolean has64BitLibs;
final boolean has32BitLibs;
if (isApkFile(codeFile)) {
// Monolithic install
has64BitLibs = (new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath())).exists();
has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists();
} else {
// Cluster install
final File rootDir = new File(codeFile, LIB_DIR_NAME);
if (!ArrayUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS)
&& !TextUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS[0])) {
final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_64_BIT_ABIS[0]);
has64BitLibs = (new File(rootDir, isa)).exists();
} else {
has64BitLibs = false;
}
if (!ArrayUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS)
&& !TextUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS[0])) {
final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]);
has32BitLibs = (new File(rootDir, isa)).exists();
} else {
has32BitLibs = false;
}
}
if (has64BitLibs && !has32BitLibs) {
// The package has 64 bit libs, but not 32 bit libs. Its primary
// ABI should be 64 bit. We can safely assume here that the bundled
// native libraries correspond to the most preferred ABI in the list.
pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
pkg.applicationInfo.secondaryCpuAbi = null;
} else if (has32BitLibs && !has64BitLibs) {
// The package has 32 bit libs but not 64 bit libs. Its primary
// ABI should be 32 bit.
pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
pkg.applicationInfo.secondaryCpuAbi = null;
} else if (has32BitLibs && has64BitLibs) {
// The application has both 64 and 32 bit bundled libraries. We check
// here that the app declares multiArch support, and warn if it doesn't.
//
// We will be lenient here and record both ABIs. The primary will be the
// ABI that's higher on the list, i.e, a device that's configured to prefer
// 64 bit apps will see a 64 bit primary ABI,
if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) {
Slog.e(TAG, "Package " + pkg + " has multiple bundled libs, but is not multiarch.");
}
if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) {
pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
} else {
pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
}
} else {
pkg.applicationInfo.primaryCpuAbi = null;
pkg.applicationInfo.secondaryCpuAbi = null;
}
}
private void killApplication(String pkgName, int appId, String reason) {
killApplication(pkgName, appId, UserHandle.USER_ALL, reason);
}
private void killApplication(String pkgName, int appId, 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(PackageParser.Package pkg, boolean chatty) {
// Remove the parent package setting
PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps != null) {
removePackageLI(ps.name, chatty);
} else if (DEBUG_REMOVE && chatty) {
Log.d(TAG, "Not removing package " + pkg.packageName + "; mExtras == null");
}
// Remove the child package setting
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPkg = pkg.childPackages.get(i);
ps = (PackageSetting) childPkg.mExtras;
if (ps != null) {
removePackageLI(ps.name, chatty);
}
}
}
void removePackageLI(String packageName, boolean chatty) {
if (DEBUG_INSTALL) {
if (chatty)
Log.d(TAG, "Removing package " + packageName);
}
// writer
synchronized (mPackages) {
final PackageParser.Package removedPackage = mPackages.remove(packageName);
if (removedPackage != null) {
cleanPackageDataStructuresLILPw(removedPackage, chatty);
}
}
}
void removeInstalledPackageLI(PackageParser.Package pkg, boolean chatty) {
if (DEBUG_INSTALL) {
if (chatty)
Log.d(TAG, "Removing package " + pkg.applicationInfo.packageName);
}
// writer
synchronized (mPackages) {
// Remove the parent package
mPackages.remove(pkg.applicationInfo.packageName);
cleanPackageDataStructuresLILPw(pkg, chatty);
// Remove the child packages
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPkg = pkg.childPackages.get(i);
mPackages.remove(childPkg.applicationInfo.packageName);
cleanPackageDataStructuresLILPw(childPkg, chatty);
}
}
}
void cleanPackageDataStructuresLILPw(PackageParser.Package pkg, boolean chatty) {
mComponentResolver.removeAllComponents(pkg, chatty);
mPermissionManager.removeAllPermissions(pkg, chatty);
final int instrumentationSize = pkg.instrumentation.size();
StringBuilder r = null;
int i;
for (i = 0; i < instrumentationSize; i++) {
PackageParser.Instrumentation a = pkg.instrumentation.get(i);
mInstrumentation.remove(a.getComponentName());
if (DEBUG_REMOVE && chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(a.info.name);
}
}
if (r != null) {
if (DEBUG_REMOVE) Log.d(TAG, " Instrumentation: " + r);
}
r = null;
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
// Only system apps can hold shared libraries.
if (pkg.libraryNames != null) {
final int libraryNamesSize = pkg.libraryNames.size();
for (i = 0; i < libraryNamesSize; i++) {
String name = pkg.libraryNames.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.staticSharedLibName != null) {
if (removeSharedLibraryLPw(pkg.staticSharedLibName, pkg.staticSharedLibVersion)) {
if (DEBUG_REMOVE && chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(pkg.staticSharedLibName);
}
}
}
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) {
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);
if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) {
doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
instantUserIds, true);
}
} catch (RemoteException ex) {
}
});
}
@Override
public void notifyPackageAdded(String packageName, int uid) {
final PackageListObserver[] observers;
synchronized (mPackages) {
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 (mPackages) {
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 (mPackages) {
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)
throws RemoteException {
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);
}
am.broadcastIntent(null, intent, null, finishedReceiver,
0, null, null, requiredPermissions, android.app.AppOpsManager.OP_NONE,
null, finishedReceiver != null, false, 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) {
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);
// 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, int appId, int[] userIds, int[] instantUserIds) {
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);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
packageName, extras, 0, null, null, userIds, instantUserIds);
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 (!mUserManagerInternal.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.broadcastIntent(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 (mUserManagerInternal.isUserUnlockingOrUnlocked(userId)) {
Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED).setPackage(packageName);
if (includeStopped) {
bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
}
am.broadcastIntent(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 (mPackages) {
pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null) {
return false;
}
if (filterAppAccessLPr(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.
PackageParser.Package pkg = mPackages.get(packageName);
if (pkg != null && pkg.staticSharedLibName != null) {
Slog.w(TAG, "Cannot hide package: " + packageName
+ " providing static shared library: "
+ pkg.staticSharedLibName);
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);
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) {
enforceSystemOrPhoneCaller("setSystemAppHiddenUntilInstalled");
synchronized (mPackages) {
final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null || !pkgSetting.isSystem()) {
return;
}
PackageParser.Package pkg = pkgSetting.pkg;
if (pkg != null && pkg.applicationInfo != null) {
pkg.applicationInfo.hiddenUntilInstalled = hidden;
}
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(packageName);
if (disabledPs == null) {
return;
}
pkg = disabledPs.pkg;
if (pkg != null && pkg.applicationInfo != null) {
pkg.applicationInfo.hiddenUntilInstalled = hidden;
}
}
}
@Override
public boolean setSystemAppInstallState(String packageName, boolean installed, int userId) {
enforceSystemOrPhoneCaller("setSystemAppInstallState");
synchronized (mPackages) {
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,
0 /*installFlags*/,
PackageManager.INSTALL_REASON_DEVICE_SETUP);
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.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);
}
private void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId,
boolean suspended, PersistableBundle launcherExtras) {
final Bundle extras = new Bundle(3);
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
if (launcherExtras != null) {
extras.putBundle(Intent.EXTRA_LAUNCHER_EXTRAS,
new Bundle(launcherExtras.deepCopy()));
}
sendPackageBroadcast(
suspended ? Intent.ACTION_PACKAGES_SUSPENDED
: Intent.ACTION_PACKAGES_UNSUSPENDED,
null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
new int[] {userId}, 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 (mPackages) {
ps = mSettings.mPackages.get(packageName);
if (ps == null) {
return true;
}
if (filterAppAccessLPr(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) {
return installExistingPackageAsUser(packageName, userId, installFlags, installReason, null);
}
int installExistingPackageAsUser(String packageName, int userId, int installFlags,
int installReason, IntentSender intentSender) {
if (DEBUG_INSTALL) {
Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId
+ " installFlags=" + installFlags + " installReason=" + installReason);
}
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 (mPackages) {
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 : sUserManager.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);
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(pkgSetting, userId, instantApp, fullApp);
}
if (installed) {
if (pkgSetting.pkg != null) {
synchronized (mInstallLock) {
// We don't need to freeze for a brand new install
prepareAppDataAfterInstallLIF(pkgSetting.pkg);
}
}
sendPackageAddedForUser(packageName, pkgSetting, userId);
synchronized (mPackages) {
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 = intentSender == null ? null :
new PostInstallData(null, res, () -> 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(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 : sUserManager.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 = sUserManager.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);
}
Preconditions.checkNotNull(packageNames, "packageNames cannot be null");
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 (mPackages) {
pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null || filterAppAccessLPr(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 (mPackages) {
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 (mPackages) {
scheduleWritePackageRestrictionsLocked(userId);
}
}
return unactionedPackages.toArray(new String[0]);
}
@Override
public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
PersistableBundle appExtras, PersistableBundle launcherExtras,
SuspendDialogInfo dialogInfo, String callingPackage, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
"setPackagesSuspendedAsUser");
final int callingUid = Binder.getCallingUid();
final int packageUid = getPackageUid(callingPackage, 0, userId);
final boolean allowedCallingUid = callingUid == Process.ROOT_UID
|| callingUid == Process.SYSTEM_UID;
final boolean allowedPackageUid = packageUid == callingUid;
final boolean allowedShell = callingUid == SHELL_UID
&& UserHandle.isSameApp(packageUid, callingUid);
if (!allowedCallingUid && !allowedShell && !allowedPackageUid) {
throw new SecurityException("Calling package " + callingPackage + " in user "
+ userId + " does not belong to calling uid " + callingUid);
}
if (!PLATFORM_PACKAGE_NAME.equals(callingPackage)
&& mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId) != null) {
throw new UnsupportedOperationException("Cannot suspend/unsuspend packages. User "
+ userId + " has an active DO or PO");
}
if (ArrayUtils.isEmpty(packageNames)) {
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 (mPackages) {
pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null || filterAppAccessLPr(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;
}
synchronized (mPackages) {
pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
launcherExtras, userId);
}
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, launcherExtras);
sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, appExtras, userId);
synchronized (mPackages) {
scheduleWritePackageRestrictionsLocked(userId);
}
}
return unactionedPackages.toArray(new String[unactionedPackages.size()]);
}
@Override
public PersistableBundle 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);
}
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
throw new IllegalArgumentException("Unknown target package: " + packageName);
}
final PackageUserState packageUserState = ps.readUserState(userId);
if (packageUserState.suspended) {
return packageUserState.suspendedAppExtras;
}
return null;
}
}
private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended,
PersistableBundle appExtras, int userId) {
final String action;
final Bundle intentExtras = new Bundle();
if (suspended) {
action = Intent.ACTION_MY_PACKAGE_SUSPENDED;
if (appExtras != null) {
final Bundle bundledAppExtras = new Bundle(appExtras.deepCopy());
intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, bundledAppExtras);
}
} else {
action = Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
}
mHandler.post(() -> {
try {
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) {
doSendBroadcast(am, action, null, intentExtras,
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
targetUserIds, false);
}
} catch (RemoteException ex) {
// Shouldn't happen as AMS is in the same process.
}
});
}
@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 (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
throw new IllegalArgumentException("Unknown target package: " + packageName);
}
return ps.getSuspended(userId);
}
}
/**
* Immediately unsuspends any packages suspended by the given package. To be called
* when such a package's data is cleared or it is removed from the device.
*
* <p><b>Should not be used on a frequent code path</b> as it flushes state to disk
* synchronously
*
* @param packageName The package holding {@link Manifest.permission#SUSPEND_APPS} permission
* @param affectedUser The user for which the changes are taking place.
*/
void unsuspendForSuspendingPackage(final String packageName, int affectedUser) {
final int[] userIds = (affectedUser == UserHandle.USER_ALL) ? sUserManager.getUserIds()
: new int[] {affectedUser};
for (int userId : userIds) {
unsuspendForSuspendingPackages(packageName::equals, userId);
}
}
/**
* Immediately unsuspends any packages in the given users not suspended by the platform or root.
* To be called when a profile owner or a device owner is added.
*
* <p><b>Should not be used on a frequent code path</b> as it flushes state to disk
* synchronously
*
* @param userIds The users for which to unsuspend packages
*/
void unsuspendForNonSystemSuspendingPackages(ArraySet<Integer> userIds) {
final int sz = userIds.size();
for (int i = 0; i < sz; i++) {
unsuspendForSuspendingPackages(
(suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage),
userIds.valueAt(i));
}
}
private void unsuspendForSuspendingPackages(Predicate<String> packagePredicate, int userId) {
final List<String> affectedPackages = new ArrayList<>();
final IntArray affectedUids = new IntArray();
synchronized (mPackages) {
for (PackageSetting ps : mSettings.mPackages.values()) {
final PackageUserState pus = ps.readUserState(userId);
if (pus.suspended && packagePredicate.test(pus.suspendingPackage)) {
ps.setSuspended(false, null, null, null, null, userId);
affectedPackages.add(ps.name);
affectedUids.add(UserHandle.getUid(userId, ps.getAppId()));
}
}
}
if (!affectedPackages.isEmpty()) {
final String[] packageArray = affectedPackages.toArray(
new String[affectedPackages.size()]);
sendMyPackageSuspendedOrUnsuspended(packageArray, false, null, userId);
sendPackagesSuspendedForUser(
packageArray, affectedUids.toArray(), userId, false, null);
// Write package restrictions immediately to avoid an inconsistent state.
mSettings.writePackageRestrictionsLPr(userId);
}
}
@Override
public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) {
Preconditions.checkNotNull("packageNames cannot be null", packageNames);
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);
}
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]);
}
}
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 long callingId = Binder.clearCallingIdentity();
try {
final String activeLauncherPackageName = getActiveLauncherPackageName(userId);
final String dialerPackageName = getDefaultDialerPackageName(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 (mPackages) {
if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
+ "\": protected package");
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.
PackageParser.Package pkg = mPackages.get(packageName);
if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) {
Slog.w(TAG, "Cannot suspend package: " + packageName
+ " providing static shared library: "
+ pkg.staticSharedLibName);
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;
}
private String getDefaultDialerPackageName(int userId) {
synchronized (mPackages) {
return mSettings.getDefaultDialerPackageNameLPw(userId);
}
}
@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, 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);
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 (mPackages) {
final PackageParser.Package pkg = mPackages.get(verifierInfo.packageName);
if (pkg == null) {
return -1;
} else if (pkg.mSigningDetails.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.mSigningDetails.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.applicationInfo.uid;
}
}
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() {
return android.provider.Settings.Global.getLong(mContext.getContentResolver(),
android.provider.Settings.Global.PACKAGE_VERIFIER_TIMEOUT,
DEFAULT_VERIFICATION_TIMEOUT);
}
/**
* Get the default verification agent response code.
*
* @return default verification response code
*/
private int getDefaultVerificationResponse(UserHandle user) {
if (sUserManager.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);
}
/**
* Check whether or not package verification has been enabled.
*
* @return true if verification should be performed
*/
private boolean isVerificationEnabled(int userId, int installFlags, int installerUid) {
if (!DEFAULT_VERIFY_ENABLE) {
return false;
}
if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
return false;
}
boolean ensureVerifyAppsEnabled = isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS);
// Check if installing from ADB
if ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0) {
// Do not run verification in a test harness environment
if (ActivityManager.isRunningInTestHarness()) {
return false;
}
if (ensureVerifyAppsEnabled) {
return true;
}
// Check if the developer does not want package verification for ADB installs
if (android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) == 0) {
return false;
}
} else {
// 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 {
mContext.getSystemService(AppOpsManager.class)
.checkPackage(installerUid, mRequiredVerifierPackage);
if (DEBUG_VERIFY) {
Slog.i(TAG, "disable verification for instant app");
}
return false;
} catch (SecurityException ignore) { }
}
}
}
if (ensureVerifyAppsEnabled) {
return true;
}
return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 1;
}
@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 (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null
|| filterAppAccessLPr(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 (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (filterAppAccessLPr(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 (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (filterAppAccessLPr(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 (mPackages) {
PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null || pkg.activities == null) {
return ParceledListSlice.emptyList();
}
if (pkg.mExtras == null) {
return ParceledListSlice.emptyList();
}
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return ParceledListSlice.emptyList();
}
final int count = pkg.activities.size();
ArrayList<IntentFilter> result = new ArrayList<>();
for (int n=0; n<count; n++) {
PackageParser.Activity activity = pkg.activities.get(n);
if (activity.intents != null && activity.intents.size() > 0) {
result.addAll(activity.intents);
}
}
return new ParceledListSlice<>(result);
}
}
@Override
public boolean setDefaultBrowserPackageName(String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
if (userId == UserHandle.USER_ALL) {
return false;
}
PackageManagerInternal.DefaultBrowserProvider provider;
synchronized (mPackages) {
provider = mDefaultBrowserProvider;
}
if (provider == null) {
Slog.e(TAG, "mDefaultBrowserProvider is null");
return false;
}
boolean successful = provider.setDefaultBrowser(packageName, userId);
if (!successful) {
return false;
}
if (packageName != null) {
synchronized (mPackages) {
mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowser(packageName,
userId);
}
}
return true;
}
private void setDefaultBrowserAsyncLPw(@Nullable String packageName, @UserIdInt int userId) {
if (userId == UserHandle.USER_ALL) {
return;
}
if (mDefaultBrowserProvider == null) {
Slog.e(TAG, "mDefaultBrowserProvider is null");
return;
}
mDefaultBrowserProvider.setDefaultBrowserAsync(packageName, userId);
if (packageName != null) {
synchronized (mPackages) {
mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowser(packageName,
userId);
}
}
}
@Override
public String getDefaultBrowserPackageName(int userId) {
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
PackageManagerInternal.DefaultBrowserProvider provider;
synchronized (mPackages) {
provider = mDefaultBrowserProvider;
}
if (provider == null) {
Slog.e(TAG, "mDefaultBrowserProvider is null");
return null;
}
return provider.getDefaultBrowser(userId);
}
/**
* 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 (mPackages) {
PackageSetting targetPackageSetting = mSettings.mPackages.get(targetPackage);
if (targetPackageSetting == null
|| filterAppAccessLPr(
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.
if (targetPackageSetting.installerPackageName != null) {
PackageSetting setting = mSettings.mPackages.get(
targetPackageSetting.installerPackageName);
// If the currently set package isn't valid, then it's always
// okay to change it.
if (setting != null) {
if (compareSignatures(callerSignature,
setting.signatures.mSigningDetails.signatures)
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as old installer package "
+ targetPackageSetting.installerPackageName);
}
}
}
// Okay!
targetPackageSetting.installerPackageName = installerPackageName;
if (installerPackageName != null) {
mSettings.mInstallerPackages.add(installerPackageName);
}
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");
}
mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
callerPackageName);
synchronized (mPackages) {
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) {
throw new IllegalArgumentException("Unknown target package " + packageName);
}
if (filterAppAccessLPr(ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
throw new IllegalArgumentException("Unknown target package " + packageName);
}
if (!Objects.equals(callerPackageName, ps.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.packageName);
}
// 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;
final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;
boolean doRestore = !update
&& ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);
// 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.
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.isBackupServiceActive(userId)) {
bm.restoreAtInstallForUser(
userId, res.pkg.applicationInfo.packageName, token);
} else {
doRestore = false;
}
} catch (RemoteException e) {
// can't happen; the backup manager is local
} catch (Exception e) {
Slog.e(TAG, "Exception trying to enqueue restore", e);
doRestore = false;
}
} else {
Slog.e(TAG, "Backup Manager not found!");
doRestore = false;
}
}
// 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 restored for the package.
//
// TODO(narayan): Get this working for cases where userId == UserHandle.USER_ALL.
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && !doRestore && update) {
IRollbackManager rm = IRollbackManager.Stub.asInterface(
ServiceManager.getService(Context.ROLLBACK_SERVICE));
final String packageName = res.pkg.applicationInfo.packageName;
final String seInfo = res.pkg.applicationInfo.seInfo;
final int[] allUsers = sUserManager.getUserIds();
final int[] installedUsers;
final PackageSetting ps;
int appId = -1;
long ceDataInode = -1;
synchronized (mSettings) {
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);
}
if (ps != null) {
try {
rm.restoreUserData(packageName, installedUsers, appId, ceDataInode,
seInfo, token);
} catch (RemoteException re) {
// Cannot happen, the RollbackManager is hosted in the same process.
}
doRestore = true;
}
}
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);
}
}
/**
* 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.applicationInfo.packageName)) {
// 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);
}
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;
}
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 String dataAppName;
final int appId;
final String seinfo;
final int targetSdkVersion;
public MoveInfo(int moveId, String fromUuid, String toUuid, String packageName,
String dataAppName, int appId, String seinfo, int targetSdkVersion) {
this.moveId = moveId;
this.fromUuid = fromUuid;
this.toUuid = toUuid;
this.packageName = packageName;
this.dataAppName = dataAppName;
this.appId = appId;
this.seinfo = seinfo;
this.targetSdkVersion = targetSdkVersion;
}
}
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;
break;
}
}
}
@Override
void handleReturnCode() {
for (InstallParams params : mChildParams) {
params.handleReturnCode();
if (params.mRet != INSTALL_SUCCEEDED) {
mRet = params.mRet;
break;
}
}
}
void tryProcessInstallRequest(InstallArgs args, int currentStatus) {
mCurrentState.put(args, currentStatus);
boolean success = true;
if (mCurrentState.size() != mChildParams.size()) {
return;
}
for (Integer status : mCurrentState.values()) {
if (status == PackageManager.INSTALL_UNKNOWN) {
return;
} else if (status != PackageManager.INSTALL_SUCCEEDED) {
success = false;
break;
}
}
final List<InstallRequest> installRequests = new ArrayList<>(mCurrentState.size());
for (Map.Entry<InstallArgs, Integer> entry : mCurrentState.entrySet()) {
installRequests.add(new InstallRequest(entry.getKey(),
createPackageInstalledInfo(entry.getValue())));
}
processInstallRequestsAsync(success, 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;
final String installerPackageName;
final String volumeUuid;
private boolean mVerificationCompleted;
private boolean mEnableRollbackCompleted;
private InstallArgs mArgs;
int mRet;
final String packageAbiOverride;
final String[] grantedRuntimePermissions;
final List<String> whitelistedRestrictedPermissions;
final VerificationInfo verificationInfo;
final PackageParser.SigningDetails signingDetails;
final int installReason;
@Nullable
MultiPackageInstallParams mParentInstallParams;
InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, String volumeUuid,
VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
PackageParser.SigningDetails signingDetails, int installReason) {
super(user);
this.origin = origin;
this.move = move;
this.observer = observer;
this.installFlags = installFlags;
this.installerPackageName = installerPackageName;
this.volumeUuid = volumeUuid;
this.verificationInfo = verificationInfo;
this.packageAbiOverride = packageAbiOverride;
this.grantedRuntimePermissions = grantedPermissions;
this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
this.signingDetails = signingDetails;
this.installReason = installReason;
}
InstallParams(ActiveInstallSession activeInstallSession) {
super(activeInstallSession.getUser());
if (DEBUG_INSTANT) {
if ((activeInstallSession.getSessionParams().installFlags
& PackageManager.INSTALL_INSTANT_APP) != 0) {
Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName());
}
}
verificationInfo = new VerificationInfo(
activeInstallSession.getSessionParams().originatingUri,
activeInstallSession.getSessionParams().referrerUri,
activeInstallSession.getSessionParams().originatingUid,
activeInstallSession.getInstallerUid());
origin = OriginInfo.fromStagedFile(activeInstallSession.getStagedDir());
move = null;
installReason = fixUpInstallReason(activeInstallSession.getInstallerPackageName(),
activeInstallSession.getInstallerUid(),
activeInstallSession.getSessionParams().installReason);
observer = activeInstallSession.getObserver();
installFlags = activeInstallSession.getSessionParams().installFlags;
installerPackageName = activeInstallSession.getInstallerPackageName();
volumeUuid = activeInstallSession.getSessionParams().volumeUuid;
packageAbiOverride = activeInstallSession.getSessionParams().abiOverride;
grantedRuntimePermissions = activeInstallSession.getSessionParams()
.grantedRuntimePermissions;
whitelistedRestrictedPermissions = activeInstallSession.getSessionParams()
.whitelistedRestrictedPermissions;
signingDetails = activeInstallSession.getSigningDetails();
}
@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 (mPackages) {
// Currently installed package which the new package is attempting to replace or
// null if no such package is installed.
PackageParser.Package 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.
PackageParser.Package dataOwnerPkg = installedPkg;
if (dataOwnerPkg == null) {
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
dataOwnerPkg = ps.pkg;
}
}
if (dataOwnerPkg != null) {
if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
dataOwnerPkg.applicationInfo.flags)) {
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.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
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 (isExternal(installedPkg)) {
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 (!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;
mEnableRollbackCompleted = true;
mArgs = args;
if (ret == PackageManager.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());
final int installerUid =
verificationInfo == null ? -1 : verificationInfo.installerUid;
if (!origin.existing && requiredUid != -1
&& isVerificationEnabled(
verifierUser.getIdentifier(), installFlags, installerUid)) {
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");
}
final int verificationId = mPendingVerificationToken++;
verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE,
installerPackageName);
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());
if (verificationInfo != null) {
if (verificationInfo.originatingUri != null) {
verification.putExtra(Intent.EXTRA_ORIGINATING_URI,
verificationInfo.originatingUri);
}
if (verificationInfo.referrer != null) {
verification.putExtra(Intent.EXTRA_REFERRER,
verificationInfo.referrer);
}
if (verificationInfo.originatingUid >= 0) {
verification.putExtra(Intent.EXTRA_ORIGINATING_UID,
verificationInfo.originatingUid);
}
if (verificationInfo.installerUid >= 0) {
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID,
verificationInfo.installerUid);
}
}
final PackageVerificationState verificationState = new PackageVerificationState(
requiredUid, this);
mPendingVerification.append(verificationId, verificationState);
final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
receivers, verificationState);
DeviceIdleController.LocalService idleController = getDeviceIdleController();
final long idleDuration = getVerificationTimeout();
/*
* 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);
}
}
}
final ComponentName requiredVerifierComponent = matchComponentForVerifier(
mRequiredVerifierPackage, receivers);
if (ret == PackageManager.INSTALL_SUCCEEDED
&& mRequiredVerifierPackage != null) {
Trace.asyncTraceBegin(
TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
/*
* 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,
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);
/*
* We don't want the copy to proceed until verification
* succeeds.
*/
mVerificationCompleted = false;
}
}
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);
final int[] installedUsers;
synchronized (mPackages) {
PackageSetting ps = mSettings.getPackageLPr(pkgLite.packageName);
if (ps != null) {
installedUsers = ps.queryInstalledUsers(sUserManager.getUserIds(),
true);
} else {
installedUsers = new int[0];
}
}
Intent enableRollbackIntent = new Intent(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
enableRollbackIntent.putExtra(
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN,
enableRollbackToken);
enableRollbackIntent.putExtra(
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS,
installFlags);
enableRollbackIntent.putExtra(
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALLED_USERS,
installedUsers);
enableRollbackIntent.putExtra(
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_USER,
getUser().getIdentifier());
enableRollbackIntent.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
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;
mHandler.sendMessageDelayed(msg, rollbackTimeout);
}
}, null, 0, null, null);
mEnableRollbackCompleted = false;
}
}
mRet = ret;
}
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() {
mVerificationCompleted = true;
handleReturnCode();
}
void handleRollbackEnabled() {
// TODO(ruhler) b/112431924: Consider halting the install if we
// couldn't enable rollback.
mEnableRollbackCompleted = true;
handleReturnCode();
}
@Override
void handleReturnCode() {
if (mVerificationCompleted && mEnableRollbackCompleted) {
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;
final String installerPackageName;
final String volumeUuid;
final UserHandle user;
final String abiOverride;
final String[] installGrantPermissions;
final List<String> whitelistedRestrictedPermissions;
/** If non-null, drop an async trace when the install completes */
final String traceMethod;
final int traceCookie;
final PackageParser.SigningDetails signingDetails;
final int installReason;
@Nullable final MultiPackageInstallParams mMultiPackageInstallParams;
// 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, String installerPackageName, String volumeUuid,
UserHandle user, String[] instructionSets,
String abiOverride, String[] installGrantPermissions,
List<String> whitelistedRestrictedPermissions,
String traceMethod, int traceCookie, SigningDetails signingDetails,
int installReason,
MultiPackageInstallParams multiPackageInstallParams) {
this.origin = origin;
this.move = move;
this.installFlags = installFlags;
this.observer = observer;
this.installerPackageName = installerPackageName;
this.volumeUuid = volumeUuid;
this.user = user;
this.instructionSets = instructionSets;
this.abiOverride = abiOverride;
this.installGrantPermissions = installGrantPermissions;
this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
this.traceMethod = traceMethod;
this.traceCookie = traceCookie;
this.signingDetails = signingDetails;
this.installReason = installReason;
this.mMultiPackageInstallParams = multiPackageInstallParams;
}
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, PackageParser.Package pkg);
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.origin, params.move, params.observer, params.installFlags,
params.installerPackageName, params.volumeUuid,
params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions,
params.traceMethod, params.traceCookie, params.signingDetails,
params.installReason, params.mParentInstallParams);
}
/** Existing install */
FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
super(OriginInfo.fromNothing(), null, null, 0, null, null, null, instructionSets,
null, null, null, null, 0, PackageParser.SigningDetails.UNKNOWN,
PackageManager.INSTALL_REASON_UNKNOWN, null /* parent */);
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 File libraryRoot = new File(codeFile, LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(codeFile);
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
abiOverride);
} 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;
}
boolean doRename(int status, PackageParser.Package pkg) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp();
return false;
}
final File targetDir = codeFile.getParentFile();
final File beforeCodeFile = codeFile;
final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName);
if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
try {
Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
} catch (ErrnoException e) {
Slog.w(TAG, "Failed to rename", e);
return false;
}
if (!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 {
pkg.setCodePath(afterCodeFile.getCanonicalPath());
} catch (IOException e) {
Slog.e(TAG, "Failed to get path: " + afterCodeFile, e);
return false;
}
pkg.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
afterCodeFile, pkg.baseCodePath));
pkg.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
afterCodeFile, pkg.splitCodePaths));
// Reflect the rename in app info
pkg.setApplicationVolumeUuid(pkg.volumeUuid);
pkg.setApplicationInfoCodePath(pkg.codePath);
pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
pkg.setApplicationInfoResourcePath(pkg.codePath);
pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
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;
}
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;
}
}
private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws
PackageManagerException {
if (copyRet < 0) {
if (copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
throw new PackageManagerException(copyRet, message);
}
}
}
/**
* 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.origin, params.move, params.observer, params.installFlags,
params.installerPackageName, params.volumeUuid,
params.getUser(), null /* instruction sets */, params.packageAbiOverride,
params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions,
params.traceMethod, params.traceCookie, params.signingDetails,
params.installReason, params.mParentInstallParams);
}
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.dataAppName, move.appId, move.seinfo, move.targetSdkVersion);
} catch (InstallerException e) {
Slog.w(TAG, "Failed to move app", e);
return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
}
}
codeFile = new File(Environment.getDataAppDirectory(move.toUuid), move.dataAppName);
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;
}
boolean doRename(int status, PackageParser.Package pkg) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp(move.toUuid);
return false;
}
// Reflect the move in app info
pkg.setApplicationVolumeUuid(pkg.volumeUuid);
pkg.setApplicationInfoCodePath(pkg.codePath);
pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
pkg.setApplicationInfoResourcePath(pkg.codePath);
pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
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 File codeFile = new File(Environment.getDataAppDirectory(volumeUuid),
move.dataAppName);
Slog.d(TAG, "Cleaning up " + move.packageName + " on " + volumeUuid);
final int[] userIds = sUserManager.getUserIds();
synchronized (mInstallLock) {
// Clean up both app data and code
// All package moves are frozen until finished
for (int userId : userIds) {
try {
mInstaller.destroyAppData(volumeUuid, move.packageName, userId,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE, 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();
}
}
private File getNextCodePath(File targetDir, String packageName) {
File result;
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[16];
do {
random.nextBytes(bytes);
String suffix = Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP);
result = new File(targetDir, packageName + "-" + suffix);
} while (result.exists());
return result;
}
// Utility method that returns the relative package path with respect
// to the installation directory. Like say for /data/data/com.test-1.apk
// string com.test-1 is returned.
static String deriveCodePathName(String codePath) {
if (codePath == null) {
return null;
}
final File codeFile = new File(codePath);
final String name = codeFile.getName();
if (codeFile.isDirectory()) {
return name;
} else if (name.endsWith(".apk") || name.endsWith(".tmp")) {
final int lastDot = name.lastIndexOf('.');
return name.substring(0, lastDot);
} else {
Slog.w(TAG, "Odd, " + codePath + " doesn't look like an APK");
return null;
}
}
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;
PackageParser.Package pkg;
int returnCode;
String returnMsg;
String installerPackageName;
PackageRemovedInfo removedInfo;
ArrayMap<String, PackageInstalledInfo> addedChildPackages;
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));
final int childCount = (addedChildPackages != null) ? addedChildPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
addedChildPackages.valueAt(i).setError(msg, e);
}
Slog.w(TAG, msg, e);
}
public void setError(String msg, PackageManagerException e) {
returnCode = e.error;
setReturnMessage(ExceptionUtils.getCompleteMessage(msg, e));
final int childCount = (addedChildPackages != null) ? addedChildPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
addedChildPackages.valueAt(i).setError(msg, e);
}
Slog.w(TAG, msg, e);
}
public void setReturnCode(int returnCode) {
this.returnCode = returnCode;
final int childCount = (addedChildPackages != null) ? addedChildPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
addedChildPackages.valueAt(i).returnCode = returnCode;
}
}
private void setReturnMessage(String returnMsg) {
this.returnMsg = returnMsg;
final int childCount = (addedChildPackages != null) ? addedChildPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
addedChildPackages.valueAt(i).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
}
}
/**
* Checks whether the parent or any of the child packages have a change shared
* user. For a package to be a valid update the shred users of the parent and
* the children should match. We may later support changing child shared users.
* @param oldPkg The updated package.
* @param newPkg The update package.
* @return The shared user that change between the versions.
*/
private String getParentOrChildPackageChangedSharedUser(PackageParser.Package oldPkg,
PackageParser.Package newPkg) {
// Check parent shared user
if (!Objects.equals(oldPkg.mSharedUserId, newPkg.mSharedUserId)) {
return newPkg.packageName;
}
// Check child shared users
final int oldChildCount = (oldPkg.childPackages != null) ? oldPkg.childPackages.size() : 0;
final int newChildCount = (newPkg.childPackages != null) ? newPkg.childPackages.size() : 0;
for (int i = 0; i < newChildCount; i++) {
PackageParser.Package newChildPkg = newPkg.childPackages.get(i);
// If this child was present, did it have the same shared user?
for (int j = 0; j < oldChildCount; j++) {
PackageParser.Package oldChildPkg = oldPkg.childPackages.get(j);
if (newChildPkg.packageName.equals(oldChildPkg.packageName)
&& !Objects.equals(newChildPkg.mSharedUserId, oldChildPkg.mSharedUserId)) {
return newChildPkg.packageName;
}
}
}
return null;
}
private void removeNativeBinariesLI(PackageSetting ps) {
// Remove the lib path for the parent package
if (ps != null) {
NativeLibraryHelper.removeNativeBinariesLI(ps.legacyNativeLibraryPathString);
// Remove the lib path for the child packages
final int childCount = (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageSetting childPs = null;
synchronized (mPackages) {
childPs = mSettings.getPackageLPr(ps.childPackageNames.get(i));
}
if (childPs != null) {
NativeLibraryHelper.removeNativeBinariesLI(childPs
.legacyNativeLibraryPathString);
}
}
}
}
@GuardedBy("mPackages")
private void enableSystemPackageLPw(PackageParser.Package pkg) {
// Enable the parent package
mSettings.enableSystemPackageLPw(pkg.packageName);
// Enable the child packages
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPkg = pkg.childPackages.get(i);
mSettings.enableSystemPackageLPw(childPkg.packageName);
}
}
@GuardedBy("mPackages")
private boolean disableSystemPackageLPw(PackageParser.Package oldPkg,
PackageParser.Package newPkg) {
// Disable the parent package (parent always replaced)
boolean disabled = mSettings.disableSystemPackageLPw(oldPkg.packageName, true);
// Disable the child packages
final int childCount = (oldPkg.childPackages != null) ? oldPkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPkg = oldPkg.childPackages.get(i);
final boolean replace = newPkg.hasChildPackage(childPkg.packageName);
disabled |= mSettings.disableSystemPackageLPw(childPkg.packageName, replace);
}
return disabled;
}
@GuardedBy("mPackages")
private void setInstallerPackageNameLPw(PackageParser.Package pkg,
String installerPackageName) {
// Enable the parent package
mSettings.setInstallerPackageName(pkg.packageName, installerPackageName);
// Enable the child packages
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPkg = pkg.childPackages.get(i);
mSettings.setInstallerPackageName(childPkg.packageName, installerPackageName);
}
}
private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
int[] allUsers, PackageInstalledInfo res, UserHandle user, int installReason) {
// Update the parent package setting
updateSettingsInternalLI(newPackage, installerPackageName, allUsers, res.origUsers,
res, user, installReason);
// Update the child packages setting
final int childCount = (newPackage.childPackages != null)
? newPackage.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPackage = newPackage.childPackages.get(i);
PackageInstalledInfo childRes = res.addedChildPackages.get(childPackage.packageName);
updateSettingsInternalLI(childPackage, installerPackageName, allUsers,
childRes.origUsers, childRes, user, installReason);
}
}
private void updateSettingsInternalLI(PackageParser.Package pkg,
String installerPackageName, int[] allUsers, int[] installedForUsers,
PackageInstalledInfo res, UserHandle user, int installReason) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
final String pkgName = pkg.packageName;
if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.codePath);
synchronized (mPackages) {
// NOTE: This changes slightly to include UPDATE_PERMISSIONS_ALL regardless of the size of pkg.permissions
mPermissionManager.updatePermissions(pkg.packageName, pkg, true, mPackages.values(),
mPermissionCallback);
// 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.
PackageSetting ps = mSettings.mPackages.get(pkgName);
final int userId = user.getIdentifier();
if (ps != null) {
if (isSystemApp(pkg)) {
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().
}
}
// It's implied that when a user requests installation, they want the app to be
// installed and enabled.
if (userId != UserHandle.USER_ALL) {
ps.setInstalled(true, userId);
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
}
// When replacing an existing package, preserve the original install reason for all
// users that had the package installed before.
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);
}
}
// Set install reason for users that are having the package newly installed.
if (userId == UserHandle.USER_ALL) {
for (int currentUserId : sUserManager.getUserIds()) {
if (!previousUserIds.contains(currentUserId)) {
ps.setInstallReason(installReason, currentUserId);
}
}
} else if (!previousUserIds.contains(userId)) {
ps.setInstallReason(installReason, userId);
}
mSettings.writeKernelMappingLPr(ps);
}
res.name = pkgName;
res.uid = pkg.applicationInfo.uid;
res.pkg = pkg;
mSettings.setInstallerPackageName(pkgName, installerPackageName);
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", "mPackages"})
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, PackageParser.Package> 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, PackageParser.Package> 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, PackageParser.Package> 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, PackageParser.Package> getCombinedPackages() {
final ArrayMap<String, PackageParser.Package> combinedPackages =
new ArrayMap<>(request.allPackages.size() + request.scannedPackages.size());
combinedPackages.putAll(request.allPackages);
for (ScanResult scanResult : request.scannedPackages.values()) {
combinedPackages.put(scanResult.pkgSetting.name, scanResult.request.pkg);
}
return combinedPackages;
}
}
@GuardedBy("mPackages")
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, PackageParser.Package> 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.pkg);
// 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,
prepareResult.childPackageSettings, 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 PackageParser.Package pkg = scanResult.request.pkg;
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, pkg)) {
// 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 " + pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
} else {
String msg = "System package " + pkg.packageName
+ " signature changed; retaining data.";
reportSettingsProblem(Log.WARN, msg);
}
}
signingDetails = pkg.mSigningDetails;
} 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, pkg.mSigningDetails, 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 = pkg.mSigningDetails;
// 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) {
if (pkg.mSigningDetails.hasAncestor(
signatureCheckPs.sharedUser.signatures.mSigningDetails)) {
signatureCheckPs.sharedUser.signatures.mSigningDetails =
pkg.mSigningDetails;
}
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 = pkg.mSigningDetails;
// 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,
pkg.mSigningDetails.signatures)
!= PackageManager.SIGNATURE_MATCH) {
if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 28) {
// 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 P,
// vendors were
// not expecting the platform to crash in this situation.
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 "
+ pkg.packageName + " for shared user "
+ scanResult.pkgSetting.sharedUser);
}
}
sharedUserSignaturesChanged = true;
signatureCheckPs.sharedUser.signatures.mSigningDetails =
pkg.mSigningDetails;
signatureCheckPs.sharedUser.signaturesChanged = Boolean.TRUE;
}
// File a report about this.
String msg = "System package " + pkg.packageName
+ " 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.pkg, 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 PackageParser.Package pkg = scanResult.request.pkg;
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 =
(pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
&& scanResult.dynamicSharedLibraryInfos != null;
if (!hasDynamicLibraries) {
return null;
}
final boolean isUpdatedSystemApp = pkg.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.libraryNames == null)) {
Slog.w(TAG, "Package " + pkg.packageName + " 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.libraryNames.contains(name)) {
Slog.w(TAG, "Package " + pkg.packageName + " declares library " + name
+ " that is not declared on system image; skipping");
continue;
}
}
if (sharedLibExists(
name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
Slog.w(TAG, "Package " + pkg.packageName + " 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("mPackages")
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 PackageParser.Package pkg = scanRequest.pkg;
final String packageName = pkg.packageName;
final PackageInstalledInfo res = reconciledPkg.installResult;
if (reconciledPkg.prepareResult.replace) {
PackageParser.Package oldPackage = mPackages.get(packageName);
// Set the update and install times
PackageSetting deletedPkgSetting = (PackageSetting) oldPackage.mExtras;
setInstallAndUpdateTime(pkg, deletedPkgSetting.firstInstallTime,
System.currentTimeMillis());
if (reconciledPkg.prepareResult.system) {
// Remove existing system package
removePackageLI(oldPackage, true);
if (!disableSystemPackageLPw(oldPackage, pkg)) {
// 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.applicationInfo.getCodePath(),
oldPackage.applicationInfo.getResourcePath(),
getAppDexInstructionSets(oldPackage.applicationInfo));
} else {
res.removedInfo.args = null;
}
// Update the package dynamic state if succeeded
// Now that the install succeeded make sure we remove data
// directories for any child package the update removed.
final int deletedChildCount = (oldPackage.childPackages != null)
? oldPackage.childPackages.size() : 0;
final int newChildCount = (pkg.childPackages != null)
? pkg.childPackages.size() : 0;
for (int i = 0; i < deletedChildCount; i++) {
PackageParser.Package deletedChildPkg = oldPackage.childPackages.get(i);
boolean childPackageDeleted = true;
for (int j = 0; j < newChildCount; j++) {
PackageParser.Package newChildPkg = pkg.childPackages.get(j);
if (deletedChildPkg.packageName.equals(newChildPkg.packageName)) {
childPackageDeleted = false;
break;
}
}
if (childPackageDeleted) {
PackageSetting ps1 = mSettings.getDisabledSystemPkgLPr(
deletedChildPkg.packageName);
if (ps1 != null && res.removedInfo.removedChildPackages != null) {
PackageRemovedInfo removedChildRes = res.removedInfo
.removedChildPackages.get(deletedChildPkg.packageName);
removePackageDataLIF(ps1, request.mAllUsers, removedChildRes, 0,
false);
removedChildRes.removedForAllUsers = mPackages.get(ps1.name)
== null;
}
}
}
} else {
try {
executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName,
true, request.mAllUsers, true, pkg);
} 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.isForwardLocked() || isExternal(oldPackage)) {
if (DEBUG_INSTALL) {
Slog.i(TAG, "upgrading pkg " + oldPackage
+ " is ASEC-hosted -> UNAVAILABLE");
}
final int[] uidArray = new int[]{oldPackage.applicationInfo.uid};
final ArrayList<String> pkgList = new ArrayList<>(1);
pkgList.add(oldPackage.applicationInfo.packageName);
sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
}
// Update the in-memory copy of the previous code paths.
PackageSetting ps1 = mSettings.mPackages.get(
reconciledPkg.prepareResult.existingPackage.packageName);
if ((reconciledPkg.installArgs.installFlags & PackageManager.DONT_KILL_APP)
== 0) {
if (ps1.mOldCodePaths == null) {
ps1.mOldCodePaths = new ArraySet<>();
}
Collections.addAll(ps1.mOldCodePaths, oldPackage.baseCodePath);
if (oldPackage.splitCodePaths != null) {
Collections.addAll(ps1.mOldCodePaths, oldPackage.splitCodePaths);
}
} else {
ps1.mOldCodePaths = null;
}
if (ps1.childPackageNames != null) {
for (int i = ps1.childPackageNames.size() - 1; i >= 0; --i) {
final String childPkgName = ps1.childPackageNames.get(i);
final PackageSetting childPs = mSettings.mPackages.get(childPkgName);
childPs.mOldCodePaths = ps1.mOldCodePaths;
}
}
if (reconciledPkg.installResult.returnCode
== PackageManager.INSTALL_SUCCEEDED) {
PackageSetting ps2 = mSettings.getPackageLPr(pkg.packageName);
if (ps2 != null) {
res.removedInfo.removedForAllUsers = mPackages.get(ps2.name) == null;
if (res.removedInfo.removedChildPackages != null) {
final int childCount1 = res.removedInfo.removedChildPackages.size();
// Iterate in reverse as we may modify the collection
for (int i = childCount1 - 1; i >= 0; i--) {
String childPackageName =
res.removedInfo.removedChildPackages.keyAt(i);
if (res.addedChildPackages.containsKey(childPackageName)) {
res.removedInfo.removedChildPackages.removeAt(i);
} else {
PackageRemovedInfo childInfo = res.removedInfo
.removedChildPackages.valueAt(i);
childInfo.removedForAllUsers = mPackages.get(
childInfo.removedPackage) == null;
}
}
}
}
}
}
}
commitReconciledScanResultLocked(reconciledPkg);
updateSettingsLI(pkg, reconciledPkg.installArgs.installerPackageName, request.mAllUsers,
res, reconciledPkg.installArgs.user, reconciledPkg.installArgs.installReason);
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
ps.setUpdateAvailable(false /*updateAvailable*/);
}
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPkg = pkg.childPackages.get(i);
PackageInstalledInfo childRes = res.addedChildPackages.get(
childPkg.packageName);
PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
if (childPs != null) {
childRes.newUsers = childPs.queryInstalledUsers(
sUserManager.getUserIds(), true);
}
}
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());
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.installerPackageName;
final String packageName = prepareResult.packageToScan.packageName;
prepareResults.put(packageName, prepareResult);
installResults.put(packageName, request.installResult);
installArgs.put(packageName, request.args);
try {
final List<ScanResult> scanResults = scanPackageTracedLI(
prepareResult.packageToScan, prepareResult.parseFlags,
prepareResult.scanFlags, System.currentTimeMillis(),
request.args.user);
for (ScanResult result : scanResults) {
if (null != preparedScans.put(result.pkgSetting.pkg.packageName, result)) {
request.installResult.setError(
PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
"Duplicate package " + result.pkgSetting.pkg.packageName
+ " in multi-package install request.");
return;
}
prepareScanResultLocked(result);
versionInfos.put(result.pkgSetting.pkg.packageName,
getSettingsVersionForPackage(result.pkgSetting.pkg));
if (result.staticSharedLibraryInfo != null) {
final PackageSetting sharedLibLatestVersionSetting =
getSharedLibLatestVersionSetting(result);
if (sharedLibLatestVersionSetting != null) {
lastStaticSharedLibSettings.put(result.pkgSetting.pkg.packageName,
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 (mPackages) {
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,
sUserManager.getUserIds());
commitPackagesLocked(commitRequest);
success = true;
} finally {
for (PrepareResult result : prepareResults.values()) {
if (result.freezer != null) {
result.freezer.close();
}
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
executePostCommitSteps(commitRequest);
} finally {
if (!success) {
for (ScanResult result : preparedScans.values()) {
unprepareScanResultLocked(result);
}
}
for (PrepareResult result : prepareResults.values()) {
if (result.freezer != null) {
result.freezer.close();
}
}
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 #mPackages}.
*/
private void executePostCommitSteps(CommitRequest commitRequest) {
for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
final boolean instantApp = ((reconciledPkg.scanResult.request.scanFlags
& PackageManagerService.SCAN_AS_INSTANT_APP) != 0);
final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg;
final String packageName = pkg.packageName;
prepareAppDataAfterInstallLIF(pkg);
if (reconciledPkg.prepareResult.clearCodeCache) {
clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
| StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
if (reconciledPkg.prepareResult.replace) {
mDexManager.notifyPackageUpdated(pkg.packageName,
pkg.baseCodePath, pkg.splitCodePaths);
}
// 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 PackageParser.Package 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.
//
// 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.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
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.
DexoptOptions dexoptOptions = new DexoptOptions(packageName,
REASON_INSTALL,
DexoptOptions.DEXOPT_BOOT_COMPLETE
| DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
mPackageDexOptimizer.performDexOpt(pkg,
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);
}
}
/**
* 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 int installReason;
public final String volumeUuid;
public final String installerPackageName;
public final UserHandle user;
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 PackageParser.Package existingPackage;
public final PackageParser.Package packageToScan;
public final boolean clearCodeCache;
public final boolean system;
/* The original package name if it was changed during an update, otherwise {@code null}. */
@Nullable
public final String renamedPackage;
public final PackageFreezer freezer;
public final PackageSetting originalPs;
public final PackageSetting disabledPs;
public final PackageSetting[] childPackageSettings;
private PrepareResult(int installReason, String volumeUuid,
String installerPackageName, UserHandle user, boolean replace, int scanFlags,
int parseFlags, PackageParser.Package existingPackage,
PackageParser.Package packageToScan, boolean clearCodeCache, boolean system,
String renamedPackage, PackageFreezer freezer, PackageSetting originalPs,
PackageSetting disabledPs, PackageSetting[] childPackageSettings) {
this.installReason = installReason;
this.volumeUuid = volumeUuid;
this.installerPackageName = installerPackageName;
this.user = user;
this.replace = replace;
this.scanFlags = scanFlags;
this.parseFlags = parseFlags;
this.existingPackage = existingPackage;
this.packageToScan = packageToScan;
this.clearCodeCache = clearCodeCache;
this.system = system;
this.renamedPackage = renamedPackage;
this.freezer = freezer;
this.originalPs = originalPs;
this.disabledPs = disabledPs;
this.childPackageSettings = childPackageSettings;
}
}
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 String installerPackageName = args.installerPackageName;
final String volumeUuid = args.volumeUuid;
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);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final PackageParser.Package pkg;
try {
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
DexMetadataHelper.validatePackageDexMetadata(pkg);
} 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 (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
Slog.w(TAG,
"Instant app package " + pkg.packageName + " does not target at least O");
throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
"Instant app package must target at least O");
}
if (pkg.mSharedUserId != null) {
Slog.w(TAG, "Instant app package " + pkg.packageName
+ " may not declare sharedUserId.");
throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
"Instant app package may not declare a sharedUserId");
}
}
if (pkg.applicationInfo.isStaticSharedLibrary()) {
// Static shared libraries have synthetic package names
renameStaticSharedLibraryPackage(pkg);
// 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");
}
}
// If we are installing a clustered package add results for the children
if (pkg.childPackages != null) {
synchronized (mPackages) {
final int childCount = pkg.childPackages.size();
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPkg = pkg.childPackages.get(i);
PackageInstalledInfo childRes = new PackageInstalledInfo();
childRes.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
childRes.pkg = childPkg;
childRes.name = childPkg.packageName;
PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
if (childPs != null) {
childRes.origUsers = childPs.queryInstalledUsers(
sUserManager.getUserIds(), true);
}
if ((mPackages.containsKey(childPkg.packageName))) {
childRes.removedInfo = new PackageRemovedInfo(this);
childRes.removedInfo.removedPackage = childPkg.packageName;
childRes.removedInfo.installerPackageName = childPs.installerPackageName;
}
if (res.addedChildPackages == null) {
res.addedChildPackages = new ArrayMap<>();
}
res.addedChildPackages.put(childPkg.packageName, childRes);
}
}
}
// If package doesn't declare API override, mark that we have an install
// time CPU ABI override.
if (TextUtils.isEmpty(pkg.cpuAbiOverride)) {
pkg.cpuAbiOverride = args.abiOverride;
}
String pkgName = res.name = pkg.packageName;
if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0) {
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) {
pkg.setSigningDetails(args.signingDetails);
} else {
PackageParser.collectCertificates(pkg, false /* skipVerify */);
}
} catch (PackageParserException e) {
throw new PrepareFailure("Failed collect during installPackageLI", e);
}
if (instantApp && pkg.mSigningDetails.signatureSchemeVersion
< SignatureSchemeVersion.SIGNING_BLOCK_V2) {
Slog.w(TAG, "Instant app package " + pkg.packageName
+ " 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");
}
// Get rid of all references to package scan path via parser.
pp = null;
boolean systemApp = false;
boolean replace = false;
synchronized (mPackages) {
// Check if installing already existing package
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
String oldName = mSettings.getRenamedPackageLPr(pkgName);
if (pkg.mOriginalPackages != null
&& pkg.mOriginalPackages.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.
pkg.setPackageName(oldName);
pkgName = pkg.packageName;
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);
}
// Child packages are installed through the parent package
if (pkg.parentPackage != null) {
throw new PrepareFailure(
PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
"Package " + pkg.packageName + " is child of package "
+ pkg.parentPackage.parentPackage + ". Child packages "
+ "can be updated only through the parent package.");
}
if (replace) {
// Prevent apps opting out from runtime permissions
PackageParser.Package oldPackage = mPackages.get(pkgName);
final int oldTargetSdk = oldPackage.applicationInfo.targetSdkVersion;
final int newTargetSdk = pkg.applicationInfo.targetSdkVersion;
if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
&& newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {
throw new PrepareFailure(
PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
"Package " + pkg.packageName + " new target SDK " + newTargetSdk
+ " doesn't support runtime permissions but the old"
+ " target SDK " + oldTargetSdk + " does.");
}
// Prevent persistent apps from being updated
if ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0) {
throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,
"Package " + oldPackage.packageName + " is a persistent app. "
+ "Persistent apps are not updateable.");
}
// Prevent installing of child packages
if (oldPackage.parentPackage != null) {
throw new PrepareFailure(
PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
"Package " + pkg.packageName + " is child of package "
+ oldPackage.parentPackage + ". Child packages "
+ "can be updated only through the parent package.");
}
}
}
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 (pkg.applicationInfo.isStaticSharedLibrary()) {
SharedLibraryInfo libraryInfo = getLatestSharedLibraVersionLPr(pkg);
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, pkg)) {
throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
}
} else {
try {
final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg);
final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg);
// We don't care about disabledPkgSetting on install for now.
final boolean compatMatch = verifySignatures(
signatureCheckPs, null, pkg.mSigningDetails, compareCompat,
compareRecover);
// The new KeySets will be re-added later in the scanning process.
if (compatMatch) {
synchronized (mPackages) {
ksms.removeAppKeySetDataLPw(pkg.packageName);
}
}
} catch (PackageManagerException e) {
throw new PrepareFailure(e.error, e.getMessage());
}
}
if (ps.pkg != null && ps.pkg.applicationInfo != null) {
systemApp = (ps.pkg.applicationInfo.flags &
ApplicationInfo.FLAG_SYSTEM) != 0;
}
res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
int N = pkg.permissions.size();
for (int i = N - 1; i >= 0; i--) {
final PackageParser.Permission perm = pkg.permissions.get(i);
final BasePermission bp =
(BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);
// Don't allow anyone but the system to define ephemeral permissions.
if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
&& !systemApp) {
Slog.w(TAG, "Non-System package " + pkg.packageName
+ " attempting to delcare ephemeral permission "
+ perm.info.name + "; Removing ephemeral.");
perm.info.protectionLevel &= ~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 PackageSettingBase sourcePackageSetting = bp.getSourcePackageSetting();
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
if (sourcePackageName.equals(pkg.packageName)
&& (ksms.shouldCheckUpgradeKeySetLocked(
sourcePackageSetting, scanFlags))) {
sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg);
} 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 (sourcePackageSetting.signatures.mSigningDetails.checkCapability(
pkg.mSigningDetails,
PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
sigsOk = true;
} else if (pkg.mSigningDetails.checkCapability(
sourcePackageSetting.signatures.mSigningDetails,
PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
// the scanned package checks out, has signing certificate rotation
// history, and is newer; bring it over
sourcePackageSetting.signatures.mSigningDetails = pkg.mSigningDetails;
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 "
+ pkg.packageName
+ " attempting to redeclare permission "
+ perm.info.name + " already owned by "
+ sourcePackageName)
.conflictsWithExistingPermission(perm.info.name,
sourcePackageName);
} else {
Slog.w(TAG, "Package " + pkg.packageName
+ " attempting to redeclare system permission "
+ perm.info.name + "; ignoring new declaration");
pkg.permissions.remove(i);
}
} else if (!PLATFORM_PACKAGE_NAME.equals(pkg.packageName)) {
// 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.info.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_DANGEROUS) {
if (bp != null && !bp.isRuntime()) {
Slog.w(TAG, "Package " + pkg.packageName + " trying to change a "
+ "non-runtime permission " + perm.info.name
+ " to runtime; keeping old protection level");
perm.info.protectionLevel = 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 (mPackages) {
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.
pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString;
pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
}
} else {
// Enable SCAN_NO_DEX flag to skip dexopt at a later stage
scanFlags |= SCAN_NO_DEX;
try {
String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?
args.abiOverride : pkg.cpuAbiOverride);
final boolean extractNativeLibs = !pkg.isLibrary();
derivePackageAbi(pkg, abiOverride, extractNativeLibs);
} 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, pkg)) {
throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
}
try {
setUpFsVerityIfPossible(pkg);
} 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, pkg);
} 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 PackageParser.Package existingPackage;
String renamedPackage = null;
boolean sysPkg = false;
String targetVolumeUuid = volumeUuid;
int targetScanFlags = scanFlags;
int targetParseFlags = parseFlags;
final PackageSetting ps;
final PackageSetting disabledPs;
final PackageSetting[] childPackages;
if (replace) {
targetVolumeUuid = null;
if (pkg.applicationInfo.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.
PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
if (existingPkg != null
&& existingPkg.getLongVersionCode() != pkg.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 PackageParser.Package oldPackage;
final String pkgName11 = pkg.packageName;
final int[] allUsers;
final int[] installedUsers;
synchronized (mPackages) {
oldPackage = mPackages.get(pkgName11);
existingPackage = oldPackage;
if (DEBUG_INSTALL) {
Slog.d(TAG,
"replacePackageLI: new=" + pkg + ", 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, pkg)) {
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 (!pkg.mSigningDetails.checkCapability(oldPackage.mSigningDetails,
SigningDetails.CertCapabilities.INSTALLED_DATA)
&& !oldPackage.mSigningDetails.checkCapability(
pkg.mSigningDetails,
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.restrictUpdateHash != null && oldPackage.isSystem()) {
final byte[] digestBytes;
try {
final MessageDigest digest = MessageDigest.getInstance("SHA-512");
updateDigest(digest, new File(pkg.baseCodePath));
if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
for (String path : pkg.splitCodePaths) {
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.restrictUpdateHash, digestBytes)) {
throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
"New package fails restrict-update check: " + pkgName11);
}
// retain upgrade restriction
pkg.restrictUpdateHash = oldPackage.restrictUpdateHash;
}
// Check for shared user id changes
String invalidPackageName =
getParentOrChildPackageChangedSharedUser(oldPackage, pkg);
if (invalidPackageName != null) {
throw new PrepareFailure(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
"Package " + invalidPackageName + " tried to change user "
+ oldPackage.mSharedUserId);
}
// In case of rollback, remember per-user/profile install state
allUsers = sUserManager.getUserIds();
installedUsers = ps.queryInstalledUsers(allUsers, true);
// 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.applicationInfo.uid;
res.removedInfo.removedPackage = oldPackage.packageName;
res.removedInfo.installerPackageName = ps.installerPackageName;
res.removedInfo.isStaticSharedLib = pkg.staticSharedLibName != 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));
}
childPackages = mSettings.getChildSettingsLPr(ps);
if (childPackages != null) {
for (PackageSetting childPs : childPackages) {
boolean childPackageUpdated = false;
PackageParser.Package childPkg = (childPs == null) ? null : childPs.pkg;
if (res.addedChildPackages != null) {
PackageInstalledInfo childRes = res.addedChildPackages.get(
childPkg.packageName);
if (childRes != null) {
childRes.removedInfo.uid = childPkg.applicationInfo.uid;
childRes.removedInfo.removedPackage = childPkg.packageName;
if (childPs != null) {
childRes.removedInfo.installerPackageName =
childPs.installerPackageName;
}
childRes.removedInfo.isUpdate = true;
childRes.removedInfo.installReasons =
res.removedInfo.installReasons;
childPackageUpdated = true;
}
}
if (!childPackageUpdated) {
PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
childRemovedRes.removedPackage = childPkg.packageName;
if (childPs != null) {
childRemovedRes.installerPackageName = childPs.installerPackageName;
}
childRemovedRes.isUpdate = false;
childRemovedRes.dataRemoved = true;
synchronized (mPackages) {
if (childPs != null) {
childRemovedRes.origUsers = childPs.queryInstalledUsers(
allUsers,
true);
}
}
if (res.removedInfo.removedChildPackages == null) {
res.removedInfo.removedChildPackages = new ArrayMap<>();
}
res.removedInfo.removedChildPackages.put(childPkg.packageName,
childRemovedRes);
}
}
}
sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
// Set the system/privileged/oem/vendor/product flags as needed
final boolean privileged = isPrivilegedApp(oldPackage);
final boolean oem = isOemApp(oldPackage);
final boolean vendor = isVendorApp(oldPackage);
final boolean product = isProductApp(oldPackage);
final boolean odm = isOdmApp(oldPackage);
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);
if (DEBUG_INSTALL) {
Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
+ ", old=" + oldPackage);
}
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
targetParseFlags = systemParseFlags;
targetScanFlags = systemScanFlags;
} else { // non system replace
replace = true;
if (DEBUG_INSTALL) {
Slog.d(TAG,
"replaceNonSystemPackageLI: new=" + pkg + ", old="
+ oldPackage);
}
String pkgName1 = oldPackage.packageName;
boolean deletedPkg = true;
boolean addedPkg = false;
boolean updatedSettings = false;
final long origUpdateTime = (pkg.mExtras != null)
? ((PackageSetting) pkg.mExtras).lastUpdateTime : 0;
}
} else { // new package install
ps = null;
childPackages = null;
disabledPs = null;
replace = false;
existingPackage = null;
// Remember this for later, in case we need to rollback this install
String pkgName1 = pkg.packageName;
if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
// TODO(patb): MOVE TO RECONCILE
synchronized (mPackages) {
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(args.installReason, targetVolumeUuid, installerPackageName,
args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg,
replace /* clearCodeCache */, sysPkg, renamedPackage, freezer,
ps, disabledPs, childPackages);
} finally {
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(PackageParser.Package 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 (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkg.packageName);
if (ps != null && ps.isPrivileged()) {
fsverityCandidates.put(pkg.baseCodePath, null);
if (pkg.splitCodePaths != null) {
for (String splitPath : pkg.splitCodePaths) {
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.baseCodePath,
VerityUtils.getFsveritySignatureFilePath(pkg.baseCodePath));
final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(pkg.baseCodePath);
if (new File(dmPath).exists()) {
fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath));
}
if (pkg.splitCodePaths != null) {
for (String path : pkg.splitCodePaths) {
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 | DigestException | NoSuchAlgorithmException
| SecurityException 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 {
mInstaller.installApkVerity(filePath, fd, result.getContentSize());
final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath);
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,
PackageParser.Package 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, replacing, userId, verifierUid);
mHandler.sendMessage(msg);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPkg = pkg.childPackages.get(i);
msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
msg.obj = new IFVerificationParams(childPkg, replacing, userId, verifierUid);
mHandler.sendMessage(msg);
}
}
private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean replacing,
PackageParser.Package pkg) {
int size = pkg.activities.size();
if (size == 0) {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"No activity, so no need to verify any IntentFilter!");
return;
}
final boolean hasDomainURLs = hasDomainURLs(pkg);
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;
final String packageName = pkg.packageName;
synchronized (mPackages) {
// 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.
if (!replacing) {
IntentFilterVerificationInfo ivi =
mSettings.getIntentFilterVerificationLPr(packageName);
if (ivi != null) {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.i(TAG, "Package " + packageName+ " already verified: status="
+ ivi.getStatusString());
}
return;
}
}
// If any filters need to be verified, then all need to be.
boolean needToVerify = false;
for (PackageParser.Activity a : pkg.activities) {
for (ActivityIntentInfo filter : a.intents) {
if (filter.needsVerification() && needsNetworkVerificationLPr(filter)) {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG,
"Intent filter needs verification, so processing all filters");
}
needToVerify = true;
break;
}
}
}
if (needToVerify) {
final int verificationId = mIntentFilterVerificationToken++;
for (PackageParser.Activity a : pkg.activities) {
for (ActivityIntentInfo filter : a.intents) {
if (filter.handlesWebUris(true) && needsNetworkVerificationLPr(filter)) {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"Verification needed for IntentFilter:" + filter.toString());
mIntentFilterVerifier.addOneIntentFilterVerification(
verifierUid, userId, verificationId, filter, packageName);
count++;
}
}
}
}
}
if (count > 0) {
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 filters or not all autoVerify for " + packageName);
}
}
}
@GuardedBy("mPackages")
private boolean needsNetworkVerificationLPr(ActivityIntentInfo filter) {
final ComponentName cn = filter.activity.getComponentName();
final String packageName = cn.getPackageName();
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 isMultiArch(ApplicationInfo info) {
return (info.flags & ApplicationInfo.FLAG_MULTIARCH) != 0;
}
private static boolean isExternal(PackageParser.Package pkg) {
return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
private static boolean isExternal(PackageSetting ps) {
return (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
private static boolean isSystemApp(PackageParser.Package pkg) {
return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
private static boolean isPrivilegedApp(PackageParser.Package pkg) {
return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
}
private static boolean isOemApp(PackageParser.Package pkg) {
return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
}
private static boolean isVendorApp(PackageParser.Package pkg) {
return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
}
private static boolean isProductApp(PackageParser.Package pkg) {
return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
}
private static boolean isProductServicesApp(PackageParser.Package pkg) {
return (pkg.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0;
}
private static boolean isOdmApp(PackageParser.Package pkg) {
return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_ODM) != 0;
}
private static boolean hasDomainURLs(PackageParser.Package pkg) {
return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 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(PackageParser.Package pkg) {
if (isExternal(pkg)) {
if (TextUtils.isEmpty(pkg.volumeUuid)) {
return mSettings.getExternalVersion();
} else {
return mSettings.findOrCreateVersion(pkg.volumeUuid);
}
} else {
return mSettings.getInternalVersion();
}
}
private void deleteTempPackageFiles() {
final FilenameFilter filter =
(dir, name) -> name.startsWith("vmdl") && name.endsWith(".tmp");
}
@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 deletePackageVersioned(VersionedPackage versionedPackage,
final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags) {
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 (mPackages) {
// Normalize package name to handle renamed packages and static libs
internalPackageName = resolveInternalPackageNameLPr(packageName, versionCode);
}
final int uid = Binder.getCallingUid();
if (!isOrphaned(internalPackageName)
&& !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 ? sUserManager.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
});
}
private String resolveExternalPackageNameLPr(PackageParser.Package pkg) {
if (pkg.staticSharedLibName != null) {
return pkg.manifestPackageName;
}
return pkg.packageName;
}
@GuardedBy("mPackages")
private String resolveInternalPackageNameLPr(String packageName, long versionCode) {
// 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(Binder.getCallingUid());
if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.SHELL_UID
&& callingAppId != Process.ROOT_UID) {
versionsCallerCanSee = new LongSparseLongArray();
String libName = versionedLib.valueAt(0).getName();
String[] uidPackages = getPackagesForUid(Binder.getCallingUid());
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 = sUserManager.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;
}
PackageSetting uninstalledPs;
PackageParser.Package pkg;
// for the uninstall-updates case and restricted profiles, remember the per-
// user handle installed state
int[] allUsers;
synchronized (mPackages) {
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;
}
// 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 = sUserManager.getUserIds();
if (pkg != null && pkg.staticSharedLibName != null) {
SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(pkg.staticSharedLibName,
pkg.staticSharedLibVersion);
if (libraryInfo != null) {
for (int currUserId : allUsers) {
if (removeUser != UserHandle.USER_ALL && removeUser != currUserId) {
continue;
}
List<VersionedPackage> libClientPackages = getPackagesUsingSharedLibraryLPr(
libraryInfo, 0, currUserId);
if (!ArrayUtils.isEmpty(libClientPackages)) {
Slog.w(TAG, "Not removing package " + pkg.manifestPackageName
+ " 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 (mPackages) {
if (res) {
if (pkg != null) {
mInstantAppRegistry.onPackageUninstalledLPw(pkg, 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();
info.sendSystemPackageAppearedBroadcasts();
}
// Force a gc here.
Runtime.getRuntime().gc();
// Delete the resources here after sending the broadcast to let
// other processes clean up before deleting resources.
if (info.args != null) {
synchronized (mInstallLock) {
info.args.doPostDeleteLI(true);
}
}
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;
boolean isRemovedPackageSystemUpdate = false;
boolean isUpdate;
boolean dataRemoved;
boolean removedForAllUsers;
boolean isStaticSharedLib;
// Clean up resources deleted packages.
InstallArgs args = null;
ArrayMap<String, PackageRemovedInfo> removedChildPackages;
ArrayMap<String, PackageInstalledInfo> appearedChildPackages;
PackageRemovedInfo(PackageSender packageSender) {
this.packageSender = packageSender;
}
void sendPackageRemovedBroadcasts(boolean killApp) {
sendPackageRemovedBroadcastInternal(killApp);
final int childCount = removedChildPackages != null ? removedChildPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageRemovedInfo childInfo = removedChildPackages.valueAt(i);
childInfo.sendPackageRemovedBroadcastInternal(killApp);
}
}
void sendSystemPackageUpdatedBroadcasts() {
if (isRemovedPackageSystemUpdate) {
sendSystemPackageUpdatedBroadcastsInternal();
final int childCount = (removedChildPackages != null)
? removedChildPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageRemovedInfo childInfo = removedChildPackages.valueAt(i);
if (childInfo.isRemovedPackageSystemUpdate) {
childInfo.sendSystemPackageUpdatedBroadcastsInternal();
}
}
}
}
void sendSystemPackageAppearedBroadcasts() {
final int packageCount = (appearedChildPackages != null)
? appearedChildPackages.size() : 0;
for (int i = 0; i < packageCount; i++) {
PackageInstalledInfo installedInfo = appearedChildPackages.valueAt(i);
packageSender.sendPackageAddedForNewUsers(installedInfo.name,
true /*sendBootCompleted*/, false /*startReceiver*/,
UserHandle.getAppId(installedInfo.uid), installedInfo.newUsers, null);
}
}
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);
packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
removedPackage, extras, 0, null /*targetPackage*/, null, null, null);
packageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null, null, 0, removedPackage, null, null, null);
if (installerPackageName != null) {
packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
removedPackage, extras, 0 /*flags*/,
installerPackageName, null, null, null);
packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
removedPackage, extras, 0 /*flags*/,
installerPackageName, null, null, null);
}
}
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);
if (installerPackageName != null) {
packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
removedPackage, extras, 0 /*flags*/,
installerPackageName, null, broadcastUsers, instantUserIds);
}
if (dataRemoved && !isRemovedPackageSystemUpdate) {
packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
removedPackage, extras,
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
null, null, broadcastUsers, instantUserIds);
packageSender.notifyPackageRemoved(removedPackage, removedUid);
}
}
if (removedAppId >= 0) {
packageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED,
null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
null, null, broadcastUsers, instantUserIds);
}
}
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 DONT_DELETE_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 PackageParser.Package deletedPkg = deletedPs.pkg;
if (outInfo != null) {
outInfo.removedPackage = packageName;
outInfo.installerPackageName = deletedPs.installerPackageName;
outInfo.isStaticSharedLib = deletedPkg != null
&& deletedPkg.staticSharedLibName != null;
outInfo.populateUsers(deletedPs == null ? null
: deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true), deletedPs);
}
removePackageLI(deletedPs.name, (flags & PackageManager.DELETE_CHATTY) != 0);
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
final PackageParser.Package 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 = new PackageParser.Package(deletedPs.name);
resolvedPkg.setVolumeUuid(deletedPs.volumeUuid);
}
destroyAppDataLIF(resolvedPkg, UserHandle.USER_ALL,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
destroyAppProfilesLIF(resolvedPkg, UserHandle.USER_ALL);
if (outInfo != null) {
outInfo.dataRemoved = true;
}
}
int removedAppId = -1;
// writer
synchronized (mPackages) {
boolean installedStateChanged = false;
if (deletedPs != null) {
if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
clearDefaultBrowserIfNeeded(packageName);
mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
removedAppId = mSettings.removePackageLPw(packageName);
if (outInfo != null) {
outInfo.removedAppId = removedAppId;
}
mPermissionManager.updatePermissions(
deletedPs.name, null, false, mPackages.values(), mPermissionCallback);
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.
for (int userId : UserManagerService.getInstance().getUserIds()) {
final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs,
userId);
if (userIdToKill == UserHandle.USER_ALL
|| userIdToKill >= UserHandle.USER_SYSTEM) {
// If gids changed for this user, kill all affected packages.
mHandler.post(() -> {
// This has to happen with no lock held.
killApplication(deletedPs.name, deletedPs.appId,
KILL_APP_REASON_GIDS_CHANGED);
});
break;
}
}
}
clearPackagePreferredActivitiesLPw(deletedPs.name, 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);
}
}
}
// 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(UserHandle.USER_ALL, removedAppId);
}
}
static boolean locationIsPrivileged(String path) {
try {
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
final File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app");
final File privilegedOdmAppDir = new File(Environment.getOdmDirectory(), "priv-app");
final File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app");
final File privilegedProductServicesAppDir =
new File(Environment.getProductServicesDirectory(), "priv-app");
return path.startsWith(privilegedAppDir.getCanonicalPath() + "/")
|| path.startsWith(privilegedVendorAppDir.getCanonicalPath() + "/")
|| path.startsWith(privilegedOdmAppDir.getCanonicalPath() + "/")
|| path.startsWith(privilegedProductAppDir.getCanonicalPath() + "/")
|| path.startsWith(privilegedProductServicesAppDir.getCanonicalPath() + "/");
} catch (IOException e) {
Slog.e(TAG, "Unable to access code path " + path);
}
return false;
}
static boolean locationIsOem(String path) {
try {
return path.startsWith(Environment.getOemDirectory().getCanonicalPath() + "/");
} catch (IOException e) {
Slog.e(TAG, "Unable to access code path " + path);
}
return false;
}
static boolean locationIsVendor(String path) {
try {
return path.startsWith(Environment.getVendorDirectory().getCanonicalPath() + "/")
|| path.startsWith(Environment.getOdmDirectory().getCanonicalPath() + "/");
} catch (IOException e) {
Slog.e(TAG, "Unable to access code path " + path);
}
return false;
}
static boolean locationIsProduct(String path) {
try {
return path.startsWith(Environment.getProductDirectory().getCanonicalPath() + "/");
} catch (IOException e) {
Slog.e(TAG, "Unable to access code path " + path);
}
return false;
}
static boolean locationIsProductServices(String path) {
try {
return path.startsWith(
Environment.getProductServicesDirectory().getCanonicalPath() + "/");
} catch (IOException e) {
Slog.e(TAG, "Unable to access code path " + path);
}
return false;
}
static boolean locationIsOdm(String path) {
try {
return path.startsWith(Environment.getOdmDirectory().getCanonicalPath() + "/");
} catch (IOException e) {
Slog.e(TAG, "Unable to access code path " + path);
}
return false;
}
/*
* Tries to delete system package.
*/
private void deleteSystemPackageLIF(DeletePackageAction action, PackageSetting deletedPs,
int[] allUserHandles, int flags, PackageRemovedInfo outInfo, boolean writeSettings)
throws SystemDeleteException {
final boolean applyUserRestrictions
= (allUserHandles != null) && (outInfo.origUsers != null);
final PackageParser.Package 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.packageName
+ " 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);
}
}
}
// Delete the updated package
outInfo.isRemovedPackageSystemUpdate = true;
if (outInfo.removedChildPackages != null) {
final int childCount = (deletedPs.childPackageNames != null)
? deletedPs.childPackageNames.size() : 0;
for (int i = 0; i < childCount; i++) {
String childPackageName = deletedPs.childPackageNames.get(i);
if (disabledPs.childPackageNames != null && disabledPs.childPackageNames
.contains(childPackageName)) {
PackageRemovedInfo childInfo = outInfo.removedChildPackages.get(
childPackageName);
if (childInfo != null) {
childInfo.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, disabledPs.pkg);
// writer
synchronized (mPackages) {
// 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.origUsers, deletedPs.getPermissionsState(), writeSettings);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to restore system package:" + deletedPkg.packageName + ": "
+ e.getMessage());
// TODO(patb): can we avoid this; throw would come from scan...
throw new SystemDeleteException(e);
} finally {
if (disabledPs.pkg.isStub) {
mSettings.disableSystemPackageLPw(disabledPs.name, true /*replaced*/);
}
}
}
/**
* Installs a package that's already on the system partition.
*/
private PackageParser.Package installPackageFromSystemLIF(@NonNull String codePathString,
@Nullable int[] allUserHandles, @Nullable int[] origUserHandles,
@Nullable PermissionsState origPermissionState, boolean writeSettings)
throws PackageManagerException {
@ParseFlags int parseFlags =
mDefParseFlags
| PackageParser.PARSE_MUST_BE_APK
| PackageParser.PARSE_IS_SYSTEM_DIR;
@ScanFlags int scanFlags = SCAN_AS_SYSTEM;
if (locationIsPrivileged(codePathString)) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
if (locationIsOem(codePathString)) {
scanFlags |= SCAN_AS_OEM;
}
if (locationIsVendor(codePathString)) {
scanFlags |= SCAN_AS_VENDOR;
}
if (locationIsProduct(codePathString)) {
scanFlags |= SCAN_AS_PRODUCT;
}
if (locationIsProductServices(codePathString)) {
scanFlags |= SCAN_AS_PRODUCT_SERVICES;
}
if (locationIsOdm(codePathString)) {
scanFlags |= SCAN_AS_ODM;
}
final File codePath = new File(codePathString);
final PackageParser.Package pkg =
scanPackageTracedLI(codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
try {
// update shared libraries for the newly re-installed system package
updateSharedLibrariesLocked(pkg, null, Collections.unmodifiableMap(mPackages));
} catch (PackageManagerException e) {
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
}
prepareAppDataAfterInstallLIF(pkg);
// writer
synchronized (mPackages) {
PackageSetting ps = mSettings.mPackages.get(pkg.packageName);
// 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.packageName, pkg, true, mPackages.values(),
mPermissionCallback);
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);
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,
PackageParser.Package replacingPackage) {
synchronized (mPackages) {
if (outInfo != null) {
outInfo.uid = ps.appId;
}
if (outInfo != null && outInfo.removedChildPackages != null) {
final int childCount = (ps.childPackageNames != null)
? ps.childPackageNames.size() : 0;
for (int i = 0; i < childCount; i++) {
String childPackageName = ps.childPackageNames.get(i);
PackageSetting childPs = mSettings.mPackages.get(childPackageName);
PackageRemovedInfo childInfo = outInfo.removedChildPackages.get(
childPackageName);
if (childInfo != null) {
childInfo.uid = childPs.appId;
}
}
}
}
// Delete package data from internal structures and also remove data if flag is set
removePackageDataLIF(ps, allUserHandles, outInfo, flags, writeSettings);
// Delete the child packages data
final int childCount = (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageSetting childPs;
synchronized (mPackages) {
childPs = mSettings.getPackageLPr(ps.childPackageNames.get(i));
}
if (childPs != null) {
PackageRemovedInfo childOutInfo = (outInfo != null
&& outInfo.removedChildPackages != null)
? outInfo.removedChildPackages.get(childPs.name) : null;
final int deleteFlags = (flags & DELETE_KEEP_DATA) != 0
&& (replacingPackage != null
&& !replacingPackage.hasChildPackage(childPs.name))
? flags & ~DELETE_KEEP_DATA : flags;
removePackageDataLIF(childPs, allUserHandles, childOutInfo,
deleteFlags, writeSettings);
}
}
// Delete application code and resources only for parent packages
if (ps.parentPackageName == null) {
if (deleteCodeAndResources && (outInfo != null)) {
outInfo.args = createInstallArgsForExisting(
ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
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);
synchronized (mPackages) {
// 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.
PackageParser.Package pkg = mPackages.get(packageName);
if (pkg != null && pkg.staticSharedLibName != null) {
Slog.w(TAG, "Cannot block uninstall of package: " + packageName
+ " providing static shared library: " + pkg.staticSharedLibName);
return false;
}
mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall);
mSettings.writePackageRestrictionsLPr(userId);
}
return true;
}
@Override
public boolean getBlockUninstallForUser(String packageName, int userId) {
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null || filterAppAccessLPr(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 (mPackages) {
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("mPackages")
private static DeletePackageAction mayDeletePackageLocked(
PackageRemovedInfo outInfo, PackageSetting ps, @Nullable PackageSetting disabledPs,
@Nullable PackageSetting[] children, int flags, UserHandle user) {
if (ps == null) {
return null;
}
if (isSystemApp(ps)) {
if (ps.parentPackageName != null) {
Slog.w(TAG, "Attempt to delete child system package " + ps.pkg.packageName);
return null;
}
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.packageName);
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
}
final int parentReferenceCount =
(ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
final int childCount = children != null ? children.length : 0;
if (childCount != parentReferenceCount) {
return null;
}
if (childCount != 0 && outInfo != null && outInfo.removedChildPackages != null) {
for (PackageSetting child : children) {
if (child == null || !ps.childPackageNames.contains(child.name)) {
return null;
}
}
}
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,
PackageParser.Package replacingPackage) {
final DeletePackageAction action;
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
PackageSetting[] children = mSettings.getChildSettingsLPr(ps);
action = mayDeletePackageLocked(outInfo, ps, disabledPs, children, 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,
PackageParser.Package 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);
synchronized (mPackages) {
if (ps.parentPackageName != null
&& (!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0)) {
if (DEBUG_REMOVE) {
Slog.d(TAG, "Uninstalled child package:" + packageName + " for user:"
+ ((user == null) ? UserHandle.USER_ALL : user));
}
final int removedUserId = (user != null) ? user.getIdentifier()
: UserHandle.USER_ALL;
clearPackageStateForUserLIF(ps, removedUserId, outInfo, flags);
markPackageUninstalledForUserLPw(ps, user);
scheduleWritePackageRestrictionsLocked(user);
return;
}
}
final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
if (ps.getPermissionsState().hasPermission(Manifest.permission.SUSPEND_APPS, userId)) {
unsuspendForSuspendingPackage(packageName, userId);
}
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.
synchronized (mPackages) {
markPackageUninstalledForUserLPw(ps, user);
if (!systemApp) {
// Do not uninstall the APK if an app should be cached
boolean keepUninstalledPackage = shouldKeepUninstalledPackageLPr(packageName);
if (ps.isAnyInstalled(sUserManager.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");
clearPackageStateForUserLIF(ps, userId, outInfo, flags);
scheduleWritePackageRestrictionsLocked(user);
return;
} 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);
}
} 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");
clearPackageStateForUserLIF(ps, userId, outInfo, flags);
scheduleWritePackageRestrictionsLocked(user);
return;
}
}
}
// If we are deleting a composite package for all users, keep track
// of result for each child.
if (ps.childPackageNames != null && outInfo != null) {
synchronized (mPackages) {
final int childCount = ps.childPackageNames.size();
outInfo.removedChildPackages = new ArrayMap<>(childCount);
for (int i = 0; i < childCount; i++) {
String childPackageName = ps.childPackageNames.get(i);
PackageRemovedInfo childInfo = new PackageRemovedInfo(this);
childInfo.removedPackage = childPackageName;
childInfo.installerPackageName = ps.installerPackageName;
outInfo.removedChildPackages.put(childPackageName, childInfo);
PackageSetting childPs = mSettings.getPackageLPr(childPackageName);
if (childPs != null) {
childInfo.origUsers = childPs.queryInstalledUsers(allUserHandles, true);
}
}
}
}
// 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, replacingPackage);
}
// Take a note whether we deleted the package for all users
if (outInfo != null) {
outInfo.removedForAllUsers = mPackages.get(ps.name) == null;
if (outInfo.removedChildPackages != null) {
synchronized (mPackages) {
final int childCount = outInfo.removedChildPackages.size();
for (int i = 0; i < childCount; i++) {
PackageRemovedInfo childInfo = outInfo.removedChildPackages.valueAt(i);
if (childInfo != null) {
childInfo.removedForAllUsers = mPackages.get(
childInfo.removedPackage) == null;
}
}
}
}
// If we uninstalled an update to a system app there may be some
// child packages that appeared as they are declared in the system
// app but were not declared in the update.
if (systemApp) {
synchronized (mPackages) {
PackageSetting updatedPs = mSettings.getPackageLPr(ps.name);
final int childCount = (updatedPs.childPackageNames != null)
? updatedPs.childPackageNames.size() : 0;
for (int i = 0; i < childCount; i++) {
String childPackageName = updatedPs.childPackageNames.get(i);
if (outInfo.removedChildPackages == null
|| outInfo.removedChildPackages.indexOfKey(childPackageName) < 0) {
PackageSetting childPs = mSettings.getPackageLPr(childPackageName);
if (childPs == null) {
continue;
}
PackageInstalledInfo installRes = new PackageInstalledInfo();
installRes.name = childPackageName;
installRes.newUsers = childPs.queryInstalledUsers(allUserHandles, true);
installRes.pkg = mPackages.get(childPackageName);
installRes.uid = childPs.pkg.applicationInfo.uid;
if (outInfo.appearedChildPackages == null) {
outInfo.appearedChildPackages = new ArrayMap<>();
}
outInfo.appearedChildPackages.put(childPackageName, installRes);
}
}
}
}
}
}
@GuardedBy("mPackages")
private void markPackageUninstalledForUserLPw(PackageSetting ps, UserHandle user) {
final int[] userIds = (user == null || user.getIdentifier() == UserHandle.USER_ALL)
? sUserManager.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 /*suspendingPackage*/,
null /*dialogInfo*/,
null /*suspendedAppExtras*/,
null /*suspendedLauncherExtras*/,
false /*instantApp*/,
false /*virtualPreload*/,
null /*lastDisableAppCaller*/,
null /*enabledComponents*/,
null /*disabledComponents*/,
ps.readUserState(nextUserId).domainVerificationStatus,
0, PackageManager.INSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/);
}
mSettings.writeKernelMappingLPr(ps);
}
private void clearPackageStateForUserLIF(PackageSetting ps, int userId,
PackageRemovedInfo outInfo, int flags) {
final PackageParser.Package pkg;
synchronized (mPackages) {
pkg = mPackages.get(ps.name);
}
final int[] userIds = (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds()
: new int[] {userId};
for (int nextUserId : userIds) {
if (DEBUG_REMOVE) {
Slog.d(TAG, "Updating package:" + ps.name + " install state for user:"
+ nextUserId);
}
destroyAppDataLIF(pkg, userId,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
destroyAppProfilesLIF(pkg, userId);
clearDefaultBrowserIfNeededForUser(ps.name, userId);
removeKeystoreDataIfNeeded(nextUserId, ps.appId);
synchronized (mPackages) {
if (clearPackagePreferredActivitiesLPw(ps.name, nextUserId)) {
scheduleWritePackageRestrictionsLocked(nextUserId);
}
resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, nextUserId);
}
// Also delete contributed media, when requested
if ((flags & PackageManager.DELETE_CONTRIBUTED_MEDIA) != 0) {
try {
MediaStore.deleteContributedMedia(mContext, ps.name, UserHandle.of(nextUserId));
} catch (IOException e) {
Slog.w(TAG, "Failed to delete contributed media for " + ps.name, e);
}
}
}
if (outInfo != null) {
outInfo.removedPackage = ps.name;
outInfo.installerPackageName = ps.installerPackageName;
outInfo.isStaticSharedLib = pkg != null && pkg.staticSharedLibName != 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 PackageParser.Package pkg;
synchronized (mPackages) {
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 PackageSetting ps = mSettings.getPackageLPr(packageName);
final boolean filterApp = (ps != null && filterAppAccessLPr(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 (mPackages) {
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);
}
}
} 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
PackageParser.Package pkg;
synchronized (mPackages) {
pkg = mPackages.get(packageName);
if (pkg == null) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
pkg = ps.pkg;
}
}
if (pkg == null) {
Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
return false;
}
PackageSetting ps = (PackageSetting) pkg.mExtras;
resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, userId);
}
clearAppDataLIF(pkg, userId,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
removeKeystoreDataIfNeeded(userId, appId);
UserManagerInternal umInternal = 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, userId, flags);
return true;
}
/**
* Reverts user permission state changes (permissions and flags) in
* all packages for a given user.
*
* @param userId The device user for which to do a reset.
*/
@GuardedBy("mPackages")
private void resetUserChangesToRuntimePermissionsAndFlagsLPw(int userId) {
final int packageCount = mPackages.size();
for (int i = 0; i < packageCount; i++) {
PackageParser.Package pkg = mPackages.valueAt(i);
PackageSetting ps = (PackageSetting) pkg.mExtras;
resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, userId);
}
}
private void resetNetworkPolicies(int userId) {
LocalServices.getService(NetworkPolicyManagerInternal.class).resetUserState(userId);
}
/**
* Reverts user permission state changes (permissions and flags).
*
* @param ps The package for which to reset.
* @param userId The device user for which to do a reset.
*/
@GuardedBy("mPackages")
private void resetUserChangesToRuntimePermissionsAndFlagsLPw(
final PackageSetting ps, final int userId) {
if (ps.pkg == null) {
return;
}
// These are flags that can change base on user actions.
final int userSettableMask = FLAG_PERMISSION_USER_SET
| FLAG_PERMISSION_USER_FIXED
| FLAG_PERMISSION_REVOKE_ON_UPGRADE
| FLAG_PERMISSION_REVIEW_REQUIRED;
final int policyOrSystemFlags = FLAG_PERMISSION_SYSTEM_FIXED
| FLAG_PERMISSION_POLICY_FIXED;
boolean writeInstallPermissions = false;
boolean writeRuntimePermissions = false;
final int permissionCount = ps.pkg.requestedPermissions.size();
for (int i = 0; i < permissionCount; i++) {
final String permName = ps.pkg.requestedPermissions.get(i);
final BasePermission bp =
(BasePermission) mPermissionManager.getPermissionTEMP(permName);
if (bp == null) {
continue;
}
if (bp.isRemoved()) {
continue;
}
// If shared user we just reset the state to which only this app contributed.
if (ps.sharedUser != null) {
boolean used = false;
final int packageCount = ps.sharedUser.packages.size();
for (int j = 0; j < packageCount; j++) {
PackageSetting pkg = ps.sharedUser.packages.valueAt(j);
if (pkg.pkg != null && !pkg.pkg.packageName.equals(ps.pkg.packageName)
&& pkg.pkg.requestedPermissions.contains(permName)) {
used = true;
break;
}
}
if (used) {
continue;
}
}
final PermissionsState permissionsState = ps.getPermissionsState();
final int oldFlags = permissionsState.getPermissionFlags(permName, userId);
// Always clear the user settable flags.
final boolean hasInstallState =
permissionsState.getInstallPermissionState(permName) != null;
// If permission review is enabled and this is a legacy app, mark the
// permission as requiring a review as this is the initial state.
int flags = 0;
if (ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M && bp.isRuntime()) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
}
if (permissionsState.updatePermissionFlags(bp, userId, userSettableMask, flags)) {
if (hasInstallState) {
writeInstallPermissions = true;
} else {
writeRuntimePermissions = true;
}
}
// Below is only runtime permission handling.
if (!bp.isRuntime()) {
continue;
}
// Never clobber system or policy.
if ((oldFlags & policyOrSystemFlags) != 0) {
continue;
}
// If this permission was granted by default, make sure it is.
if ((oldFlags & FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0) {
if (permissionsState.grantRuntimePermission(bp, userId)
!= PERMISSION_OPERATION_FAILURE) {
writeRuntimePermissions = true;
}
// If permission review is enabled the permissions for a legacy apps
// are represented as constantly granted runtime ones, so don't revoke.
} else if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
// Otherwise, reset the permission.
final int revokeResult = permissionsState.revokeRuntimePermission(bp, userId);
switch (revokeResult) {
case PERMISSION_OPERATION_SUCCESS:
case PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
writeRuntimePermissions = true;
final int appId = ps.appId;
mHandler.post(
() -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));
} break;
}
}
}
// Synchronously write as we are taking permissions away.
if (writeRuntimePermissions) {
mSettings.writeRuntimePermissionsForUserLPr(userId, true);
}
// Synchronously write as we are taking permissions away.
if (writeInstallPermissions) {
mSettings.writeLPr();
}
}
/**
* Remove entries from the keystore daemon. Will only remove it if the
* {@code appId} is valid.
*/
private static void removeKeystoreDataIfNeeded(int userId, int appId) {
if (appId < 0) {
return;
}
final KeyStore keyStore = KeyStore.getInstance();
if (keyStore != null) {
if (userId == UserHandle.USER_ALL) {
for (final int individual : sUserManager.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 PackageParser.Package pkg;
synchronized (mPackages) {
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 : (PackageSetting) pkg.mExtras;
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 = StorageManager.FLAG_STORAGE_DE
| StorageManager.FLAG_STORAGE_CE;
// 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 userHandle,
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 (mPackages) {
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("mPackages")
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.applicationInfo.targetSdkVersion;
if (v < vers) vers = v;
}
}
return vers;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (ps.pkg != null) {
return ps.pkg.applicationInfo.targetSdkVersion;
}
}
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
@GuardedBy("mPackages")
private int getPackageTargetSdkVersionLockedLPr(String packageName) {
final PackageParser.Package p = mPackages.get(packageName);
if (p != null) {
return p.applicationInfo.targetSdkVersion;
}
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 (filter.countActions() == 0) {
Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
return;
}
synchronized (mPackages) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
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);
}
PreferredIntentResolver pir = mSettings.editPreferredActivitiesLPw(userId);
Slog.i(TAG, opname + " activity " + activity.flattenToShortString() + " for user "
+ userId + ":");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
scheduleWritePackageRestrictionsLocked(userId);
if (!updateDefaultHomeLPw(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);
try {
am.broadcastIntent(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");
synchronized (mPackages) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
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);
}
PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
if (pir != null) {
// Get all of the existing entries that exactly match this filter.
ArrayList<PreferredActivity> existing = pir.findFilters(filter);
if (existing != null && existing.size() == 1) {
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 = 0; i < existing.size(); i++) {
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 (mPackages) {
PackageParser.Package 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
&& filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
return;
}
int user = UserHandle.getCallingUserId();
if (clearPackagePreferredActivitiesLPw(packageName, user)) {
scheduleWritePackageRestrictionsLocked(user);
}
}
}
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
@GuardedBy("mPackages")
boolean clearPackagePreferredActivitiesLPw(String packageName, int userId) {
return clearPackagePreferredActivitiesLPw(packageName, false, userId);
}
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
@GuardedBy("mPackages")
private boolean clearPackagePreferredActivitiesLPw(String packageName,
boolean skipUpdateDefaultHome, int userId) {
ArrayList<PreferredActivity> removed = null;
boolean changed = false;
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);
}
changed = true;
if (!skipUpdateDefaultHome) {
updateDefaultHomeLPw(thisUserId);
}
}
}
if (changed) {
postPreferredActivityChangedBroadcast(userId);
}
return changed;
}
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
@GuardedBy("mPackages")
private void clearIntentFilterVerificationsLPw(int userId) {
final int packageCount = mPackages.size();
for (int i = 0; i < packageCount; i++) {
PackageParser.Package pkg = mPackages.valueAt(i);
clearIntentFilterVerificationsLPw(pkg.packageName, userId);
}
}
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
@GuardedBy("mPackages")
void clearIntentFilterVerificationsLPw(String packageName, int userId) {
if (userId == UserHandle.USER_ALL) {
if (mSettings.removeIntentFilterVerificationLPw(packageName,
sUserManager.getUserIds())) {
for (int oneUserId : sUserManager.getUserIds()) {
scheduleWritePackageRestrictionsLocked(oneUserId);
}
}
} else {
if (mSettings.removeIntentFilterVerificationLPw(packageName, userId)) {
scheduleWritePackageRestrictionsLocked(userId);
}
}
}
/** Clears state for all users, and touches intent filter verification policy */
void clearDefaultBrowserIfNeeded(String packageName) {
for (int oneUserId : sUserManager.getUserIds()) {
clearDefaultBrowserIfNeededForUser(packageName, oneUserId);
}
}
private void clearDefaultBrowserIfNeededForUser(String packageName, int userId) {
final String defaultBrowserPackageName = getDefaultBrowserPackageName(userId);
if (!TextUtils.isEmpty(defaultBrowserPackageName)) {
if (packageName.equals(defaultBrowserPackageName)) {
setDefaultBrowserPackageName(null, userId);
}
}
}
@Override
public void resetApplicationPreferences(int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
final long identity = Binder.clearCallingIdentity();
// writer
try {
synchronized (mPackages) {
clearPackagePreferredActivitiesLPw(null, true, userId);
mSettings.applyDefaultPreferredAppsLPw(userId);
updateDefaultHomeLPw(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.
setDefaultBrowserPackageName(null, userId);
clearIntentFilterVerificationsLPw(userId);
primeDomainVerificationsLPw(userId);
resetUserChangesToRuntimePermissionsAndFlagsLPw(userId);
scheduleWritePackageRestrictionsLocked(userId);
}
resetNetworkPolicies(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 (mPackages) {
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;
}
synchronized (mPackages) {
Slog.i(TAG, "Adding persistent preferred activity " + activity + " for user " + userId +
":");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
new PersistentPreferredActivity(filter, activity));
scheduleWritePackageRestrictionsLocked(userId);
postPreferredActivityChangedBroadcast(userId);
updateDefaultHomeLPw(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 (mPackages) {
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) {
scheduleWritePackageRestrictionsLocked(userId);
postPreferredActivityChangedBroadcast(userId);
updateDefaultHomeLPw(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 (mPackages) {
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 (mPackages) {
mSettings.readPreferredActivitiesLPw(readParser, readUserId);
updateDefaultHomeLPw(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 (mPackages) {
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) -> {
String defaultBrowser;
synchronized (mPackages) {
mSettings.readDefaultAppsLPw(parser1, userId1);
defaultBrowser = mSettings.removeDefaultBrowserPackageNameLPw(userId1);
}
if (defaultBrowser != null) {
PackageManagerInternal.DefaultBrowserProvider provider;
synchronized (mPackages) {
provider = mDefaultBrowserProvider;
}
provider.setDefaultBrowser(defaultBrowser, 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 (mPackages) {
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 (mPackages) {
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(
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 (mPackages) {
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(
UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
synchronized (mPackages) {
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) {
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);
PackageManagerInternal.DefaultHomeProvider provider;
synchronized (mPackages) {
provider = mDefaultHomeProvider;
}
if (provider == null) {
Slog.e(TAG, "mDefaultHomeProvider is null");
return null;
}
String packageName = provider.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;
}
/**
* @return Whether the ACTION_PREFERRED_ACTIVITY_CHANGED broadcast has been scheduled.
*/
private boolean updateDefaultHomeLPw(int userId) {
Intent intent = getHomeIntent();
List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
PackageManager.GET_META_DATA, userId);
ResolveInfo preferredResolveInfo = findPreferredActivity(intent, null, 0, resolveInfos,
0, true, false, false, userId);
String packageName = preferredResolveInfo != null
&& preferredResolveInfo.activityInfo != null
? preferredResolveInfo.activityInfo.packageName : null;
String currentPackageName = mDefaultHomeProvider.getDefaultHome(userId);
if (TextUtils.equals(currentPackageName, packageName)) {
return false;
}
String[] callingPackages = getPackagesForUid(Binder.getCallingUid());
if (callingPackages != null && ArrayUtils.contains(callingPackages,
mRequiredPermissionControllerPackage)) {
// PermissionController manages default home directly.
return false;
}
mDefaultHomeProvider.setDefaultHomeAsync(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 getSetupWizardPackageName() {
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 getSystemTextClassifierPackageName() {
return mContext.getString(R.string.config_defaultTextClassifierPackage);
}
@Override
public String getAttentionServicePackageName() {
return mContext.getString(R.string.config_defaultAttentionService);
}
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;
}
}
@Override
public String getWellbeingPackageName() {
return 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 appPredictionServiceComponentName.getPackageName();
}
@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 systemCaptionsServiceComponentName.getPackageName();
}
public String getIncidentReportApproverPackageName() {
return mContext.getString(R.string.config_incidentReportApproverPackage);
}
@Override
public void setApplicationEnabledSetting(String appPackageName,
int newState, int flags, int userId, String callingPackage) {
if (!sUserManager.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 (mPackages) {
final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting != null) {
pkgSetting.setUpdateAvailable(updateAvailable);
}
}
}
@Override
public void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags, int userId) {
if (!sUserManager.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 (mPackages) {
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
if (!allowedByPermission
|| filterAppAccessLPr(pkgSetting, callingUid, userId)) {
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 (mPackages) {
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 (mPackages) {
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 PackageParser.Package deletedPkg = pkgSetting.pkg;
final boolean isSystemStub = deletedPkg.isStub
&& deletedPkg.isSystem();
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
final File codePath = decompressPackage(deletedPkg);
if (codePath == null) {
Slog.e(TAG, "couldn't decompress pkg: " + pkgSetting.name);
return;
}
// TODO remove direct parsing of the package object during internal cleanup
// of scan package
// We need to call parse directly here for no other reason than we need
// the new package in order to disable the old one [we use the information
// for some internal optimization to optionally create a new package setting
// object on replace]. However, we can't get the package from the scan
// because the scan modifies live structures and we need to remove the
// old [system] package from the system before a scan can be attempted.
// Once scan is indempotent we can remove this parse and use the package
// object we scanned, prior to adding it to package settings.
final PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);
final PackageParser.Package tmpPkg;
try {
final @ParseFlags int parseFlags = mDefParseFlags
| PackageParser.PARSE_MUST_BE_APK
| PackageParser.PARSE_IS_SYSTEM_DIR;
tmpPkg = pp.parsePackage(codePath, parseFlags);
} catch (PackageParserException e) {
Slog.w(TAG, "Failed to parse compressed system package:" + pkgSetting.name, e);
return;
}
synchronized (mInstallLock) {
// Disable the stub and remove any package entries
removePackageLI(deletedPkg, true);
synchronized (mPackages) {
disableSystemPackageLPw(deletedPkg, tmpPkg);
}
final PackageParser.Package pkg;
try (PackageFreezer freezer =
freezePackage(deletedPkg.packageName, "setEnabledSetting")) {
final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
| PackageParser.PARSE_ENFORCE_CODE;
pkg = scanPackageTracedLI(codePath, parseFlags, 0 /*scanFlags*/,
0 /*currentTime*/, null /*user*/);
prepareAppDataAfterInstallLIF(pkg);
synchronized (mPackages) {
try {
updateSharedLibrariesLocked(pkg, null, mPackages);
} catch (PackageManagerException e) {
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e);
}
mPermissionManager.updatePermissions(
pkg.packageName, pkg, true, mPackages.values(),
mPermissionCallback);
mSettings.writeLPr();
}
} catch (PackageManagerException e) {
// Whoops! Something went wrong; try to roll back to the stub
Slog.w(TAG, "Failed to install compressed system package:"
+ pkgSetting.name, e);
// Remove the failed install
removeCodePathLI(codePath);
// Install the system package
try (PackageFreezer freezer =
freezePackage(deletedPkg.packageName, "setEnabledSetting")) {
synchronized (mPackages) {
// 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.
// This leaves us in a fragile state; the stub should never be
// enabled, so, cross your fingers and hope nothing goes wrong
// until we can disable the package later.
enableSystemPackageLPw(deletedPkg);
}
installPackageFromSystemLIF(deletedPkg.codePath,
/*isPrivileged*/ null /*allUserHandles*/,
null /*origUserHandles*/, null /*origPermissionsState*/,
true /*writeSettings*/);
} catch (PackageManagerException pme) {
Slog.w(TAG, "Failed to restore system package:"
+ deletedPkg.packageName, pme);
} finally {
synchronized (mPackages) {
mSettings.disableSystemPackageLPw(
deletedPkg.packageName, true /*replaced*/);
mSettings.writeLPr();
}
}
return;
}
clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE
| FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
mDexManager.notifyPackageUpdated(pkg.packageName,
pkg.baseCodePath, pkg.splitCodePaths);
}
}
if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
// Don't care about who enables an app.
callingPackage = null;
}
synchronized (mPackages) {
pkgSetting.setEnabled(newState, userId, callingPackage);
}
} else {
synchronized (mPackages) {
// We're dealing with a component level state change
// First, verify that this is a valid class name.
PackageParser.Package pkg = pkgSetting.pkg;
if (pkg == null || !pkg.hasComponentClassName(className)) {
if (pkg != null &&
pkg.applicationInfo.targetSdkVersion >=
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 (mPackages) {
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);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
@Override
public void flushPackageRestrictionsAsUser(int userId) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return;
}
if (!sUserManager.exists(userId)) {
return;
}
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
false /* checkShell */, "flushPackageRestrictions");
synchronized (mPackages) {
mSettings.writePackageRestrictionsLPr(userId);
mDirtyUsers.remove(userId);
if (mDirtyUsers.isEmpty()) {
mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
}
}
}
private void sendPackageChangedBroadcast(String packageName,
boolean killFlag, ArrayList<String> componentNames, int packageUid) {
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, killFlag);
extras.putInt(Intent.EXTRA_UID, packageUid);
// 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;
sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null,
userIds, instantUserIds);
}
@Override
public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
if (!sUserManager.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 (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (!filterAppAccessLPr(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 (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
return null;
}
return mSettings.getInstallerPackageNameLPr(packageName);
}
}
public boolean isOrphaned(String packageName) {
// reader
synchronized (mPackages) {
if (!mPackages.containsKey(packageName)) {
return false;
}
return mSettings.isOrphaned(packageName);
}
}
@Override
public int getApplicationEnabledSetting(String packageName, int userId) {
if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get enabled");
// reader
synchronized (mPackages) {
if (filterAppAccessLPr(mSettings.getPackageLPr(packageName), callingUid, userId)) {
return COMPONENT_ENABLED_STATE_DISABLED;
}
return mSettings.getApplicationEnabledSettingLPr(packageName, userId);
}
}
@Override
public int getComponentEnabledSetting(@NonNull ComponentName component, int userId) {
if (component == null) return COMPONENT_ENABLED_STATE_DEFAULT;
if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getComponentEnabled");
synchronized (mPackages) {
if (filterAppAccessLPr(mSettings.getPackageLPr(component.getPackageName()), callingUid,
component, TYPE_UNKNOWN, userId)) {
return COMPONENT_ENABLED_STATE_DISABLED;
}
return mSettings.getComponentEnabledSettingLPr(component, userId);
}
}
@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);
// 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(), this,
mContext.getContentResolver(), UserHandle.USER_SYSTEM);
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);
}
int[] grantPermissionsUserIds = EMPTY_INT_ARRAY;
synchronized (mPackages) {
// 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));
}
}
for (int userId : UserManagerService.getInstance().getUserIds()) {
if (!mSettings.areDefaultRuntimePermissionsGrantedLPr(userId)) {
grantPermissionsUserIds = ArrayUtils.appendInt(
grantPermissionsUserIds, userId);
}
}
}
sUserManager.systemReady();
// If we upgraded grant all default permissions before kicking off.
for (int userId : grantPermissionsUserIds) {
mDefaultPermissionPolicy.grantDefaultPermissions(userId);
}
if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
// If we did not grant default permissions, we preload from this the
// default permission exceptions lazily to ensure we don't hit the
// disk on a new user creation.
mDefaultPermissionPolicy.scheduleReadDefaultPermissionExceptions();
}
// 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 (mPackages) {
mPermissionManager.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(),
mPermissionCallback);
}
// Watch for external volumes that come and go over time
final StorageManager storage = mContext.getSystemService(StorageManager.class);
storage.registerListener(mStorageListener);
mInstallerService.systemReady();
mApexManager.systemReady();
mPackageDexOptimizer.systemReady();
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
sUserManager.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));
}
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();
}
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)).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(" 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 ("s".equals(cmd) || "shared-users".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_SHARED_USERS);
} 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 (mPackages) {
mSettings.writeLPr();
pw.println("Settings written.");
return;
}
}
}
if (checkin) {
pw.println("vers,1");
}
// reader
synchronized (mPackages) {
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 : sUserManager.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_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, packageName);
}
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 : sUserManager.getUsers(false)) {
setSystemAppInstallState(packageName, false, user.id);
}
}
}
private void dumpProto(FileDescriptor fd) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
synchronized (mPackages) {
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).writeToProto(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("mPackages")
@SuppressWarnings("resource")
private void dumpDexoptStateLPr(PrintWriter pw, String packageName) {
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.println();
ipw.println("Dexopt state:");
ipw.increaseIndent();
Collection<PackageParser.Package> packages;
if (packageName != null) {
PackageParser.Package 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 (PackageParser.Package pkg : packages) {
ipw.println("[" + pkg.packageName + "]");
ipw.increaseIndent();
mPackageDexOptimizer.dumpDexoptState(ipw, pkg,
mDexManager.getPackageUseInfoOrDefault(pkg.packageName));
ipw.decreaseIndent();
}
}
@GuardedBy("mPackages")
@SuppressWarnings("resource")
private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) {
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.println();
ipw.println("Compiler stats:");
ipw.increaseIndent();
Collection<PackageParser.Package> packages;
if (packageName != null) {
PackageParser.Package 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 (PackageParser.Package pkg : packages) {
ipw.println("[" + pkg.packageName + "]");
ipw.increaseIndent();
CompilerStats.PackageStats stats = getCompilerPackageStats(pkg.packageName);
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<ApplicationInfo> infos, IIntentReceiver finishedReceiver) {
final int size = infos.size();
final String[] packageNames = new String[size];
final int[] packageUids = new int[size];
for (int i = 0; i < size; i++) {
final ApplicationInfo info = infos.get(i);
packageNames[i] = info.packageName;
packageUids[i] = info.uid;
}
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;
sendPackageBroadcast(action, null, extras, 0, null, finishedReceiver, 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<ApplicationInfo> loaded = new ArrayList<>();
final int parseFlags = mDefParseFlags | PackageParser.PARSE_EXTERNAL_STORAGE;
final VersionInfo ver;
final List<PackageSetting> packages;
synchronized (mPackages) {
ver = mSettings.findOrCreateVersion(volumeUuid);
packages = mSettings.getVolumePackagesLPr(volumeUuid);
}
for (PackageSetting ps : packages) {
freezers.add(freezePackage(ps.name, "loadPrivatePackagesInner"));
synchronized (mInstallLock) {
final PackageParser.Package pkg;
try {
pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0, null);
loaded.add(pkg.applicationInfo);
} 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,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
}
}
// Reconcile app data for all started/unlocked users
final StorageManager sm = mContext.getSystemService(StorageManager.class);
final UserManager um = mContext.getSystemService(UserManager.class);
UserManagerInternal umInternal = getUserManagerInternal();
for (UserInfo user : um.getUsers()) {
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 (mPackages) {
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, mPackages.values(),
mPermissionCallback);
// 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<ApplicationInfo> unloaded = new ArrayList<>();
synchronized (mInstallLock) {
synchronized (mPackages) {
final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid);
for (PackageSetting ps : packages) {
if (ps.pkg == null) continue;
final ApplicationInfo info = ps.pkg.applicationInfo;
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(info);
} 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 (mPackages) {
// 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 (mPackages) {
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 (absolutePath.startsWith(absoluteCodePath)) {
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 = mContext.getSystemService(StorageManager.class);
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 (mPackages) {
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.coreApp) {
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 mPackages} lock held</em>
*/
private void prepareAppDataAfterInstallLIF(PackageParser.Package pkg) {
final PackageSetting ps;
synchronized (mPackages) {
ps = mSettings.mPackages.get(pkg.packageName);
mSettings.writeKernelMappingLPr(ps);
}
final UserManager um = mContext.getSystemService(UserManager.class);
UserManagerInternal umInternal = getUserManagerInternal();
for (UserInfo user : um.getUsers()) {
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);
}
}
}
/**
* 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(PackageParser.Package pkg, int userId, int flags) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
prepareAppDataLeafLIF(pkg, userId, flags);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
prepareAppDataLeafLIF(pkg.childPackages.get(i), userId, flags);
}
}
private void prepareAppDataAndMigrateLIF(PackageParser.Package 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(PackageParser.Package pkg, int userId, int flags) {
if (DEBUG_APP_DATA) {
Slog.v(TAG, "prepareAppData for " + pkg.packageName + " u" + userId + " 0x"
+ Integer.toHexString(flags));
}
final PackageSetting ps;
synchronized (mPackages) {
ps = mSettings.mPackages.get(pkg.packageName);
}
final String volumeUuid = pkg.volumeUuid;
final String packageName = pkg.packageName;
ApplicationInfo app = (ps == null)
? pkg.applicationInfo
: PackageParser.generateApplicationInfo(pkg, 0, ps.readUserState(userId), userId);
if (app == null) {
app = pkg.applicationInfo;
}
final int appId = UserHandle.getAppId(app.uid);
Preconditions.checkNotNull(app.seInfo);
final String seInfo = app.seInfo + (app.seInfoUser != null ? app.seInfoUser : "");
long ceDataInode = -1;
try {
ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
appId, seInfo, app.targetSdkVersion);
} catch (InstallerException e) {
if (app.isSystemApp()) {
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, app.targetSdkVersion);
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 (mPackages) {
if (ps != null) {
ps.setCeDataInode(ceDataInode, userId);
}
}
}
prepareAppDataContentsLeafLIF(pkg, userId, flags);
if (getStorageManagerInternal() != null) {
getStorageManagerInternal().prepareSandboxForApp(
pkg.packageName, appId, pkg.mSharedUserId, userId);
}
}
private void prepareAppDataContentsLIF(PackageParser.Package pkg, int userId, int flags) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
prepareAppDataContentsLeafLIF(pkg, userId, flags);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
prepareAppDataContentsLeafLIF(pkg.childPackages.get(i), userId, flags);
}
}
private void prepareAppDataContentsLeafLIF(PackageParser.Package pkg, int userId, int flags) {
final String volumeUuid = pkg.volumeUuid;
final String packageName = pkg.packageName;
final ApplicationInfo app = pkg.applicationInfo;
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.
if (app.primaryCpuAbi != null && !VMRuntime.is64BitAbi(app.primaryCpuAbi)) {
final String nativeLibPath = app.nativeLibraryDir;
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(PackageParser.Package pkg, int userId) {
if (pkg.isSystem() && !StorageManager.isFileEncryptedNativeOrEmulated()
&& PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
final int storageTarget = pkg.applicationInfo.isDefaultToDeviceProtectedStorage()
? StorageManager.FLAG_STORAGE_DE : StorageManager.FLAG_STORAGE_CE;
try {
mInstaller.migrateAppData(pkg.volumeUuid, pkg.packageName, userId,
storageTarget);
} catch (InstallerException e) {
logCriticalInfo(Log.WARN,
"Failed to migrate " + pkg.packageName + ": " + 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 PackageFreezer[] mChildren;
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;
mChildren = null;
mWeFroze = false;
mCloseGuard.open("close");
}
public PackageFreezer(String packageName, int userId, String killReason) {
synchronized (mPackages) {
mPackageName = packageName;
mWeFroze = mFrozenPackages.add(mPackageName);
final PackageSetting ps = mSettings.mPackages.get(mPackageName);
if (ps != null) {
killApplication(ps.name, ps.appId, userId, killReason);
}
final PackageParser.Package p = mPackages.get(packageName);
if (p != null && p.childPackages != null) {
final int N = p.childPackages.size();
mChildren = new PackageFreezer[N];
for (int i = 0; i < N; i++) {
mChildren[i] = new PackageFreezer(p.childPackages.get(i).packageName,
userId, killReason);
}
} else {
mChildren = null;
}
}
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 (mPackages) {
if (mWeFroze) {
mFrozenPackages.remove(mPackageName);
}
if (mChildren != null) {
for (PackageFreezer freezer : mChildren) {
freezer.close();
}
}
}
}
}
}
/**
* Verify that given package is currently frozen.
*/
private void checkPackageFrozen(String packageName) {
synchronized (mPackages) {
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 = mContext.getSystemService(StorageManager.class);
final PackageManager pm = mContext.getPackageManager();
final String currentVolumeUuid;
final File codeFile;
final String installerPackageName;
final String packageAbiOverride;
final int appId;
final String seinfo;
final String label;
final int targetSdkVersion;
final PackageFreezer freezer;
final int[] installedUserIds;
final boolean isCurrentLocationExternal;
// reader
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (pkg == null
|| ps == null
|| filterAppAccessLPr(ps, callingUid, user.getIdentifier())) {
throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
}
if (pkg.applicationInfo.isSystemApp()) {
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.codePath);
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.applicationInfo.isInternal() && 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 = isExternal(pkg);
codeFile = new File(pkg.codePath);
installerPackageName = ps.installerPackageName;
packageAbiOverride = ps.cpuAbiOverrideString;
appId = UserHandle.getAppId(pkg.applicationInfo.uid);
seinfo = pkg.applicationInfo.seInfo;
label = String.valueOf(pm.getApplicationLabel(pkg.applicationInfo));
targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
freezer = freezePackage(packageName, "movePackageInternal");
installedUserIds = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
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();
final String dataAppName = codeFile.getName();
move = new MoveInfo(moveId, currentVolumeUuid, volumeUuid, packageName,
dataAppName, appId, seinfo, targetSdkVersion);
} 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,
installerPackageName, volumeUuid, null /*verificationInfo*/, user,
packageAbiOverride, null /*grantedPermissions*/,
null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN,
PackageManager.INSTALL_REASON_UNKNOWN);
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 PackageParser.Package pkg;
synchronized (mPackages) {
pkg = mPackages.get(packageName);
}
if (pkg == null) {
return;
}
final StorageManager storage = mContext.getSystemService(StorageManager.class);
VolumeInfo volume = storage.findVolumeByUuid(pkg.applicationInfo.storageUuid.toString());
int packageExternalStorageType = getPackageExternalStorageType(volume, isExternal(pkg));
if (!isPreviousLocationExternal && isExternal(pkg)) {
// Move from internal to external storage.
StatsLog.write(StatsLog.APP_MOVED_STORAGE_REPORTED, packageExternalStorageType,
StatsLog.APP_MOVED_STORAGE_REPORTED__MOVE_TYPE__TO_EXTERNAL,
packageName);
} else if (isPreviousLocationExternal && !isExternal(pkg)) {
// Move from external to internal storage.
StatsLog.write(StatsLog.APP_MOVED_STORAGE_REPORTED, packageExternalStorageType,
StatsLog.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 = mContext.getSystemService(StorageManager.class);
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, int userHandle) {
synchronized (mPackages) {
mDirtyUsers.remove(userHandle);
mUserNeedsBadging.delete(userHandle);
mSettings.removeUserLPw(userHandle);
mPendingBroadcasts.remove(userHandle);
mInstantAppRegistry.onUserRemovedLPw(userHandle);
removeUnusedPackagesLPw(userManager, userHandle);
}
}
/**
* We're removing userHandle and would like to remove any downloaded packages
* that are no longer in use by any other user.
* @param userHandle the user being removed
*/
@GuardedBy("mPackages")
private void removeUnusedPackagesLPw(UserManagerService userManager, final int userHandle) {
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.packageName;
// Skip over if system app
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
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] != userHandle && 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,
userHandle, 0));
}
}
}
/** Called by UserManagerService */
void createNewUser(int userId, String[] disallowedPackages) {
synchronized (mInstallLock) {
mSettings.createNewUserLI(this, mInstaller, userId, disallowedPackages);
}
synchronized (mPackages) {
scheduleWritePackageRestrictionsLocked(userId);
scheduleWritePackageListLocked(userId);
primeDomainVerificationsLPw(userId);
}
}
void onNewUserCreated(final int userId) {
mDefaultPermissionPolicy.grantDefaultPermissions(userId);
synchronized(mPackages) {
// NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG
mPermissionManager.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, true, mPackages.values(),
mPermissionCallback);
}
}
@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 (mPackages) {
return mSettings.getVerifierDeviceIdentityLPw();
}
}
@Override
public void setPermissionEnforced(String permission, boolean enforced) {
// TODO: Now that we no longer change GID for storage, this should to away.
mContext.enforceCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
"setPermissionEnforced");
if (READ_EXTERNAL_STORAGE.equals(permission)) {
synchronized (mPackages) {
if (mSettings.mReadExternalStorageEnforced == null
|| mSettings.mReadExternalStorageEnforced != enforced) {
mSettings.mReadExternalStorageEnforced =
enforced ? Boolean.TRUE : Boolean.FALSE;
mSettings.writeLPr();
}
}
// kill any non-foreground processes so we restart them and
// grant/revoke the GID.
final IActivityManager am = ActivityManager.getService();
if (am != null) {
final long token = Binder.clearCallingIdentity();
try {
am.killProcessesBelowForeground("setPermissionEnforcement");
} catch (RemoteException e) {
} finally {
Binder.restoreCallingIdentity(token);
}
}
} else {
throw new IllegalArgumentException("No selective enforcement for " + permission);
}
}
@Override
@Deprecated
public boolean isPermissionEnforced(String permission) {
// allow instant applications
return true;
}
@Override
public boolean isStorageLow() {
// allow instant applications
final long token = Binder.clearCallingIdentity();
try {
final DeviceStorageMonitorInternal
dsm = LocalServices.getService(DeviceStorageMonitorInternal.class);
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 = sUserManager.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(mPackages) {
final PackageParser.Package 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 = (PackageSetting) pkg.mExtras;
if (filterAppAccessLPr(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(mPackages) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final PackageParser.Package 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 = (PackageSetting) pkg.mExtras;
if (filterAppAccessLPr(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.applicationInfo.uid != 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(mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null
|| filterAppAccessLPr((PackageSetting) pkg.mExtras, 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(mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null
|| filterAppAccessLPr((PackageSetting) pkg.mExtras, 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("mPackages")
private void deletePackageIfUnusedLPr(final String packageName) {
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) {
return;
}
if (!ps.isAnyInstalled(sUserManager.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(PackageParser.Package 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.baseRevisionCode) {
throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
"Update base revision code " + after.baseRevisionCode
+ " is older than current " + before.baseRevisionCode);
}
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.splitNames, splitName);
if (j != -1) {
if (after.splitRevisionCodes[i] < before.splitRevisionCodes[j]) {
throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
"Update split " + splitName + " revision code "
+ after.splitRevisionCodes[i] + " is older than current "
+ before.splitRevisionCodes[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 static class OnPermissionChangeListeners extends Handler {
private static final int MSG_ON_PERMISSIONS_CHANGED = 1;
private final RemoteCallbackList<IOnPermissionsChangeListener> mPermissionListeners =
new RemoteCallbackList<>();
public OnPermissionChangeListeners(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ON_PERMISSIONS_CHANGED: {
final int uid = msg.arg1;
handleOnPermissionsChanged(uid);
} break;
}
}
public void addListenerLocked(IOnPermissionsChangeListener listener) {
mPermissionListeners.register(listener);
}
public void removeListenerLocked(IOnPermissionsChangeListener listener) {
mPermissionListeners.unregister(listener);
}
public void onPermissionsChanged(int uid) {
if (mPermissionListeners.getRegisteredCallbackCount() > 0) {
obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0).sendToTarget();
}
}
private void handleOnPermissionsChanged(int uid) {
final int count = mPermissionListeners.beginBroadcast();
try {
for (int i = 0; i < count; i++) {
IOnPermissionsChangeListener callback = mPermissionListeners
.getBroadcastItem(i);
try {
callback.onPermissionsChanged(uid);
} catch (RemoteException e) {
Log.e(TAG, "Permission listener is dead", e);
}
}
} finally {
mPermissionListeners.finishBroadcast();
}
}
}
private class PackageManagerNative extends IPackageManagerNative.Stub {
@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 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)
| (appInfo.isProductServices()
? IPackageManagerNative.LOCATION_PRODUCT_SERVICES : 0));
}
}
private class PackageManagerInternalImpl extends PackageManagerInternal {
@Override
public void updatePermissionFlagsTEMP(String permName, String packageName, int flagMask,
int flagValues, int userId) {
PackageManagerService.this.updatePermissionFlags(
permName, packageName, flagMask, flagValues, true, userId);
}
@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;
}
PackageParser.Package pkg = packageSetting.pkg;
if (pkg == null) {
// May happen if package in on a removable sd card
return false;
}
return pkg.mSigningDetails.hasAncestorOrSelf(mPlatformPackage.mSigningDetails)
|| mPlatformPackage.mSigningDetails.checkCapability(pkg.mSigningDetails,
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 (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
if (p == null) {
return null;
}
return p.mSigningDetails;
}
}
private SigningDetails getSigningDetails(int uid) {
synchronized (mPackages) {
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 int getPermissionFlagsTEMP(String permName, String packageName, int userId) {
return PackageManagerService.this.getPermissionFlags(permName, packageName, userId);
}
@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(PackageParser.Package pkg, int callingUid, int userId) {
synchronized (mPackages) {
return PackageManagerService.this.filterAppAccessLPr(
(PackageSetting) pkg.mExtras, callingUid, userId);
}
}
@Override
public PackageParser.Package getPackage(String packageName) {
synchronized (mPackages) {
packageName = resolveInternalPackageNameLPr(
packageName, PackageManager.VERSION_CODE_HIGHEST);
return mPackages.get(packageName);
}
}
@Override
public PackageList getPackageList(PackageListObserver observer) {
synchronized (mPackages) {
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 (mPackages) {
mPackageListObservers.remove(observer);
}
}
@Override
public PackageParser.Package getDisabledSystemPackage(String packageName) {
synchronized (mPackages) {
final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
return (ps != null) ? ps.pkg : null;
}
}
@Override
public @Nullable String getDisabledSystemPackageName(@NonNull String packageName) {
PackageParser.Package pkg = getDisabledSystemPackage(packageName);
return pkg == null ? null : pkg.packageName;
}
@Override
public String getKnownPackageName(int knownPackage, int userId) {
switch(knownPackage) {
case PackageManagerInternal.PACKAGE_BROWSER:
return getDefaultBrowserPackageName(userId);
case PackageManagerInternal.PACKAGE_INSTALLER:
return mRequiredInstallerPackage;
case PackageManagerInternal.PACKAGE_SETUP_WIZARD:
return mSetupWizardPackage;
case PackageManagerInternal.PACKAGE_SYSTEM:
return "android";
case PackageManagerInternal.PACKAGE_VERIFIER:
return mRequiredVerifierPackage;
case PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER:
return mSystemTextClassifierPackage;
case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
return mRequiredPermissionControllerPackage;
case PackageManagerInternal.PACKAGE_WELLBEING:
return mWellbeingPackage;
case PackageManagerInternal.PACKAGE_DOCUMENTER:
return mDocumenterPackage;
case PackageManagerInternal.PACKAGE_CONFIGURATOR:
return mConfiguratorPackage;
case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
return mIncidentReportApproverPackage;
case PackageManagerInternal.PACKAGE_APP_PREDICTOR:
return mAppPredictionServicePackage;
}
return null;
}
@Override
public boolean isResolveActivityComponent(ComponentInfo component) {
return mResolveActivity.packageName.equals(component.packageName)
&& mResolveActivity.name.equals(component.name);
}
@Override
public void setLocationPackagesProvider(PackagesProvider provider) {
mDefaultPermissionPolicy.setLocationPackagesProvider(provider);
}
@Override
public void setLocationExtraPackagesProvider(PackagesProvider provider) {
mDefaultPermissionPolicy.setLocationExtraPackagesProvider(provider);
}
@Override
public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
mDefaultPermissionPolicy.setVoiceInteractionPackagesProvider(provider);
}
@Override
public void setUseOpenWifiAppPackagesProvider(PackagesProvider provider) {
mDefaultPermissionPolicy.setUseOpenWifiAppPackagesProvider(provider);
}
@Override
public void setSyncAdapterPackagesprovider(SyncAdapterPackagesProvider provider) {
mDefaultPermissionPolicy.setSyncAdapterPackagesProvider(provider);
}
@Override
public void onDefaultDialerAppChanged(String packageName, int userId) {
synchronized (mPackages) {
mSettings.setDefaultDialerPackageNameLPw(packageName, userId);
}
}
@Override
public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) {
mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultUseOpenWifiApp(
packageName, userId);
}
@Override
public void setKeepUninstalledPackages(final List<String> packageList) {
Preconditions.checkNotNull(packageList);
List<String> removedFromList = null;
synchronized (mPackages) {
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 (mPackages) {
return mPermissionManager.isPermissionsReviewRequired(
mPackages.get(packageName), 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 Bundle getSuspendedPackageLauncherExtras(String packageName, int userId) {
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
PersistableBundle launcherExtras = null;
if (ps != null) {
launcherExtras = ps.readUserState(userId).suspendedLauncherExtras;
}
return (launcherExtras != null) ? new Bundle(launcherExtras.deepCopy()) : null;
}
}
@Override
public boolean isPackageSuspended(String packageName, int userId) {
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
return (ps != null) ? ps.getSuspended(userId) : false;
}
}
@Override
public String getSuspendingPackage(String suspendedPackage, int userId) {
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
return (ps != null) ? ps.readUserState(userId).suspendingPackage : null;
}
}
@Override
public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId) {
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
return (ps != null) ? ps.readUserState(userId).dialogInfo : null;
}
}
@Override
public int getDistractingPackageRestrictions(String packageName, int userId) {
synchronized (mPackages) {
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 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, int flags, int filterCallingUid, int userId) {
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
return PackageManagerService.this
.queryIntentActivitiesInternal(intent, resolvedType, flags, 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 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));
}
}
unsuspendForNonSystemSuspendingPackages(usersWithPoOrDo);
}
@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 (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
return ps != null ? ps.getInstantApp(userId) : false;
}
}
@Override
public boolean wasPackageEverLaunched(String packageName, int userId) {
synchronized (mPackages) {
return mSettings.wasPackageEverLaunchedLPr(packageName, userId);
}
}
@Override
public boolean isEnabledAndMatches(ComponentInfo info, int flags, int userId) {
synchronized (mPackages) {
return mSettings.isEnabledAndMatchLPr(info, flags, userId);
}
}
@Override
public boolean userNeedsBadging(int userId) {
synchronized (mPackages) {
return PackageManagerService.this.userNeedsBadging(userId);
}
}
@Override
public void grantRuntimePermission(String packageName, String permName, int userId,
boolean overridePolicy) {
PackageManagerService.this.mPermissionManager.grantRuntimePermission(
permName, packageName, overridePolicy, getCallingUid(), userId,
mPermissionCallback);
}
@Override
public void revokeRuntimePermission(String packageName, String permName, int userId,
boolean overridePolicy) {
mPermissionManager.revokeRuntimePermission(
permName, packageName, overridePolicy, userId,
mPermissionCallback);
}
@Override
public String getNameForUid(int uid) {
return PackageManagerService.this.getNameForUid(uid);
}
@Override
public void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
Intent origIntent, String resolvedType, String callingPackage,
Bundle verificationBundle, int userId) {
PackageManagerService.this.requestInstantAppResolutionPhaseTwo(
responseObj, origIntent, resolvedType, callingPackage, verificationBundle,
userId);
}
@Override
public void grantEphemeralAccess(int userId, Intent intent,
int targetAppId, int ephemeralAppId) {
synchronized (mPackages) {
mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
targetAppId, ephemeralAppId);
}
}
@Override
public boolean isInstantAppInstallerComponent(ComponentName component) {
synchronized (mPackages) {
return mInstantAppInstallerActivity != null
&& mInstantAppInstallerActivity.getComponentName().equals(component);
}
}
@Override
public void pruneInstantApps() {
mInstantAppRegistry.pruneInstantApps();
}
@Override
public String getSetupWizardPackageName() {
return mSetupWizardPackage;
}
public void setExternalSourcesPolicy(ExternalSourcesPolicy policy) {
if (policy != null) {
mExternalSourcesPolicy = policy;
}
}
@Override
public boolean isPackagePersistent(String packageName) {
synchronized (mPackages) {
PackageParser.Package pkg = mPackages.get(packageName);
return pkg != null
? ((pkg.applicationInfo.flags&(ApplicationInfo.FLAG_SYSTEM
| ApplicationInfo.FLAG_PERSISTENT)) ==
(ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PERSISTENT))
: false;
}
}
@Override
public boolean isLegacySystemApp(PackageParser.Package pkg) {
synchronized (mPackages) {
final PackageSetting ps = (PackageSetting) pkg.mExtras;
return mPromoteSystemApps
&& ps.isSystem()
&& mExistingSystemPackages.contains(ps.name);
}
}
@Override
public List<PackageInfo> getOverlayPackages(int userId) {
final ArrayList<PackageInfo> overlayPackages = new ArrayList<PackageInfo>();
synchronized (mPackages) {
for (PackageParser.Package p : mPackages.values()) {
if (p.mOverlayTarget != null) {
PackageInfo pkg = generatePackageInfo((PackageSetting)p.mExtras, 0, userId);
if (pkg != null) {
overlayPackages.add(pkg);
}
}
}
}
return overlayPackages;
}
@Override
public List<String> getTargetPackageNames(int userId) {
List<String> targetPackages = new ArrayList<>();
synchronized (mPackages) {
for (PackageParser.Package p : mPackages.values()) {
if (p.mOverlayTarget == null) {
targetPackages.add(p.packageName);
}
}
}
return targetPackages;
}
@Override
public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName,
@Nullable List<String> overlayPackageNames) {
synchronized (mPackages) {
if (targetPackageName == null || mPackages.get(targetPackageName) == 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 PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
Slog.e(TAG, "failed to find package " + packageName);
return false;
}
overlayPaths.add(pkg.baseCodePath);
}
}
final PackageSetting ps = mSettings.mPackages.get(targetPackageName);
ps.setOverlayPaths(overlayPaths, userId);
return true;
}
}
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId, boolean resolveForStart, int filterCallingUid) {
return resolveIntentInternal(
intent, resolvedType, flags, 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 (mPackages) {
mIsolatedOwners.put(isolatedUid, ownerUid);
}
}
@Override
public void removeIsolatedUid(int isolatedUid) {
synchronized (mPackages) {
mIsolatedOwners.delete(isolatedUid);
}
}
@Override
public int getUidTargetSdkVersion(int uid) {
synchronized (mPackages) {
return getUidTargetSdkVersionLockedLPr(uid);
}
}
@Override
public int getPackageTargetSdkVersion(String packageName) {
synchronized (mPackages) {
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 (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
return ps != null && !PackageManagerService.this.filterAppAccessLPr(
ps, callingUid, component, TYPE_UNKNOWN, userId);
}
}
@Override
public boolean hasInstantApplicationMetadata(String packageName, int userId) {
synchronized (mPackages) {
return mInstantAppRegistry.hasInstantApplicationMetadataLPr(packageName, userId);
}
}
@Override
public void notifyPackageUse(String packageName, int reason) {
synchronized (mPackages) {
PackageManagerService.this.notifyPackageUseLocked(packageName, reason);
}
}
@Override
public CheckPermissionDelegate getCheckPermissionDelegate() {
synchronized (mPackages) {
return PackageManagerService.this.getCheckPermissionDelegateLocked();
}
}
@Override
public void setCheckPermissionDelegate(CheckPermissionDelegate delegate) {
synchronized (mPackages) {
PackageManagerService.this.setCheckPermissionDelegateLocked(delegate);
}
}
@Override
public SparseArray<String> getAppsWithSharedUserIds() {
synchronized (mPackages) {
return getAppsWithSharedUserIdsLocked();
}
}
@Override
public String getSharedUserIdForPackage(String packageName) {
synchronized (mPackages) {
return getSharedUserIdForPackageLocked(packageName);
}
}
@Override
public String[] getPackagesForSharedUserId(String sharedUserId, int userId) {
synchronized (mPackages) {
return getPackagesForSharedUserIdLocked(sharedUserId, 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<PackageParser.Package> actionLocked) {
PackageManagerService.this.forEachPackage(actionLocked);
}
@Override
public ArraySet<String> getEnabledComponents(String packageName, int userId) {
synchronized (mPackages) {
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 (mPackages) {
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 (mPackages) {
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) {
PackageParser.Package pkg;
synchronized (mPackages) {
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 (mPackages) {
return mSettings.removeDefaultBrowserPackageNameLPw(userId);
}
}
@Override
public void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider) {
synchronized (mPackages) {
mDefaultBrowserProvider = provider;
}
}
@Override
public void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider) {
synchronized (mPackages) {
mDefaultHomeProvider = provider;
}
}
@Override
public boolean isApexPackage(String packageName) {
return PackageManagerService.this.mApexManager.isApexPackage(packageName);
}
@Override
public void uninstallApex(String packageName, long versionCode, int userId,
IntentSender intentSender) {
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 (userId != UserHandle.USER_ALL) {
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.getActivePackage(packageName);
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 boolean wereDefaultPermissionsGrantedSinceBoot(int userId) {
synchronized (mPackages) {
return mDefaultPermissionPolicy.wereDefaultPermissionsGrantedSinceBoot(userId);
}
}
}
@GuardedBy("mPackages")
private SparseArray<String> getAppsWithSharedUserIdsLocked() {
final SparseArray<String> sharedUserIds = new SparseArray<>();
synchronized (mPackages) {
for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
sharedUserIds.put(UserHandle.getAppId(setting.userId), setting.name);
}
}
return sharedUserIds;
}
@GuardedBy("mPackages")
private String getSharedUserIdForPackageLocked(String packageName) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
return (ps != null && ps.isSharedUser()) ? ps.sharedUser.name : null;
}
@GuardedBy("mPackages")
private String[] getPackagesForSharedUserIdLocked(String sharedUserId, int userId) {
try {
final SharedUserSetting sus = mSettings.getSharedUserLPw(
sharedUserId, 0, 0, false);
if (sus == null) {
return EmptyArray.STRING;
}
String[] res = new String[sus.packages.size()];
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;
} else {
res = ArrayUtils.removeElement(String.class, res, res[i]);
}
}
return res;
} catch (PackageManagerException e) {
// Should not happen
}
return EmptyArray.STRING;
}
@Override
public int getRuntimePermissionsVersion(@UserIdInt int userId) {
Preconditions.checkArgumentNonnegative(userId);
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
"setRuntimePermissionVersion");
synchronized (mPackages) {
return mSettings.getDefaultRuntimePermissionsVersionLPr(userId);
}
}
@Override
public void setRuntimePermissionsVersion(int version, @UserIdInt int userId) {
Preconditions.checkArgumentNonnegative(version);
Preconditions.checkArgumentNonnegative(userId);
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
"setRuntimePermissionVersion");
synchronized (mPackages) {
mSettings.setDefaultRuntimePermissionsVersionLPr(version, userId);
}
}
@Override
public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) {
enforceSystemOrPhoneCaller("grantPermissionsToEnabledCarrierApps");
synchronized (mPackages) {
final long identity = Binder.clearCallingIdentity();
try {
mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledCarrierApps(
packageNames, userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@Override
public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) {
enforceSystemOrPhoneCaller("grantDefaultPermissionsToEnabledImsServices");
synchronized (mPackages) {
final long identity = Binder.clearCallingIdentity();
try {
mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledImsServices(
packageNames, userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@Override
public void grantDefaultPermissionsToEnabledTelephonyDataServices(
String[] packageNames, int userId) {
enforceSystemOrPhoneCaller("grantDefaultPermissionsToEnabledTelephonyDataServices");
synchronized (mPackages) {
Binder.withCleanCallingIdentity( () -> mDefaultPermissionPolicy.
grantDefaultPermissionsToEnabledTelephonyDataServices(
packageNames, userId));
}
}
@Override
public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(
String[] packageNames, int userId) {
enforceSystemOrPhoneCaller("revokeDefaultPermissionsFromDisabledTelephonyDataServices");
synchronized (mPackages) {
Binder.withCleanCallingIdentity( () -> mDefaultPermissionPolicy.
revokeDefaultPermissionsFromDisabledTelephonyDataServices(
packageNames, userId));
}
}
@Override
public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) {
enforceSystemOrPhoneCaller("grantDefaultPermissionsToActiveLuiApp");
synchronized (mPackages) {
final long identity = Binder.clearCallingIdentity();
try {
mDefaultPermissionPolicy.grantDefaultPermissionsToActiveLuiApp(
packageName, userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@Override
public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId) {
enforceSystemOrPhoneCaller("revokeDefaultPermissionsFromLuiApps");
synchronized (mPackages) {
final long identity = Binder.clearCallingIdentity();
try {
mDefaultPermissionPolicy.revokeDefaultPermissionsFromLuiApps(packageNames, userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
void forEachPackage(Consumer<PackageParser.Package> actionLocked) {
synchronized (mPackages) {
int numPackages = mPackages.size();
for (int i = 0; i < numPackages; i++) {
actionLocked.accept(mPackages.valueAt(i));
}
}
}
private static void enforceSystemOrPhoneCaller(String tag) {
int callingUid = Binder.getCallingUid();
if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) {
throw new SecurityException(
"Cannot call " + tag + " from UID " + callingUid);
}
}
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<PackageParser.Package> getPackages() {
synchronized (mPackages) {
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(PackageParser.Package pkg) {
return getOrCreateCompilerPackageStats(pkg.packageName);
}
public CompilerStats.PackageStats getOrCreateCompilerPackageStats(String pkgName) {
return mCompilerStats.getOrCreatePackageStats(pkgName);
}
public void deleteCompilerPackageStats(String pkgName) {
mCompilerStats.deletePackageStats(pkgName);
}
@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 (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (filterAppAccessLPr(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 = getAppOpPermissionPackages(appOpPermission);
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 (sUserManager.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userId)
|| sUserManager.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 (mPackages) {
return mInstantAppRegistry.getInstantAppAndroidIdLPw(packageName, userId);
}
}
boolean canHaveOatDir(String packageName) {
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
if (p == null) {
return false;
}
return p.canHaveOatDir();
}
}
private String getOatDir(PackageParser.Package pkg) {
if (!pkg.canHaveOatDir()) {
return null;
}
File codePath = new File(pkg.codePath);
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 PackageParser.Package pkg;
synchronized (mPackages) {
pkg = mPackages.get(packageName);
}
instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
codePaths = pkg.getAllCodePaths();
oatDir = getOatDir(pkg);
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 (mPackages) {
for (PackageParser.Package pkg : mPackages.values()) {
PackageSetting ps = mSettings.mPackages.get(pkg.packageName);
if (ps == null) {
continue;
}
PackageDexUsage.PackageUseInfo packageUseInfo =
getDexManager().getPackageUseInfoOrDefault(pkg.packageName);
if (PackageManagerServiceUtils
.isUnusedSinceTimeInMillis(ps.firstInstallTime, currentTimeInMillis,
downgradeTimeThresholdMillis, packageUseInfo,
pkg.getLatestPackageUseTimeInMills(),
pkg.getLatestForegroundPackageUseTimeInMills())) {
unusedPackages.add(pkg.packageName);
}
}
}
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(mPackages) {
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(mPackages) {
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.broadcastIntent(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);
}
}
static class ActiveInstallSession {
private final String mPackageName;
private final File mStagedDir;
private final IPackageInstallObserver2 mObserver;
private final PackageInstaller.SessionParams mSessionParams;
private final String mInstallerPackageName;
private final int mInstallerUid;
private final UserHandle mUser;
private final SigningDetails mSigningDetails;
ActiveInstallSession(String packageName, File stagedDir, IPackageInstallObserver2 observer,
PackageInstaller.SessionParams sessionParams, String installerPackageName,
int installerUid, UserHandle user, SigningDetails signingDetails) {
mPackageName = packageName;
mStagedDir = stagedDir;
mObserver = observer;
mSessionParams = sessionParams;
mInstallerPackageName = installerPackageName;
mInstallerUid = installerUid;
mUser = user;
mSigningDetails = signingDetails;
}
public String getPackageName() {
return mPackageName;
}
public File getStagedDir() {
return mStagedDir;
}
public IPackageInstallObserver2 getObserver() {
return mObserver;
}
public PackageInstaller.SessionParams getSessionParams() {
return mSessionParams;
}
public String getInstallerPackageName() {
return mInstallerPackageName;
}
public int getInstallerUid() {
return mInstallerUid;
}
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);
void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted,
boolean includeStopped, int appId, int[] userIds, int[] instantUserIds);
void notifyPackageAdded(String packageName, int uid);
void notifyPackageChanged(String packageName, int uid);
void notifyPackageRemoved(String packageName, int uid);
}