/*
|
* 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 android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
|
|
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 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.CollectionUtils;
|
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.IntPair;
|
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.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.policy.PermissionPolicyInternal;
|
import com.android.server.security.VerityUtils;
|
import com.android.server.storage.DeviceStorageMonitorInternal;
|
import com.android.server.wm.ActivityTaskManagerInternal;
|
|
// AW:add for BOOTEVENT
|
import com.aw.server.AwSystemServer;
|
|
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";
|
|
private static final String OEM_OVERLAY_DIR = "/oem/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<>();
|
|
/*AW_code;powerlaunchcall background dex2oat optimize status;jiangbin;191012*/
|
private boolean mHaveLaunchedBdoTask = false;
|
private boolean mNeedStopLaunchedBdo = false;
|
public static final int LAUNCH_BDO_DELAY_TIME = 20000;//ms
|
/*end*/
|
|
|
@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();
|
|
private final Map<String, Pair<PackageInstalledInfo, IPackageInstallObserver2>>
|
mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>());
|
|
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.DefaultDialerProvider mDefaultDialerProvider;
|
|
@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;
|
|
// 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 DEFERRED_NO_KILL_POST_DELETE = 23;
|
static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24;
|
|
static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
|
static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
|
|
static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds
|
|
private static final long BROADCAST_DELAY_DURING_STARTUP = 10 * 1000L; // 10 seconds (in millis)
|
private static final long BROADCAST_DELAY = 1 * 1000L; // 1 second (in millis)
|
|
// When the service constructor finished plus a delay (used for broadcast delay computation)
|
private long mServiceStartWithDelay;
|
|
private static final long DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD =
|
2 * 60 * 60 * 1000L; /* two hours */
|
|
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();
|
|
// AW:add for BOOTEVENT
|
private static AwSystemServer sAwSystemServerIns = AwSystemServer.getInstance();
|
|
|
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
|
: args.whitelistedRestrictedPermissions;
|
|
// 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 DEFERRED_NO_KILL_POST_DELETE: {
|
synchronized (mInstallLock) {
|
InstallArgs args = (InstallArgs) msg.obj;
|
if (args != null) {
|
args.doPostDeleteLI(true);
|
}
|
}
|
} break;
|
case DEFERRED_NO_KILL_INSTALL_OBSERVER: {
|
String packageName = (String) msg.obj;
|
if (packageName != null) {
|
notifyInstallObserver(packageName);
|
}
|
} break;
|
case WRITE_SETTINGS: {
|
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
|
synchronized (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();
|
Intent rollbackTimeoutIntent = new Intent(
|
Intent.ACTION_CANCEL_ENABLE_ROLLBACK);
|
rollbackTimeoutIntent.putExtra(
|
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN,
|
enableRollbackToken);
|
rollbackTimeoutIntent.addFlags(
|
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
|
mContext.sendBroadcastAsUser(rollbackTimeoutIntent, UserHandle.SYSTEM,
|
android.Manifest.permission.PACKAGE_ROLLBACK_AGENT);
|
}
|
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) {
|
final boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED;
|
final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null;
|
|
if (succeeded) {
|
// Send the removed broadcasts
|
if (res.removedInfo != null) {
|
res.removedInfo.sendPackageRemovedBroadcasts(killApp);
|
}
|
|
// Whitelist any restricted permissions first as some may be runtime
|
// that the installer requested to be granted at install time.
|
if (whitelistedRestrictedPermissions != null
|
&& !whitelistedRestrictedPermissions.isEmpty()) {
|
mPermissionManager.setWhitelistedRestrictedPermissions(
|
res.pkg, res.newUsers, whitelistedRestrictedPermissions,
|
Process.myUid(), 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) {
|
if (grantPermissions || res.name.contains(SystemProperties.get("persist.app.grant.prms"))) {
|
final int callingUid = Binder.getCallingUid();
|
mPermissionManager.grantRequestedRuntimePermissions(
|
res.pkg, res.newUsers, grantedPermissions, callingUid,
|
mPermissionCallback);
|
}
|
|
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);
|
}
|
} else if (!ArrayUtils.isEmpty(res.libraryConsumers)) { // if static shared lib
|
for (int i = 0; i < res.libraryConsumers.size(); i++) {
|
PackageParser.Package pkg = res.libraryConsumers.get(i);
|
// send broadcast that all consumers of the static shared library have changed
|
sendPackageChangedBroadcast(pkg.packageName, false /*killFlag*/,
|
new ArrayList<>(Collections.singletonList(pkg.packageName)),
|
pkg.applicationInfo.uid);
|
}
|
}
|
|
// Work that needs to happen on first install within each user
|
if (firstUserIds != null && firstUserIds.length > 0) {
|
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
|
synchronized (mPackages) {
|
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.
|
updateDefaultHomeNotLocked(userId);
|
}
|
}
|
|
if (allNewUsers && !update) {
|
notifyPackageAdded(packageName, res.uid);
|
} else {
|
notifyPackageChanged(packageName, res.uid);
|
}
|
|
// Log current value of "unknown sources" setting
|
EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
|
getUnknownSourcesSettings());
|
|
// Remove the replaced package's older resources safely now
|
InstallArgs args = res.removedInfo != null ? res.removedInfo.args : null;
|
if (args != null) {
|
if (!killApp) {
|
// If we didn't kill the app, defer the deletion of code/resource files, since
|
// they may still be in use by the running application. This mitigates problems
|
// in cases where resources or code is loaded by a new Activity before
|
// ApplicationInfo changes have propagated to all application threads.
|
scheduleDeferredNoKillPostDelete(args);
|
} else {
|
synchronized (mInstallLock) {
|
args.doPostDeleteLI(true);
|
}
|
}
|
} else {
|
// Force a gc to clear up things. Ask for a background one, it's fine to go on
|
// and not block here.
|
VMRuntime.getRuntime().requestConcurrentGC();
|
}
|
|
// Notify DexManager that the package was installed for new users.
|
// The updated users should already be indexed and the package code paths
|
// should not change.
|
// Don't notify the manager for ephemeral apps as they are not expected to
|
// survive long enough to benefit of background optimizations.
|
for (int userId : firstUserIds) {
|
PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);
|
// There's a race currently where some install events may interleave with an uninstall.
|
// This can lead to package info being null (b/36642664).
|
if (info != null) {
|
mDexManager.notifyPackageInstalled(info, userId);
|
}
|
}
|
}
|
|
final boolean deferInstallObserver = succeeded && update && !killApp;
|
if (deferInstallObserver) {
|
scheduleDeferredNoKillInstallObserver(res, installObserver);
|
} else {
|
notifyInstallObserver(res, installObserver);
|
}
|
}
|
|
@Override
|
public void notifyPackagesReplacedReceived(String[] packages) {
|
final int callingUid = Binder.getCallingUid();
|
final int callingUserId = UserHandle.getUserId(callingUid);
|
|
for (String packageName : packages) {
|
PackageSetting setting = mSettings.mPackages.get(packageName);
|
if (setting != null && filterAppAccessLPr(setting, callingUid, callingUserId)) {
|
notifyInstallObserver(packageName);
|
}
|
}
|
}
|
|
private void notifyInstallObserver(String packageName) {
|
Pair<PackageInstalledInfo, IPackageInstallObserver2> pair =
|
mNoKillInstallObservers.remove(packageName);
|
|
if (pair != null) {
|
notifyInstallObserver(pair.first, pair.second);
|
}
|
}
|
|
private void notifyInstallObserver(PackageInstalledInfo info,
|
IPackageInstallObserver2 installObserver) {
|
if (installObserver != null) {
|
try {
|
Bundle extras = extrasForInstallResult(info);
|
installObserver.onPackageInstalled(info.name, info.returnCode,
|
info.returnMsg, extras);
|
} catch (RemoteException e) {
|
Slog.i(TAG, "Observer no longer exists.");
|
}
|
}
|
}
|
|
private void scheduleDeferredNoKillPostDelete(InstallArgs args) {
|
Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args);
|
mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_POST_DELETE_DELAY_MS);
|
}
|
|
private void scheduleDeferredNoKillInstallObserver(PackageInstalledInfo info,
|
IPackageInstallObserver2 observer) {
|
String packageName = info.pkg.packageName;
|
mNoKillInstallObservers.put(packageName, Pair.create(info, observer));
|
Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_INSTALL_OBSERVER, packageName);
|
mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS);
|
}
|
|
/**
|
* Gets the type of the external storage a package is installed on.
|
* @param packageVolume The storage volume of the package.
|
* @param packageIsExternal true if the package is currently installed on
|
* external/removable/unprotected storage.
|
* @return {@link 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());
|
|
// AW:BOOTEVENT
|
sAwSystemServerIns.logBootEvent("Android:PackageManagerService_Start");
|
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,
|
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);
|
|
mApexManager = new ApexManager(context);
|
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);
|
|
// AW:BOOTEVENT
|
sAwSystemServerIns.logBootEvent("Android:PMS_scan_START");
|
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;
|
|
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);
|
scanDirTracedLI(new File(OEM_OVERLAY_DIR),
|
mDefParseFlags
|
| PackageParser.PARSE_IS_SYSTEM_DIR,
|
scanFlags
|
| SCAN_AS_SYSTEM
|
| SCAN_AS_OEM,
|
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);
|
} else {
|
// We're expecting that the system app should remain disabled, but add
|
// it to expecting better to recover in case the data version cannot
|
// be scanned.
|
mExpectingBetter.put(disabledPs.name, disabledPs.codePath);
|
}
|
}
|
}
|
}
|
|
//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 (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
|
final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
|
final PackageParser.Package pkg = mPackages.get(packageName);
|
final String msg;
|
|
// remove from the disabled system list; do this first so any future
|
// scans of this package are performed without this state
|
mSettings.removeDisabledSystemPackageLPw(packageName);
|
|
if (pkg == null) {
|
// should have found an update, but, we didn't; remove everything
|
msg = "Updated system package " + packageName
|
+ " no longer exists; removing its data";
|
// Actual deletion of code and data will be handled by later
|
// reconciliation step
|
} else {
|
// found an update; revoke system privileges
|
msg = "Updated system package " + packageName
|
+ " no longer exists; rescanning package on data";
|
|
// NOTE: We don't do anything special if a stub is removed from the
|
// system image. But, if we were [like removing the uncompressed
|
// version from the /data partition], this is where it'd be done.
|
|
// remove the package from the system and re-scan it without any
|
// special privileges
|
removePackageLI(pkg, true);
|
try {
|
final File codePath = new File(pkg.applicationInfo.getCodePath());
|
scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
|
} catch (PackageManagerException e) {
|
Slog.e(TAG, "Failed to parse updated, ex-system package: "
|
+ e.getMessage());
|
}
|
}
|
|
// one final check. if we still have a package setting [ie. it was
|
// previously scanned and known to the system], but, we don't have
|
// a package [ie. there was an error scanning it from the /data
|
// partition], completely remove the package data.
|
final PackageSetting ps = mSettings.mPackages.get(packageName);
|
if (ps != null && mPackages.get(packageName) == null) {
|
removePackageDataLIF(ps, null, null, 0, false);
|
|
}
|
logCriticalInfo(Log.WARN, msg);
|
}
|
|
/*
|
* Make sure all system apps that we expected to appear on
|
* the userdata partition actually showed up. If they never
|
* appeared, crawl back and revive the system version.
|
*/
|
for (int i = 0; i < mExpectingBetter.size(); i++) {
|
final String packageName = mExpectingBetter.keyAt(i);
|
if (!mPackages.containsKey(packageName)) {
|
final File scanFile = mExpectingBetter.valueAt(i);
|
|
logCriticalInfo(Log.WARN, "Expected better " + packageName
|
+ " but never showed up; reverting to system");
|
|
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.
|
installSystemStubPackages(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 = getDeviceConfiguratorPackageName();
|
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());
|
// AW:BOOTEVENT
|
sAwSystemServerIns.logBootEvent("Android:PMS_scan_END");
|
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,
|
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
|
| 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());
|
|
// AW:BOOTEVENT
|
sAwSystemServerIns.logBootEvent("Android:PMS_READY");
|
|
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);
|
}
|
}
|
|
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 installSystemStubPackages(@NonNull List<String> systemStubPackageNames,
|
@ScanFlags int scanFlags) {
|
for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
|
final String packageName = systemStubPackageNames.get(i);
|
// skip if the system package is already disabled
|
if (mSettings.isDisabledSystemPackageLPr(packageName)) {
|
systemStubPackageNames.remove(i);
|
continue;
|
}
|
// skip if the package isn't installed (?!); this should never happen
|
final PackageParser.Package pkg = mPackages.get(packageName);
|
if (pkg == null) {
|
systemStubPackageNames.remove(i);
|
continue;
|
}
|
// skip if the package has been disabled by the user
|
final PackageSetting ps = mSettings.mPackages.get(packageName);
|
if (ps != null) {
|
final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM);
|
if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
|
systemStubPackageNames.remove(i);
|
continue;
|
}
|
}
|
|
// install the package to replace the stub on /system
|
try {
|
installStubPackageLI(pkg, 0, scanFlags);
|
ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
|
UserHandle.USER_SYSTEM, "android");
|
systemStubPackageNames.remove(i);
|
} catch (PackageManagerException e) {
|
Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage());
|
}
|
|
// any failed attempt to install the package will be cleaned up later
|
}
|
|
// disable any stub still left; these failed to install the full application
|
for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
|
final String pkgName = systemStubPackageNames.get(i);
|
final PackageSetting ps = mSettings.mPackages.get(pkgName);
|
ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
|
UserHandle.USER_SYSTEM, "android");
|
logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName);
|
}
|
}
|
|
/**
|
* Extract, install and enable a stub package.
|
* <p>If the compressed file can not be extracted / installed for any reason, the stub
|
* APK will be installed and the package will be disabled. To recover from this situation,
|
* the user will need to go into system settings and re-enable the package.
|
*/
|
private boolean enableCompressedPackage(PackageParser.Package stubPkg) {
|
final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
|
| PackageParser.PARSE_ENFORCE_CODE;
|
synchronized (mInstallLock) {
|
final PackageParser.Package pkg;
|
try (PackageFreezer freezer =
|
freezePackage(stubPkg.packageName, "setEnabledSetting")) {
|
pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/);
|
synchronized (mPackages) {
|
prepareAppDataAfterInstallLIF(pkg);
|
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 very wrong; roll back to the stub and disable the package
|
try (PackageFreezer freezer =
|
freezePackage(stubPkg.packageName, "setEnabledSetting")) {
|
synchronized (mPackages) {
|
// NOTE: Ensure the system package is enabled; even for a compressed stub.
|
// If we don't, installing the system package fails during scan
|
enableSystemPackageLPw(stubPkg);
|
}
|
installPackageFromSystemLIF(stubPkg.codePath,
|
null /*allUserHandles*/, null /*origUserHandles*/,
|
null /*origPermissionsState*/, true /*writeSettings*/);
|
} catch (PackageManagerException pme) {
|
// Serious WTF; we have to be able to install the stub
|
Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.packageName, pme);
|
} finally {
|
// Disable the package; the stub by itself is not runnable
|
synchronized (mPackages) {
|
final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.packageName);
|
if (stubPs != null) {
|
stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED,
|
UserHandle.USER_SYSTEM, "android");
|
}
|
mSettings.writeLPr();
|
}
|
}
|
return false;
|
}
|
clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
|
| FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
|
mDexManager.notifyPackageUpdated(pkg.packageName,
|
pkg.baseCodePath, pkg.splitCodePaths);
|
}
|
return true;
|
}
|
|
private PackageParser.Package installStubPackageLI(PackageParser.Package stubPkg,
|
@ParseFlags int parseFlags, @ScanFlags int scanFlags)
|
throws PackageManagerException {
|
if (DEBUG_COMPRESSION) {
|
Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.packageName);
|
}
|
// uncompress the binary to its eventual destination on /data
|
final File scanFile = decompressPackage(stubPkg.packageName, stubPkg.codePath);
|
if (scanFile == null) {
|
throw new PackageManagerException("Unable to decompress stub at " + stubPkg.codePath);
|
}
|
synchronized (mPackages) {
|
mSettings.disableSystemPackageLPw(stubPkg.packageName, true /*replaced*/);
|
}
|
removePackageLI(stubPkg, true /*chatty*/);
|
try {
|
return scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
|
} catch (PackageManagerException e) {
|
Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.packageName, e);
|
// Remove the failed install
|
removeCodePathLI(scanFile);
|
throw e;
|
}
|
}
|
|
/**
|
* Decompresses the given package on the system image onto
|
* the /data partition.
|
* @return The directory the package was decompressed into. Otherwise, {@code null}.
|
*/
|
private File decompressPackage(String packageName, String codePath) {
|
final File[] compressedFiles = getCompressedFiles(codePath);
|
if (compressedFiles == null || compressedFiles.length == 0) {
|
if (DEBUG_COMPRESSION) {
|
Slog.i(TAG, "No files to decompress: " + codePath);
|
}
|
return null;
|
}
|
final File dstCodePath =
|
getNextCodePath(Environment.getDataAppDirectory(null), packageName);
|
int ret = PackageManager.INSTALL_SUCCEEDED;
|
try {
|
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: " + packageName
|
+ ", file: " + dstFileName);
|
break;
|
}
|
}
|
} catch (ErrnoException e) {
|
logCriticalInfo(Log.ERROR, "Failed to decompress"
|
+ "; pkg: " + packageName
|
+ ", err: " + e.errno);
|
}
|
if (ret == PackageManager.INSTALL_SUCCEEDED) {
|
final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
|
NativeLibraryHelper.Handle handle = null;
|
try {
|
handle = NativeLibraryHelper.Handle.create(dstCodePath);
|
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
|
null /*abiOverride*/);
|
} catch (IOException e) {
|
logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
|
+ "; pkg: " + packageName);
|
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
|
} finally {
|
IoUtils.closeQuietly(handle);
|
}
|
}
|
if (ret != PackageManager.INSTALL_SUCCEEDED) {
|
if (!dstCodePath.exists()) {
|
return null;
|
}
|
removeCodePathLI(dstCodePath);
|
return null;
|
}
|
|
return dstCodePath;
|
}
|
|
@GuardedBy("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();
|
if (checkPermission(Manifest.permission.INSTALL_PACKAGES,
|
rInfo.activityInfo.packageName, 0) == PERMISSION_GRANTED || Build.IS_ENG) {
|
continue;
|
}
|
iter.remove();
|
}
|
if (matches.size() == 0) {
|
return null;
|
} else if (matches.size() == 1) {
|
return (ActivityInfo) matches.get(0).getComponentInfo();
|
} else {
|
throw new RuntimeException(
|
"There must be at most one ephemeral installer; found " + matches);
|
}
|
}
|
|
private @Nullable ComponentName getInstantAppResolverSettingsLPr(
|
@NonNull ComponentName resolver) {
|
final Intent intent = new Intent(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS)
|
.addCategory(Intent.CATEGORY_DEFAULT)
|
.setPackage(resolver.getPackageName());
|
final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
|
List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
|
UserHandle.USER_SYSTEM);
|
if (matches.isEmpty()) {
|
return null;
|
}
|
return matches.get(0).getComponentInfo().getComponentName();
|
}
|
|
@GuardedBy("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
|
Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions)
|
? Collections.emptySet() : permissionsState.getPermissions(userId);
|
if (state.instantApp) {
|
permissions = new ArraySet<>(permissions);
|
permissions.removeIf(permissionName -> {
|
BasePermission permission = mPermissionManager.getPermissionTEMP(
|
permissionName);
|
if (permission == null) {
|
return true;
|
}
|
if (!permission.isInstant()) {
|
EventLog.writeEvent(0x534e4554, "140256621", UserHandle.getUid(userId,
|
ps.appId), permissionName);
|
return true;
|
}
|
return false;
|
});
|
}
|
|
PackageInfo packageInfo = 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) {
|
// Instant app filtering for APEX modules is ignored
|
if ((flags & MATCH_APEX) != 0) {
|
return mApexManager.getPackageInfo(packageName,
|
ApexManager.MATCH_FACTORY_PACKAGE);
|
}
|
final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
|
if (ps != null) {
|
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
|
return null;
|
}
|
if (filterAppAccessLPr(ps, filterCallingUid, userId)) {
|
return null;
|
}
|
return generatePackageInfo(ps, flags, userId);
|
}
|
}
|
|
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.getPackageInfo(packageName, ApexManager.MATCH_ACTIVE_PACKAGE);
|
}
|
}
|
return null;
|
}
|
|
private boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
|
if (isComponentVisibleToInstantApp(component, TYPE_ACTIVITY)) {
|
return true;
|
}
|
if (isComponentVisibleToInstantApp(component, TYPE_SERVICE)) {
|
return true;
|
}
|
if (isComponentVisibleToInstantApp(component, TYPE_PROVIDER)) {
|
return true;
|
}
|
return false;
|
}
|
|
private boolean isComponentVisibleToInstantApp(
|
@Nullable ComponentName component, @ComponentType int type) {
|
if (type == TYPE_ACTIVITY) {
|
final 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);
|
PackageParser.Package pkg = null;
|
final int N = packageNames == null ? 0 : packageNames.length;
|
for (int i = 0; pkg == null && i < N; i++) {
|
pkg = mPackages.get(packageNames[i]);
|
}
|
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);
|
}
|
|
final PackageParser.Package pkg;
|
|
synchronized (mPackages) {
|
final PackageSetting packageSetting = mSettings.mPackages.get(packageName);
|
if (packageSetting == null) {
|
Slog.w(TAG, "Unknown package: " + packageName);
|
return null;
|
}
|
|
pkg = packageSetting.pkg;
|
|
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(
|
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);
|
|
if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) {
|
return false;
|
}
|
|
List<String> permissions = getWhitelistedRestrictedPermissions(packageName,
|
whitelistFlags, userId);
|
if (permissions == null) {
|
permissions = new ArrayList<>(1);
|
}
|
if (permissions.indexOf(permission) < 0) {
|
permissions.add(permission);
|
return setWhitelistedRestrictedPermissions(packageName, permissions,
|
whitelistFlags, userId);
|
}
|
return false;
|
}
|
|
private boolean checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(
|
@NonNull String permission) {
|
synchronized (mPackages) {
|
final BasePermission bp = mPermissionManager.getPermissionTEMP(permission);
|
if (bp == null) {
|
Slog.w(TAG, "No such permissions: " + permission);
|
return false;
|
}
|
if (bp.isHardOrSoftRestricted() && bp.isImmutablyRestricted()
|
&& mContext.checkCallingOrSelfPermission(
|
Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS)
|
!= PackageManager.PERMISSION_GRANTED) {
|
throw new SecurityException("Cannot modify whitelisting of an immutably "
|
+ "restricted permission: " + permission);
|
}
|
return true;
|
}
|
}
|
|
@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);
|
|
if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) {
|
return false;
|
}
|
|
final List<String> permissions = getWhitelistedRestrictedPermissions(packageName,
|
whitelistFlags, userId);
|
if (permissions != null && permissions.remove(permission)) {
|
return setWhitelistedRestrictedPermissions(packageName, permissions,
|
whitelistFlags, userId);
|
}
|
return false;
|
}
|
|
private boolean 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);
|
}
|
|
final PackageParser.Package pkg;
|
|
synchronized (mPackages) {
|
final PackageSetting packageSetting = mSettings.mPackages.get(packageName);
|
if (packageSetting == null) {
|
Slog.w(TAG, "Unknown package: " + packageName);
|
return false;
|
}
|
|
pkg = packageSetting.pkg;
|
|
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 true;
|
}
|
} 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 false;
|
}
|
}
|
|
final long identity = Binder.clearCallingIdentity();
|
try {
|
mPermissionManager.setWhitelistedRestrictedPermissions(pkg,
|
new int[]{userId}, permissions, Process.myUid(), whitelistFlag,
|
mPermissionCallback);
|
} finally {
|
Binder.restoreCallingIdentity(identity);
|
}
|
|
return true;
|
}
|
|
@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;
|
}
|
}
|
|
/**
|
* <em>IMPORTANT:</em> Not all packages returned by this method may be known
|
* to the system. There are two conditions in which this may occur:
|
* <ol>
|
* <li>The package is on adoptable storage and the device has been removed</li>
|
* <li>The package is being removed and the internal structures are partially updated</li>
|
* </ol>
|
* The second is an artifact of the current data structures and should be fixed. See
|
* b/111075456 for one such instance.
|
*/
|
@Override
|
public String[] getPackagesForUid(int uid) {
|
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
|
findPreferredActivityNotLocked(
|
intent, resolvedType, flags, query, 0, false, true, false, userId);
|
// Add the new activity as the last chosen for this filter
|
addPreferredActivityInternal(filter, match, null, activity, false, userId,
|
"Setting last chosen");
|
}
|
|
@Override
|
public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags) {
|
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
|
return null;
|
}
|
final int userId = UserHandle.getCallingUserId();
|
if (DEBUG_PREFERRED) Log.v(TAG, "Querying last chosen activity for " + intent);
|
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
|
userId);
|
return findPreferredActivityNotLocked(
|
intent, resolvedType, flags, query, 0, false, false, false, userId);
|
}
|
|
/**
|
* Returns whether or not instant apps have been disabled remotely.
|
*/
|
private boolean areWebInstantAppsDisabled(int userId) {
|
return mWebInstantAppsDisabled.get(userId);
|
}
|
|
private boolean isInstantAppResolutionAllowed(
|
Intent intent, List<ResolveInfo> resolvedActivities, int userId,
|
boolean skipPackageCheck) {
|
if (mInstantAppResolverConnection == null) {
|
return false;
|
}
|
if (mInstantAppInstallerActivity == null) {
|
return false;
|
}
|
if (intent.getComponent() != null) {
|
return false;
|
}
|
if ((intent.getFlags() & Intent.FLAG_IGNORE_EPHEMERAL) != 0) {
|
return false;
|
}
|
if (!skipPackageCheck && intent.getPackage() != null) {
|
return false;
|
}
|
if (!intent.isWebIntent()) {
|
// for non web intents, we should not resolve externally if an app already exists to
|
// handle it or if the caller didn't explicitly request it.
|
if ((resolvedActivities != null && resolvedActivities.size() != 0)
|
|| (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) {
|
return false;
|
}
|
} else {
|
if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) {
|
return false;
|
} else if (areWebInstantAppsDisabled(userId)) {
|
return false;
|
}
|
}
|
// Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
|
// Or if there's already an ephemeral app installed that handles the action
|
synchronized (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 = findPreferredActivityNotLocked(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
|
/** <b>must not hold {@link #mPackages}</b> */
|
ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
|
List<ResolveInfo> query, int priority, boolean always,
|
boolean removeMatches, boolean debug, int userId) {
|
if (Thread.holdsLock(mPackages)) {
|
Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
|
+ " is holding mPackages", new Throwable());
|
}
|
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;
|
final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 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) {
|
if (listFactory) {
|
list.addAll(mApexManager.getFactoryPackages());
|
} else {
|
list.addAll(mApexManager.getActivePackages());
|
}
|
if (listUninstalled) {
|
list.addAll(mApexManager.getInactivePackages());
|
}
|
}
|
return new ParceledListSlice<>(list);
|
}
|
}
|
|
private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageSetting ps,
|
String[] permissions, boolean[] tmp, int flags, int userId) {
|
int numMatch = 0;
|
for (int i=0; i<permissions.length; i++) {
|
final String permission = permissions[i];
|
if (checkPermission(permission, ps.name, userId) == PERMISSION_GRANTED) {
|
tmp[i] = true;
|
numMatch++;
|
} else {
|
tmp[i] = false;
|
}
|
}
|
if (numMatch == 0) {
|
return;
|
}
|
final PackageInfo pi = generatePackageInfo(ps, flags, userId);
|
|
// The above might return null in cases of uninstalled apps or install-state
|
// skew across users/profiles.
|
if (pi != null) {
|
if ((flags&PackageManager.GET_PERMISSIONS) == 0) {
|
if (numMatch == permissions.length) {
|
pi.requestedPermissions = permissions;
|
} else {
|
pi.requestedPermissions = new String[numMatch];
|
numMatch = 0;
|
for (int i=0; i<permissions.length; i++) {
|
if (tmp[i]) {
|
pi.requestedPermissions[numMatch] = permissions[i];
|
numMatch++;
|
}
|
}
|
}
|
}
|
list.add(pi);
|
}
|
}
|
|
@Override
|
public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
|
String[] permissions, int flags, int userId) {
|
if (!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;
|
}
|
|
// AW:BOOTEVENT
|
sAwSystemServerIns.logBootEvent("Android:PMS_scan_data:" + scanDir.getPath().toString());
|
|
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) {
|
boolean appIdCreated = false;
|
try {
|
final String pkgName = scanResult.pkgSetting.name;
|
final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked(
|
new ReconcileRequest(
|
Collections.singletonMap(pkgName, scanResult),
|
mSharedLibraries,
|
mPackages,
|
Collections.singletonMap(
|
pkgName, getSettingsVersionForPackage(pkg)),
|
Collections.singletonMap(pkgName,
|
getSharedLibLatestVersionSetting(scanResult))),
|
mSettings.mKeySetManagerService);
|
appIdCreated = optimisticallyRegisterAppId(scanResult);
|
commitReconciledScanResultLocked(reconcileResult.get(pkgName));
|
} catch (PackageManagerException e) {
|
if (appIdCreated) {
|
cleanUpAppIdCreation(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");
|
/*AW_code;powerlaunchcall background dex2oat optimize;jiangbin;191012*/
|
if(packageNames.contains("PowerLaunchCall")) {
|
|
if(mHaveLaunchedBdoTask) {
|
mNeedStopLaunchedBdo = true;
|
Slog.i(TAG, "stop cur launched bdo");
|
return false;
|
}
|
|
String targetpackagename = packageNames.get(1);
|
if(targetpackagename == null) {
|
return false;
|
}
|
boolean hitpackage = false;
|
for (PackageParser.Package p : mPackages.values()) {
|
|
if (targetpackagename.equals(p.packageName)) {
|
if(PackageDexOptimizer.canOptimizePackage(p)) {
|
hitpackage = true;
|
break;
|
}else {
|
return false;
|
}
|
|
}
|
}
|
if(hitpackage && !mHaveLaunchedBdoTask) {
|
|
PackageManagerService pm = this;
|
// mHandler.postDelayed(new Runnable() { //reserve info
|
mHaveLaunchedBdoTask = true;
|
mNeedStopLaunchedBdo = false;
|
new Thread(new Runnable() {
|
@Override
|
public void run() {
|
|
try {
|
Thread.sleep(LAUNCH_BDO_DELAY_TIME);
|
} catch (InterruptedException e) {
|
e.printStackTrace();
|
}
|
|
if(!mNeedStopLaunchedBdo
|
&& !SystemProperties.getBoolean("pm.dexopt.disable_bg_dexopt" /* key */,false /* default */)) {
|
|
Slog.i(TAG, "launched bdo start " + targetpackagename);
|
final long identity = Binder.clearCallingIdentity();
|
try {
|
BackgroundDexOptService.runIdleOptimizationsNow(pm, mContext, packageNames);
|
} finally {
|
Binder.restoreCallingIdentity(identity);
|
}
|
} else {
|
Slog.i(TAG, "launched bdo stoped");
|
}
|
// mHandler.removeCallbacks(this);
|
mHaveLaunchedBdoTask = false;
|
|
}
|
}).start();
|
}
|
return hitpackage;
|
} else {
|
|
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));
|
}
|
mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId);
|
}
|
}
|
|
private void destroyAppProfilesLIF(PackageParser.Package pkg) {
|
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 by registering
|
* the app ID required for reconcile.
|
* @return {@code true} if a new app ID was registered and will need to be cleaned up on
|
* failure.
|
*/
|
private boolean optimisticallyRegisterAppId(@NonNull ScanResult result)
|
throws PackageManagerException {
|
if (!result.existingSettingCopied) {
|
// THROWS: when we can't allocate a user id. add call to check if there's
|
// enough space to ensure we won't throw; otherwise, don't modify state
|
return mSettings.registerAppIdLPw(result.pkgSetting);
|
}
|
return false;
|
}
|
|
/**
|
* Reverts any app ID creation that were made by
|
* {@link #optimisticallyRegisterAppId(ScanResult)}. Note: this is only necessary if the
|
* referenced method returned true.
|
*/
|
private void cleanUpAppIdCreation(@NonNull ScanResult result) {
|
// iff we've acquired an app ID for a new package setting, remove it so that it can be
|
// acquired by another request.
|
if (result.pkgSetting.appId > 0) {
|
mSettings.removeAppIdLPw(result.pkgSetting.appId);
|
}
|
}
|
|
/**
|
* Commits the package scan and modifies system state.
|
* <p><em>WARNING:</em> The method may throw an excpetion in the middle
|
* of committing the package, leaving the system in an inconsistent state.
|
* This needs to be fixed so, once we get to this point, no errors are
|
* possible and the system is not left in an inconsistent state.
|
*/
|
@GuardedBy({"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 (request.pkgSetting != null && request.pkgSetting.sharedUser != null
|
&& request.pkgSetting.sharedUser != result.pkgSetting.sharedUser) {
|
// shared user changed, remove from old shared user
|
request.pkgSetting.sharedUser.removePackage(request.pkgSetting);
|
}
|
if (result.existingSettingCopied) {
|
pkgSetting = request.pkgSetting;
|
pkgSetting.updateFrom(result.pkgSetting);
|
pkg.mExtras = 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);
|
}
|
}
|
if (pkgSetting.sharedUser != null) {
|
pkgSetting.sharedUser.addPackage(pkgSetting);
|
}
|
// 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;
|
// clear protected broadcasts
|
pkg.protectedBroadcasts = null;
|
// 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) {
|
// 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");
|
}
|
|
// Check that there is an APEX package with the same name only during install/first boot
|
// after OTA.
|
final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
|
final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
|
if ((isUserInstall || isFirstBootOrUpgrade)
|
&& mApexManager.isApexPackage(pkg.packageName)) {
|
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
|
pkg.packageName + " is an APEX package and can't be installed as an APK.");
|
}
|
|
// Make sure we're not adding any bogus keyset info
|
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
|
ksms.assertScannedPackageValid(pkg);
|
|
synchronized (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.");
|
}
|
}
|
}
|
}
|
|
// 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 (reconciledPkg.installResult != null) {
|
reconciledPkg.installResult.libraryConsumers = clientLibPkgs;
|
}
|
|
if ((scanFlags & SCAN_BOOTING) != 0) {
|
// No apps can run during boot scan, so they don't need to be frozen
|
} else if ((scanFlags & SCAN_DONT_KILL_APP) != 0) {
|
// Caller asked to not kill app, so it's probably not frozen
|
} else if ((scanFlags & SCAN_IGNORE_FROZEN) != 0) {
|
// Caller asked us to ignore frozen check for some reason; they
|
// probably didn't know the package name
|
} else {
|
// We're doing major surgery on this package, so it better be frozen
|
// right now to keep it from launching
|
checkPackageFrozen(pkgName);
|
}
|
|
// Also need to kill any apps that are dependent on the library.
|
if (clientLibPkgs != null) {
|
for (int i=0; i<clientLibPkgs.size(); i++) {
|
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);
|
}
|
|
// If a permission has had its defining app changed, or it has had its protection
|
// upgraded, we need to revoke apps that hold it
|
final List<String> permissionsWithChangedDefinition;
|
// Don't allow ephemeral applications to define new permissions.
|
if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
|
permissionsWithChangedDefinition = null;
|
Slog.w(TAG, "Permissions from package " + pkg.packageName
|
+ " ignored: instant apps cannot define new permissions.");
|
} else {
|
permissionsWithChangedDefinition =
|
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));
|
}
|
}
|
}
|
|
boolean hasOldPkg = oldPkg != null;
|
boolean hasPermissionDefinitionChanges =
|
!CollectionUtils.isEmpty(permissionsWithChangedDefinition);
|
if (hasOldPkg || hasPermissionDefinitionChanges) {
|
// We need to call revokeRuntimePermissionsIfGroupChanged async as permission
|
// revoke callbacks from this method might need to kill apps which need the
|
// mPackages lock on a different thread. This would dead lock.
|
//
|
// Hence create a copy of all package names and pass it into
|
// revokeRuntimePermissionsIfGroupChanged. Only for those permissions might get
|
// revoked. If a new package is added before the async code runs the permission
|
// won't be granted yet, hence new packages are no problem.
|
final ArrayList<String> allPackageNames = new ArrayList<>(mPackages.keySet());
|
|
AsyncTask.execute(() -> {
|
if (hasOldPkg) {
|
mPermissionManager.revokeRuntimePermissionsIfGroupChanged(pkg, oldPkg,
|
allPackageNames, mPermissionCallback);
|
}
|
if (hasPermissionDefinitionChanges) {
|
mPermissionManager.revokeRuntimePermissionsIfPermissionDefinitionChanged(
|
permissionsWithChangedDefinition, 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,
|
PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
|
PackageManager.INSTALL_REASON_DEVICE_SETUP,
|
null);
|
return true;
|
}
|
|
// uninstall the app from installed state
|
deletePackageVersioned(
|
new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
|
new LegacyPackageDeleteObserver(null).getBinder(),
|
userId,
|
PackageManager.DELETE_SYSTEM_APP);
|
return true;
|
} finally {
|
Binder.restoreCallingIdentity(callingId);
|
}
|
}
|
|
private void sendApplicationHiddenForUser(String packageName, PackageSetting pkgSetting,
|
int userId) {
|
final PackageRemovedInfo info = new PackageRemovedInfo(this);
|
info.removedPackage = packageName;
|
info.installerPackageName = pkgSetting.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, List<String> whiteListedPermissions) {
|
return installExistingPackageAsUser(packageName, userId, installFlags, installReason,
|
whiteListedPermissions, null);
|
}
|
|
int installExistingPackageAsUser(@Nullable String packageName, @UserIdInt int userId,
|
@PackageManager.InstallFlags int installFlags,
|
@PackageManager.InstallReason int installReason,
|
@Nullable List<String> whiteListedPermissions, @Nullable IntentSender intentSender) {
|
if (DEBUG_INSTALL) {
|
Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId
|
+ " installFlags=" + installFlags + " installReason=" + installReason
|
+ " whiteListedPermissions=" + whiteListedPermissions);
|
}
|
|
final int callingUid = Binder.getCallingUid();
|
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
|
!= PackageManager.PERMISSION_GRANTED
|
&& mContext.checkCallingOrSelfPermission(
|
android.Manifest.permission.INSTALL_EXISTING_PACKAGES)
|
!= PackageManager.PERMISSION_GRANTED) {
|
throw new SecurityException("Neither user " + callingUid + " nor current process has "
|
+ android.Manifest.permission.INSTALL_PACKAGES + ".");
|
}
|
PackageSetting pkgSetting;
|
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
|
true /* requireFullPermission */, true /* checkShell */,
|
"installExistingPackage for user " + userId);
|
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
|
return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
|
}
|
|
long callingId = Binder.clearCallingIdentity();
|
try {
|
boolean installed = false;
|
final boolean instantApp =
|
(installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
|
final boolean fullApp =
|
(installFlags & PackageManager.INSTALL_FULL_APP) != 0;
|
|
// writer
|
synchronized (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 ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS)
|
!= 0 && pkgSetting.pkg != null) {
|
whiteListedPermissions = pkgSetting.pkg.requestedPermissions;
|
}
|
setWhitelistedRestrictedPermissions(packageName, whiteListedPermissions,
|
PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER, userId);
|
|
if (pkgSetting.pkg != null) {
|
synchronized (mInstallLock) {
|
// We don't need to freeze for a brand new install
|
prepareAppDataAfterInstallLIF(pkgSetting.pkg);
|
}
|
}
|
sendPackageAddedForUser(packageName, pkgSetting, userId);
|
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]);
|
}
|
|
private void enforceCanSetPackagesSuspendedAsUser(String callingPackage, int callingUid,
|
int userId, String callingMethod) {
|
if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID) {
|
return;
|
}
|
|
final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId);
|
if (ownerPackage != null) {
|
final int ownerUid = getPackageUid(ownerPackage, 0, userId);
|
if (ownerUid == callingUid) {
|
return;
|
}
|
throw new UnsupportedOperationException("Cannot suspend/unsuspend packages. User "
|
+ userId + " has an active DO or PO");
|
}
|
|
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
|
callingMethod);
|
|
final int packageUid = getPackageUid(callingPackage, 0, userId);
|
final boolean allowedPackageUid = packageUid == callingUid;
|
final boolean allowedShell = callingUid == SHELL_UID
|
&& UserHandle.isSameApp(packageUid, callingUid);
|
|
if (!allowedShell && !allowedPackageUid) {
|
throw new SecurityException("Calling package " + callingPackage + " in user "
|
+ userId + " does not belong to calling uid " + callingUid);
|
}
|
}
|
|
@Override
|
public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
|
PersistableBundle appExtras, PersistableBundle launcherExtras,
|
SuspendDialogInfo dialogInfo, String callingPackage, int userId) {
|
final int callingUid = Binder.getCallingUid();
|
enforceCanSetPackagesSuspendedAsUser(callingPackage, callingUid, userId,
|
"setPackagesSuspendedAsUser");
|
|
if (ArrayUtils.isEmpty(packageNames)) {
|
return packageNames;
|
}
|
|
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;
|
}
|
|
@Nullable
|
private String getDefaultDialerPackageName(@UserIdInt int userId) {
|
PackageManagerInternal.DefaultDialerProvider provider;
|
synchronized (mPackages) {
|
provider = mDefaultDialerProvider;
|
}
|
if (provider == null) {
|
Slog.e(TAG, "mDefaultDialerProvider is null");
|
return null;
|
}
|
return provider.getDefaultDialer(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.
|
String targetInstallerPackageName =
|
targetPackageSetting.installerPackageName;
|
PackageSetting targetInstallerPkgSetting = targetInstallerPackageName == null ? null :
|
mSettings.mPackages.get(targetInstallerPackageName);
|
|
if (targetInstallerPkgSetting != null) {
|
if (compareSignatures(callerSignature,
|
targetInstallerPkgSetting.signatures.mSigningDetails.signatures)
|
!= PackageManager.SIGNATURE_MATCH) {
|
throw new SecurityException(
|
"Caller does not have same cert as old installer package "
|
+ targetInstallerPackageName);
|
}
|
} else if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
|
!= PackageManager.PERMISSION_GRANTED) {
|
// This is probably an attempt to exploit vulnerability b/150857253 of taking
|
// privileged installer permissions when the installer has been uninstalled or
|
// was never set.
|
EventLog.writeEvent(0x534e4554, "150857253", callingUid, "");
|
|
if (getUidTargetSdkVersionLockedLPr(callingUid) > Build.VERSION_CODES.Q) {
|
throw new SecurityException("Neither user " + callingUid
|
+ " nor current process has " + Manifest.permission.INSTALL_PACKAGES);
|
} else {
|
// If not targeting >Q, fail silently for backwards compatibility
|
return;
|
}
|
}
|
|
// 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.snapshotAndRestoreUserData(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;
|
}
|
|
/**
|
* Gets the user handle for the user that the rollback agent should
|
* use to look up information about this installation when enabling
|
* rollback.
|
*/
|
UserHandle getRollbackUser() {
|
// The session for packages installed for "all" users is
|
// associated with the "system" user.
|
if (mUser == UserHandle.ALL) {
|
return UserHandle.SYSTEM;
|
}
|
return mUser;
|
}
|
|
HandlerParams setTraceMethod(String traceMethod) {
|
this.traceMethod = traceMethod;
|
return this;
|
}
|
|
HandlerParams setTraceCookie(int traceCookie) {
|
this.traceCookie = traceCookie;
|
return this;
|
}
|
|
final void startCopy() {
|
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
|
handleStartCopy();
|
handleReturnCode();
|
}
|
|
abstract void handleStartCopy();
|
abstract void handleReturnCode();
|
}
|
|
static class OriginInfo {
|
/**
|
* Location where install is coming from, before it has been
|
* copied/renamed into place. This could be a single monolithic APK
|
* file, or a cluster directory. This location may be untrusted.
|
*/
|
final File file;
|
|
/**
|
* Flag indicating that {@link #file} has already been staged, meaning downstream users
|
* don't need to defensively copy the contents.
|
*/
|
final boolean staged;
|
|
/**
|
* Flag indicating that {@link #file} is an already installed app that is being moved.
|
*/
|
final boolean existing;
|
|
final String resolvedPath;
|
final File resolvedFile;
|
|
static OriginInfo fromNothing() {
|
return new OriginInfo(null, false, false);
|
}
|
|
static OriginInfo fromUntrustedFile(File file) {
|
return new OriginInfo(file, false, false);
|
}
|
|
static OriginInfo fromExistingFile(File file) {
|
return new OriginInfo(file, false, true);
|
}
|
|
static OriginInfo fromStagedFile(File file) {
|
return new OriginInfo(file, true, false);
|
}
|
|
private OriginInfo(File file, boolean staged, boolean existing) {
|
this.file = file;
|
this.staged = staged;
|
this.existing = existing;
|
|
if (file != null) {
|
resolvedPath = file.getAbsolutePath();
|
resolvedFile = file;
|
} else {
|
resolvedPath = null;
|
resolvedFile = null;
|
}
|
}
|
}
|
|
static class MoveInfo {
|
final int moveId;
|
final String fromUuid;
|
final String toUuid;
|
final String packageName;
|
final 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;
|
}
|
}
|
}
|
|
@Override
|
void handleReturnCode() {
|
for (InstallParams params : mChildParams) {
|
params.handleReturnCode();
|
if (params.mRet != INSTALL_SUCCEEDED) {
|
mRet = params.mRet;
|
}
|
}
|
}
|
|
void tryProcessInstallRequest(InstallArgs args, int currentStatus) {
|
mCurrentState.put(args, currentStatus);
|
if (mCurrentState.size() != mChildParams.size()) {
|
return;
|
}
|
int completeStatus = PackageManager.INSTALL_SUCCEEDED;
|
for (Integer status : mCurrentState.values()) {
|
if (status == PackageManager.INSTALL_UNKNOWN) {
|
return;
|
} else if (status != PackageManager.INSTALL_SUCCEEDED) {
|
completeStatus = status;
|
break;
|
}
|
}
|
final List<InstallRequest> installRequests = new ArrayList<>(mCurrentState.size());
|
for (Map.Entry<InstallArgs, Integer> entry : mCurrentState.entrySet()) {
|
installRequests.add(new InstallRequest(entry.getKey(),
|
createPackageInstalledInfo(completeStatus)));
|
}
|
processInstallRequestsAsync(
|
completeStatus == PackageManager.INSTALL_SUCCEEDED,
|
installRequests);
|
}
|
}
|
|
class InstallParams extends HandlerParams {
|
// TODO: see if we can collapse this into ActiveInstallSession
|
|
final OriginInfo origin;
|
final MoveInfo move;
|
final IPackageInstallObserver2 observer;
|
int installFlags;
|
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;
|
final long requiredInstalledVersionCode;
|
|
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,
|
long requiredInstalledVersionCode) {
|
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;
|
this.requiredInstalledVersionCode = requiredInstalledVersionCode;
|
}
|
|
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();
|
requiredInstalledVersionCode = activeInstallSession.getSessionParams()
|
.requiredInstalledVersionCode;
|
}
|
|
@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 (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
|
if (dataOwnerPkg == null) {
|
Slog.w(TAG, "Required installed version code was "
|
+ requiredInstalledVersionCode
|
+ " but package is not installed");
|
return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION;
|
}
|
|
if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
|
Slog.w(TAG, "Required installed version code was "
|
+ requiredInstalledVersionCode
|
+ " but actual installed version is "
|
+ dataOwnerPkg.getLongVersionCode());
|
return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION;
|
}
|
}
|
|
if (dataOwnerPkg != null) {
|
if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
|
dataOwnerPkg.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 (loc == PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION) {
|
ret = PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
|
} else if (!onInt) {
|
// Override install location with flags
|
if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
|
// Set the flag to install on external media.
|
installFlags &= ~PackageManager.INSTALL_INTERNAL;
|
} else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
|
if (DEBUG_INSTANT) {
|
Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
|
}
|
installFlags |= PackageManager.INSTALL_INSTANT_APP;
|
installFlags &= ~PackageManager.INSTALL_INTERNAL;
|
} else {
|
// Make sure the flag for installing on external
|
// media is unset
|
installFlags |= PackageManager.INSTALL_INTERNAL;
|
}
|
}
|
}
|
}
|
|
final InstallArgs args = createInstallArgs(this);
|
mVerificationCompleted = true;
|
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,
|
getRollbackUser().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 ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
|
String packageName = "";
|
try {
|
PackageLite packageInfo =
|
new PackageParser().parsePackageLite(origin.file, 0);
|
packageName = packageInfo.packageName;
|
} catch (PackageParserException e) {
|
Slog.e(TAG, "Can't parse package at " + origin.file.getAbsolutePath(), e);
|
}
|
try {
|
observer.onPackageInstalled(packageName, mRet, "Dry run", new Bundle());
|
} catch (RemoteException e) {
|
Slog.i(TAG, "Observer no longer exists.");
|
}
|
return;
|
}
|
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
|
mRet = mArgs.copyApk();
|
}
|
processPendingInstall(mArgs, mRet);
|
}
|
}
|
}
|
|
private InstallArgs createInstallArgs(InstallParams params) {
|
if (params.move != null) {
|
return new MoveInstallArgs(params);
|
} else {
|
return new FileInstallArgs(params);
|
}
|
}
|
|
/**
|
* Create args that describe an existing installed package. Typically used
|
* when cleaning up old installs, or used as a move source.
|
*/
|
private InstallArgs createInstallArgsForExisting(String codePath,
|
String resourcePath, String[] instructionSets) {
|
return new FileInstallArgs(codePath, resourcePath, instructionSets);
|
}
|
|
static abstract class InstallArgs {
|
/** @see InstallParams#origin */
|
final OriginInfo origin;
|
/** @see InstallParams#move */
|
final MoveInfo move;
|
|
final IPackageInstallObserver2 observer;
|
// Always refers to PackageManager flags only
|
final int installFlags;
|
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
|
|
// We purposefully exclude FLAG_STORAGE_EXTERNAL here, since
|
// this task was only focused on moving data on internal storage.
|
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;
|
// The set of packages consuming this shared library or null if no consumers exist.
|
ArrayList<PackageParser.Package> libraryConsumers;
|
|
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) <= 29) {
|
// Mismatched signatures is an error and silently skipping system
|
// packages will likely break the device in unforeseen ways.
|
// However, we allow the device to boot anyway because, prior to Q,
|
// vendors were not expecting the platform to crash in this
|
// situation.
|
// This WILL be a hard failure on any new API levels after Q.
|
throw new ReconcileFailure(
|
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
|
"Signature mismatch for shared user: "
|
+ scanResult.pkgSetting.sharedUser);
|
} else {
|
// Treat mismatched signatures on system packages using a shared
|
// UID as
|
// fatal for the system overall, rather than just failing to install
|
// whichever package happened to be scanned later.
|
throw new IllegalStateException(
|
"Signature mismatch on system package "
|
+ 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());
|
final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
|
boolean success = false;
|
try {
|
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
|
for (InstallRequest request : requests) {
|
// TODO(b/109941548): remove this once we've pulled everything from it and into
|
// scan, reconcile or commit.
|
final PrepareResult prepareResult;
|
try {
|
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
|
prepareResult = preparePackageLI(request.args, request.installResult);
|
} catch (PrepareFailure prepareFailure) {
|
request.installResult.setError(prepareFailure.error,
|
prepareFailure.getMessage());
|
request.installResult.origPackage = prepareFailure.conflictingPackage;
|
request.installResult.origPermission = prepareFailure.conflictingPermission;
|
return;
|
} finally {
|
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
|
}
|
request.installResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
|
request.installResult.installerPackageName = request.args.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;
|
}
|
createdAppId.put(packageName, optimisticallyRegisterAppId(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()) {
|
if (createdAppId.getOrDefault(result.request.pkg.packageName, false)) {
|
cleanUpAppIdCreation(result);
|
}
|
}
|
// TODO(patb): create a more descriptive reason than unknown in future release
|
// mark all non-failure installs as UNKNOWN so we do not treat them as success
|
for (InstallRequest request : requests) {
|
if (request.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) {
|
request.installResult.returnCode = PackageManager.INSTALL_UNKNOWN;
|
}
|
}
|
}
|
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, FLAG_STORAGE_DE | FLAG_STORAGE_CE
|
| FLAG_STORAGE_EXTERNAL | 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)
|
&& ((installFlags & PackageManager.INSTALL_STAGED) == 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 {
|
final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath);
|
try {
|
// A file may already have fs-verity, e.g. when reused during a split
|
// install. If the measurement succeeds, no need to attempt to set up.
|
mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
|
} catch (InstallerException e) {
|
mInstaller.installApkVerity(filePath, fd, result.getContentSize());
|
mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
|
}
|
} finally {
|
IoUtils.closeQuietly(fd);
|
}
|
} else if (result.isFailed()) {
|
throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
|
"Failed to generate verity");
|
}
|
}
|
}
|
|
private void startIntentFilterVerifications(int userId, boolean replacing,
|
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;
|
boolean handlesWebUris = false;
|
ArraySet<String> domains = new ArraySet<>();
|
final boolean previouslyVerified;
|
boolean hostSetExpanded = false;
|
boolean needToRunVerify = false;
|
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.
|
IntentFilterVerificationInfo ivi =
|
mSettings.getIntentFilterVerificationLPr(packageName);
|
previouslyVerified = (ivi != null);
|
if (!replacing && previouslyVerified) {
|
if (DEBUG_DOMAIN_VERIFICATION) {
|
Slog.i(TAG, "Package " + packageName + " already verified: status="
|
+ ivi.getStatusString());
|
}
|
return;
|
}
|
|
if (DEBUG_DOMAIN_VERIFICATION) {
|
Slog.i(TAG, " Previous verified hosts: "
|
+ (ivi == null ? "[none]" : ivi.getDomainsString()));
|
}
|
|
// If any filters need to be verified, then all need to be. In addition, we need to
|
// know whether an updating app has any web navigation intent filters, to re-
|
// examine handling policy even if not re-verifying.
|
final boolean needsVerification = needsNetworkVerificationLPr(packageName);
|
for (PackageParser.Activity a : pkg.activities) {
|
for (ActivityIntentInfo filter : a.intents) {
|
if (filter.handlesWebUris(true)) {
|
handlesWebUris = true;
|
}
|
if (needsVerification && filter.needsVerification()) {
|
if (DEBUG_DOMAIN_VERIFICATION) {
|
Slog.d(TAG, "autoVerify requested, processing all filters");
|
}
|
needToRunVerify = true;
|
// It's safe to break out here because filter.needsVerification()
|
// can only be true if filter.handlesWebUris(true) returned true, so
|
// we've already noted that.
|
break;
|
}
|
}
|
}
|
|
// Compare the new set of recognized hosts if the app is either requesting
|
// autoVerify or has previously used autoVerify but no longer does.
|
if (needToRunVerify || previouslyVerified) {
|
final int verificationId = mIntentFilterVerificationToken++;
|
for (PackageParser.Activity a : pkg.activities) {
|
for (ActivityIntentInfo filter : a.intents) {
|
// Run verification against hosts mentioned in any web-nav intent filter,
|
// even if the filter matches non-web schemes as well
|
if (filter.handlesWebUris(false /*onlyWebSchemes*/)) {
|
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
|
"Verification needed for IntentFilter:" + filter.toString());
|
mIntentFilterVerifier.addOneIntentFilterVerification(
|
verifierUid, userId, verificationId, filter, packageName);
|
domains.addAll(filter.getHostsList());
|
count++;
|
}
|
}
|
}
|
}
|
|
if (DEBUG_DOMAIN_VERIFICATION) {
|
Slog.i(TAG, " Update published hosts: " + domains.toString());
|
}
|
|
// If we've previously verified this same host set (or a subset), we can trust that
|
// a current ALWAYS policy is still applicable. If this is the case, we're done.
|
// (If we aren't in ALWAYS, we want to reverify to allow for apps that had failing
|
// hosts in their intent filters, then pushed a new apk that removed them and now
|
// passes.)
|
//
|
// Cases:
|
// + still autoVerify (needToRunVerify):
|
// - preserve current state if all of: unexpanded, in always
|
// - otherwise rerun as usual (fall through)
|
// + no longer autoVerify (alreadyVerified && !needToRunVerify)
|
// - wipe verification history always
|
// - preserve current state if all of: unexpanded, in always
|
hostSetExpanded = !previouslyVerified
|
|| (ivi != null && !ivi.getDomains().containsAll(domains));
|
final int currentPolicy =
|
mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
|
final boolean keepCurState = !hostSetExpanded
|
&& currentPolicy == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
|
|
if (needToRunVerify && keepCurState) {
|
if (DEBUG_DOMAIN_VERIFICATION) {
|
Slog.i(TAG, "Host set not expanding + ALWAYS -> no need to reverify");
|
}
|
ivi.setDomains(domains);
|
scheduleWriteSettingsLocked();
|
return;
|
} else if (previouslyVerified && !needToRunVerify) {
|
// Prior autoVerify state but not requesting it now. Clear autoVerify history,
|
// and preserve the always policy iff the host set is not expanding.
|
clearIntentFilterVerificationsLPw(packageName, userId, !keepCurState);
|
return;
|
}
|
}
|
|
if (needToRunVerify && count > 0) {
|
// app requested autoVerify and has at least one matching intent filter
|
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Starting " + count
|
+ " IntentFilter verification" + (count > 1 ? "s" : "")
|
+ " for userId:" + userId);
|
mIntentFilterVerifier.startVerifications(userId);
|
} else {
|
if (DEBUG_DOMAIN_VERIFICATION) {
|
Slog.d(TAG, "No web filters or no new host policy for " + packageName);
|
}
|
}
|
}
|
|
@GuardedBy("mPackages")
|
private boolean needsNetworkVerificationLPr(String packageName) {
|
IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr(
|
packageName);
|
if (ivi == null) {
|
return true;
|
}
|
int status = ivi.getStatus();
|
switch (status) {
|
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
|
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
|
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
|
return true;
|
|
default:
|
// Nothing to do
|
return false;
|
}
|
}
|
|
private static boolean 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;
|
}
|
|
final PackageSetting uninstalledPs;
|
final PackageSetting disabledSystemPs;
|
final PackageParser.Package pkg;
|
|
// for the uninstall-updates case and restricted profiles, remember the per-
|
// user handle installed state
|
int[] allUsers;
|
/** enabled state of the uninstalled application */
|
final int origEnabledState;
|
synchronized (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;
|
}
|
|
disabledSystemPs = mSettings.getDisabledSystemPkgLPr(packageName);
|
// Save the enabled state before we delete the package. When deleting a stub
|
// application we always set the enabled state to 'disabled'.
|
origEnabledState = uninstalledPs == null
|
? COMPONENT_ENABLED_STATE_DEFAULT : uninstalledPs.getEnabled(userId);
|
// Static shared libs can be declared by any package, so let us not
|
// allow removing a package if it provides a lib others depend on.
|
pkg = mPackages.get(packageName);
|
|
allUsers = 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, MATCH_KNOWN_PACKAGES, 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.
|
synchronized (mInstallLock) {
|
if (info.args != null) {
|
info.args.doPostDeleteLI(true);
|
}
|
final PackageParser.Package stubPkg =
|
(disabledSystemPs == null) ? null : disabledSystemPs.pkg;
|
if (stubPkg != null && stubPkg.isStub) {
|
synchronized (mPackages) {
|
// restore the enabled state of the stub; the state is overwritten when
|
// the stub is uninstalled
|
final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.packageName);
|
if (stubPs != null) {
|
stubPs.setEnabled(origEnabledState, userId, "android");
|
}
|
}
|
if (origEnabledState == COMPONENT_ENABLED_STATE_DEFAULT
|
|| origEnabledState == COMPONENT_ENABLED_STATE_ENABLED) {
|
if (DEBUG_COMPRESSION) {
|
Slog.i(TAG, "Enabling system stub after removal; pkg: "
|
+ stubPkg.packageName);
|
}
|
enableCompressedPackage(stubPkg);
|
}
|
}
|
}
|
|
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,
|
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
|
destroyAppProfilesLIF(resolvedPkg);
|
if (outInfo != null) {
|
outInfo.dataRemoved = true;
|
}
|
}
|
|
int removedAppId = -1;
|
|
// writer
|
boolean installedStateChanged = false;
|
if (deletedPs != null) {
|
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
|
final SparseBooleanArray changedUsers = new SparseBooleanArray();
|
synchronized (mPackages) {
|
clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL, true);
|
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, changedUsers, UserHandle.USER_ALL);
|
}
|
if (changedUsers.size() > 0) {
|
updateDefaultHomeNotLocked(changedUsers);
|
postPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
|
}
|
}
|
// make sure to preserve per-user disabled state if this removal was just
|
// a downgrade of a system app to the factory package
|
if (allUserHandles != null && outInfo != null && outInfo.origUsers != null) {
|
if (DEBUG_REMOVE) {
|
Slog.d(TAG, "Propagating install state across downgrade");
|
}
|
for (int userId : allUserHandles) {
|
final boolean installed = ArrayUtils.contains(outInfo.origUsers, userId);
|
if (DEBUG_REMOVE) {
|
Slog.d(TAG, " user " + userId + " => " + installed);
|
}
|
if (installed != deletedPs.getInstalled(userId)) {
|
installedStateChanged = true;
|
}
|
deletedPs.setInstalled(installed, userId);
|
}
|
}
|
}
|
synchronized (mPackages) {
|
// 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, @Nullable PackageRemovedInfo outInfo,
|
boolean writeSettings)
|
throws SystemDeleteException {
|
final boolean applyUserRestrictions =
|
(allUserHandles != null) && outInfo != 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);
|
}
|
}
|
}
|
|
if (outInfo != null) {
|
// 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 == null ? null : 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) {
|
// We've re-installed the stub; make sure it's disabled here. If package was
|
// originally enabled, we'll install the compressed version of the application
|
// and re-enable it afterward.
|
final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.packageName);
|
if (stubPs != null) {
|
stubPs.setEnabled(
|
COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android");
|
}
|
}
|
}
|
}
|
|
/**
|
* 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);
|
|
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);
|
synchronized (mPackages) {
|
markPackageUninstalledForUserLPw(ps, user);
|
scheduleWritePackageRestrictionsLocked(user);
|
}
|
return;
|
}
|
|
final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
|
if (checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
|
== PERMISSION_GRANTED) {
|
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.
|
final boolean clearPackageStateAndReturn;
|
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");
|
clearPackageStateAndReturn = true;
|
} else {
|
// We need to set it back to 'installed' so the uninstall
|
// broadcasts will be sent correctly.
|
if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");
|
ps.setInstalled(true, userId);
|
mSettings.writeKernelMappingLPr(ps);
|
clearPackageStateAndReturn = false;
|
}
|
} else {
|
// This is a system app, so we assume that the
|
// other users still have this package installed, so all
|
// we need to do is clear this user's data and save that
|
// it is uninstalled.
|
if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
|
clearPackageStateAndReturn = true;
|
}
|
}
|
if (clearPackageStateAndReturn) {
|
clearPackageStateForUserLIF(ps, userId, outInfo, flags);
|
synchronized (mPackages) {
|
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);
|
}
|
|
destroyAppProfilesLIF(pkg);
|
|
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, nextUserId,
|
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
|
clearDefaultBrowserIfNeededForUser(ps.name, nextUserId);
|
removeKeystoreDataIfNeeded(nextUserId, ps.appId);
|
final SparseBooleanArray changedUsers = new SparseBooleanArray();
|
clearPackagePreferredActivitiesLPw(ps.name, changedUsers, nextUserId);
|
if (changedUsers.size() > 0) {
|
updateDefaultHomeNotLocked(changedUsers);
|
postPreferredActivityChangedBroadcast(nextUserId);
|
synchronized (mPackages) {
|
scheduleWritePackageRestrictionsLocked(nextUserId);
|
}
|
}
|
synchronized (mPackages) {
|
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,
|
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
|
|
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;
|
}
|
|
final String packageName = ps.pkg.packageName;
|
|
// 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;
|
|
// Delay and combine non-async permission callbacks
|
final boolean[] permissionRemoved = new boolean[1];
|
final ArraySet<Long> revokedPermissions = new ArraySet<>();
|
final SparseBooleanArray updatedUsers = new SparseBooleanArray();
|
|
PermissionCallback delayingPermCallback = new PermissionCallback() {
|
public void onGidsChanged(int appId, int userId) {
|
mPermissionCallback.onGidsChanged(appId, userId);
|
}
|
|
public void onPermissionChanged() {
|
mPermissionCallback.onPermissionChanged();
|
}
|
|
public void onPermissionGranted(int uid, int userId) {
|
mPermissionCallback.onPermissionGranted(uid, userId);
|
}
|
|
public void onInstallPermissionGranted() {
|
mPermissionCallback.onInstallPermissionGranted();
|
}
|
|
public void onPermissionRevoked(int uid, int userId) {
|
revokedPermissions.add(IntPair.of(uid, userId));
|
|
updatedUsers.put(userId, true);
|
}
|
|
public void onInstallPermissionRevoked() {
|
mPermissionCallback.onInstallPermissionRevoked();
|
}
|
|
public void onPermissionUpdated(int[] updatedUserIds, boolean sync) {
|
for (int userId : updatedUserIds) {
|
if (sync) {
|
updatedUsers.put(userId, true);
|
} else {
|
// Don't override sync=true by sync=false
|
if (!updatedUsers.get(userId)) {
|
updatedUsers.put(userId, false);
|
}
|
}
|
}
|
}
|
|
public void onPermissionRemoved() {
|
permissionRemoved[0] = true;
|
}
|
|
public void onInstallPermissionUpdated() {
|
mPermissionCallback.onInstallPermissionUpdated();
|
}
|
};
|
|
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 int oldFlags = mPermissionManager.getPermissionFlags(permName, packageName,
|
Process.SYSTEM_UID, userId);
|
|
// Always clear the user settable flags.
|
// 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;
|
}
|
|
mPermissionManager.updatePermissionFlags(permName, packageName,
|
userSettableMask, flags, Process.SYSTEM_UID, userId, false,
|
delayingPermCallback);
|
|
// 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) {
|
mPermissionManager.grantRuntimePermission(permName, packageName, false,
|
Process.SYSTEM_UID, userId, delayingPermCallback);
|
// 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.
|
mPermissionManager.revokeRuntimePermission(permName, packageName, false, userId,
|
delayingPermCallback);
|
}
|
}
|
|
// Execute delayed callbacks
|
if (permissionRemoved[0]) {
|
mPermissionCallback.onPermissionRemoved();
|
}
|
|
// Slight variation on the code in mPermissionCallback.onPermissionRevoked() as we cannot
|
// kill uid while holding mPackages-lock
|
if (!revokedPermissions.isEmpty()) {
|
int numRevokedPermissions = revokedPermissions.size();
|
for (int i = 0; i < numRevokedPermissions; i++) {
|
int revocationUID = IntPair.first(revokedPermissions.valueAt(i));
|
int revocationUserId = IntPair.second(revokedPermissions.valueAt(i));
|
|
mOnPermissionChangeListeners.onPermissionsChanged(revocationUID);
|
|
// Kill app later as we are holding mPackages
|
mHandler.post(() -> killUid(UserHandle.getAppId(revocationUID), revocationUserId,
|
KILL_APP_REASON_PERMISSIONS_REVOKED));
|
}
|
}
|
|
int numUpdatedUsers = updatedUsers.size();
|
for (int i = 0; i < numUpdatedUsers; i++) {
|
mSettings.writeRuntimePermissionsForUserLPr(updatedUsers.keyAt(i),
|
updatedUsers.valueAt(i));
|
}
|
}
|
|
/**
|
* 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 = FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL;
|
// We're only clearing cache files, so we don't care if the
|
// app is unfrozen and still able to run
|
clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY);
|
clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
|
}
|
}
|
if (observer != null) {
|
try {
|
observer.onRemoveCompleted(packageName, true);
|
} catch (RemoteException e) {
|
Log.i(TAG, "Observer no longer exists.");
|
}
|
}
|
});
|
}
|
|
@Override
|
public void getPackageSizeInfo(final String packageName, int 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 (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);
|
}
|
if (filter.countActions() == 0) {
|
Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
|
return;
|
}
|
if (DEBUG_PREFERRED) {
|
Slog.i(TAG, opname + " activity " + activity.flattenToShortString() + " for user "
|
+ userId + ":");
|
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
|
}
|
synchronized (mPackages) {
|
final PreferredIntentResolver pir = mSettings.editPreferredActivitiesLPw(userId);
|
pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
|
scheduleWritePackageRestrictionsLocked(userId);
|
}
|
if (!updateDefaultHomeNotLocked(userId)) {
|
postPreferredActivityChangedBroadcast(userId);
|
}
|
}
|
|
private void postPreferredActivityChangedBroadcast(int userId) {
|
mHandler.post(() -> {
|
final IActivityManager am = ActivityManager.getService();
|
if (am == null) {
|
return;
|
}
|
|
final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
|
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
|
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
|
try {
|
am.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");
|
if (mContext.checkCallingOrSelfPermission(
|
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
|
!= PackageManager.PERMISSION_GRANTED) {
|
synchronized (mPackages) {
|
if (getUidTargetSdkVersionLockedLPr(callingUid)
|
< Build.VERSION_CODES.FROYO) {
|
Slog.w(TAG, "Ignoring replacePreferredActivity() from uid "
|
+ Binder.getCallingUid());
|
return;
|
}
|
}
|
mContext.enforceCallingOrSelfPermission(
|
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
|
}
|
|
synchronized (mPackages) {
|
final PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
|
if (pir != null) {
|
// Get all of the existing entries that exactly match this filter.
|
final ArrayList<PreferredActivity> existing = pir.findFilters(filter);
|
if (existing != null && existing.size() == 1) {
|
final PreferredActivity cur = existing.get(0);
|
if (DEBUG_PREFERRED) {
|
Slog.i(TAG, "Checking replace of preferred:");
|
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
|
if (!cur.mPref.mAlways) {
|
Slog.i(TAG, " -- CUR; not mAlways!");
|
} else {
|
Slog.i(TAG, " -- CUR: mMatch=" + cur.mPref.mMatch);
|
Slog.i(TAG, " -- CUR: mSet="
|
+ Arrays.toString(cur.mPref.mSetComponents));
|
Slog.i(TAG, " -- CUR: mComponent=" + cur.mPref.mShortComponent);
|
Slog.i(TAG, " -- NEW: mMatch="
|
+ (match&IntentFilter.MATCH_CATEGORY_MASK));
|
Slog.i(TAG, " -- CUR: mSet=" + Arrays.toString(set));
|
Slog.i(TAG, " -- CUR: mComponent=" + activity.flattenToShortString());
|
}
|
}
|
if (cur.mPref.mAlways && cur.mPref.mComponent.equals(activity)
|
&& cur.mPref.mMatch == (match&IntentFilter.MATCH_CATEGORY_MASK)
|
&& cur.mPref.sameSet(set)) {
|
// Setting the preferred activity to what it happens to be already
|
if (DEBUG_PREFERRED) {
|
Slog.i(TAG, "Replacing with same preferred activity "
|
+ cur.mPref.mShortComponent + " for user "
|
+ userId + ":");
|
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
|
}
|
return;
|
}
|
}
|
if (existing != null) {
|
if (DEBUG_PREFERRED) {
|
Slog.i(TAG, existing.size() + " existing preferred matches for:");
|
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
|
}
|
for (int i = existing.size() - 1; i >= 0; --i) {
|
final PreferredActivity pa = existing.get(i);
|
if (DEBUG_PREFERRED) {
|
Slog.i(TAG, "Removing existing preferred activity "
|
+ pa.mPref.mComponent + ":");
|
pa.dump(new LogPrinter(Log.INFO, TAG), " ");
|
}
|
pir.removeFilter(pa);
|
}
|
}
|
}
|
}
|
addPreferredActivityInternal(filter, match, set, activity, true, userId,
|
"Replacing preferred");
|
}
|
|
@Override
|
public void clearPackagePreferredActivities(String packageName) {
|
final int callingUid = Binder.getCallingUid();
|
if (getInstantAppPackageName(callingUid) != null) {
|
return;
|
}
|
// writer
|
synchronized (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 callingUserId = UserHandle.getCallingUserId();
|
final SparseBooleanArray changedUsers = new SparseBooleanArray();
|
clearPackagePreferredActivitiesLPw(packageName, changedUsers, callingUserId);
|
if (changedUsers.size() > 0) {
|
updateDefaultHomeNotLocked(changedUsers);
|
postPreferredActivityChangedBroadcast(callingUserId);
|
synchronized (mPackages) {
|
scheduleWritePackageRestrictionsLocked(callingUserId);
|
}
|
}
|
}
|
|
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
|
@GuardedBy("mPackages")
|
private void clearPackagePreferredActivitiesLPw(String packageName,
|
@NonNull SparseBooleanArray outUserChanged, int userId) {
|
ArrayList<PreferredActivity> removed = null;
|
for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
|
final int thisUserId = mSettings.mPreferredActivities.keyAt(i);
|
PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
|
if (userId != UserHandle.USER_ALL && userId != thisUserId) {
|
continue;
|
}
|
Iterator<PreferredActivity> it = pir.filterIterator();
|
while (it.hasNext()) {
|
PreferredActivity pa = it.next();
|
// Mark entry for removal only if it matches the package name
|
// and the entry is of type "always".
|
if (packageName == null ||
|
(pa.mPref.mComponent.getPackageName().equals(packageName)
|
&& pa.mPref.mAlways)) {
|
if (removed == null) {
|
removed = new ArrayList<>();
|
}
|
removed.add(pa);
|
}
|
}
|
if (removed != null) {
|
for (int j=0; j<removed.size(); j++) {
|
PreferredActivity pa = removed.get(j);
|
pir.removeFilter(pa);
|
}
|
outUserChanged.put(thisUserId, true);
|
}
|
}
|
}
|
|
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
|
@GuardedBy("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, true);
|
}
|
}
|
|
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
|
@GuardedBy("mPackages")
|
void clearIntentFilterVerificationsLPw(String packageName, int userId,
|
boolean alsoResetStatus) {
|
if (userId == UserHandle.USER_ALL) {
|
if (mSettings.removeIntentFilterVerificationLPw(packageName,
|
sUserManager.getUserIds())) {
|
for (int oneUserId : sUserManager.getUserIds()) {
|
scheduleWritePackageRestrictionsLocked(oneUserId);
|
}
|
}
|
} else {
|
if (mSettings.removeIntentFilterVerificationLPw(packageName, userId,
|
alsoResetStatus)) {
|
scheduleWritePackageRestrictionsLocked(userId);
|
}
|
}
|
}
|
|
/** Clears state for all users, and touches intent filter verification policy */
|
void clearDefaultBrowserIfNeeded(String packageName) {
|
for (int oneUserId : 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 {
|
final SparseBooleanArray changedUsers = new SparseBooleanArray();
|
clearPackagePreferredActivitiesLPw(null, changedUsers, userId);
|
if (changedUsers.size() > 0) {
|
postPreferredActivityChangedBroadcast(userId);
|
}
|
synchronized (mPackages) {
|
mSettings.applyDefaultPreferredAppsLPw(userId);
|
clearIntentFilterVerificationsLPw(userId);
|
primeDomainVerificationsLPw(userId);
|
resetUserChangesToRuntimePermissionsAndFlagsLPw(userId);
|
}
|
updateDefaultHomeNotLocked(userId);
|
// TODO: We have to reset the default SMS and Phone. This requires
|
// significant refactoring to keep all default apps in the package
|
// manager (cleaner but more work) or have the services provide
|
// callbacks to the package manager to request a default app reset.
|
setDefaultBrowserPackageName(null, userId);
|
resetNetworkPolicies(userId);
|
synchronized (mPackages) {
|
scheduleWritePackageRestrictionsLocked(userId);
|
}
|
} finally {
|
Binder.restoreCallingIdentity(identity);
|
}
|
}
|
|
@Override
|
public int getPreferredActivities(List<IntentFilter> outFilters,
|
List<ComponentName> outActivities, String packageName) {
|
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
|
return 0;
|
}
|
int num = 0;
|
final int userId = UserHandle.getCallingUserId();
|
// reader
|
synchronized (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;
|
}
|
if (DEBUG_PREFERRED) {
|
Slog.i(TAG, "Adding persistent preferred activity " + activity
|
+ " for user " + userId + ":");
|
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
|
}
|
synchronized (mPackages) {
|
mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
|
new PersistentPreferredActivity(filter, activity));
|
scheduleWritePackageRestrictionsLocked(userId);
|
}
|
updateDefaultHomeNotLocked(userId);
|
postPreferredActivityChangedBroadcast(userId);
|
}
|
|
@Override
|
public void clearPackagePersistentPreferredActivities(String packageName, int userId) {
|
int callingUid = Binder.getCallingUid();
|
if (callingUid != Process.SYSTEM_UID) {
|
throw new SecurityException(
|
"clearPackagePersistentPreferredActivities can only be run by the system");
|
}
|
ArrayList<PersistentPreferredActivity> removed = null;
|
boolean changed = false;
|
synchronized (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) {
|
updateDefaultHomeNotLocked(userId);
|
postPreferredActivityChangedBroadcast(userId);
|
synchronized (mPackages) {
|
scheduleWritePackageRestrictionsLocked(userId);
|
}
|
}
|
}
|
|
/**
|
* Common machinery for picking apart a restored XML blob and passing
|
* it to a caller-supplied functor to be applied to the running system.
|
*/
|
private void restoreFromXml(XmlPullParser parser, int userId,
|
String expectedStartTag, BlobXmlRestorer functor)
|
throws IOException, XmlPullParserException {
|
int type;
|
while ((type = parser.next()) != XmlPullParser.START_TAG
|
&& type != XmlPullParser.END_DOCUMENT) {
|
}
|
if (type != XmlPullParser.START_TAG) {
|
// oops didn't find a start tag?!
|
if (DEBUG_BACKUP) {
|
Slog.e(TAG, "Didn't find start tag during restore");
|
}
|
return;
|
}
|
// this is supposed to be TAG_PREFERRED_BACKUP
|
if (!expectedStartTag.equals(parser.getName())) {
|
if (DEBUG_BACKUP) {
|
Slog.e(TAG, "Found unexpected tag " + parser.getName());
|
}
|
return;
|
}
|
|
// skip interfering stuff, then we're aligned with the backing implementation
|
while ((type = parser.next()) == XmlPullParser.TEXT) { }
|
functor.apply(parser, userId);
|
}
|
|
private interface BlobXmlRestorer {
|
void apply(XmlPullParser parser, int userId) throws IOException, XmlPullParserException;
|
}
|
|
/**
|
* Non-Binder method, support for the backup/restore mechanism: write the
|
* full set of preferred activities in its canonical XML format. Returns the
|
* XML output as a byte array, or null if there is none.
|
*/
|
@Override
|
public byte[] getPreferredActivityBackup(int userId) {
|
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
|
throw new SecurityException("Only the system may call getPreferredActivityBackup()");
|
}
|
|
ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
|
try {
|
final XmlSerializer serializer = new FastXmlSerializer();
|
serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
|
serializer.startDocument(null, true);
|
serializer.startTag(null, TAG_PREFERRED_BACKUP);
|
|
synchronized (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);
|
}
|
updateDefaultHomeNotLocked(readUserId);
|
});
|
} catch (Exception e) {
|
if (DEBUG_BACKUP) {
|
Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
|
}
|
}
|
}
|
|
/**
|
* Non-Binder method, support for the backup/restore mechanism: write the
|
* default browser (etc) settings in its canonical XML format. Returns the default
|
* browser XML representation as a byte array, or null if there is none.
|
*/
|
@Override
|
public byte[] getDefaultAppsBackup(int userId) {
|
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
|
throw new SecurityException("Only the system may call getDefaultAppsBackup()");
|
}
|
|
ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
|
try {
|
final XmlSerializer serializer = new FastXmlSerializer();
|
serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
|
serializer.startDocument(null, true);
|
serializer.startTag(null, TAG_DEFAULT_APPS);
|
|
synchronized (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;
|
}
|
|
/** <b>must not hold {@link #mPackages}</b> */
|
private void updateDefaultHomeNotLocked(SparseBooleanArray userIds) {
|
if (Thread.holdsLock(mPackages)) {
|
Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
|
+ " is holding mPackages", new Throwable());
|
}
|
for (int i = userIds.size() - 1; i >= 0; --i) {
|
final int userId = userIds.keyAt(i);
|
updateDefaultHomeNotLocked(userId);
|
}
|
}
|
|
/**
|
* <b>must not hold {@link #mPackages}</b>
|
*
|
* @return Whether the ACTION_PREFERRED_ACTIVITY_CHANGED broadcast has been scheduled.
|
*/
|
private boolean updateDefaultHomeNotLocked(int userId) {
|
if (Thread.holdsLock(mPackages)) {
|
Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
|
+ " is holding mPackages", new Throwable());
|
}
|
if (!mSystemReady) {
|
// We might get called before system is ready because of package changes etc, but
|
// finding preferred activity depends on settings provider, so we ignore the update
|
// before that.
|
return false;
|
}
|
final Intent intent = getHomeIntent();
|
final List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
|
PackageManager.GET_META_DATA, userId);
|
final ResolveInfo preferredResolveInfo = findPreferredActivityNotLocked(
|
intent, null, 0, resolveInfos, 0, true, false, false, userId);
|
final String packageName = preferredResolveInfo != null
|
&& preferredResolveInfo.activityInfo != null
|
? preferredResolveInfo.activityInfo.packageName : null;
|
final PackageManagerInternal.DefaultHomeProvider provider;
|
synchronized (mPackages) {
|
provider = mDefaultHomeProvider;
|
}
|
if (provider == null) {
|
Slog.e(TAG, "Default home provider has not been set");
|
return false;
|
}
|
final String currentPackageName = provider.getDefaultHome(userId);
|
if (TextUtils.equals(currentPackageName, packageName)) {
|
return false;
|
}
|
final String[] callingPackages = getPackagesForUid(Binder.getCallingUid());
|
if (callingPackages != null && ArrayUtils.contains(callingPackages,
|
mRequiredPermissionControllerPackage)) {
|
// PermissionController manages default home directly.
|
return false;
|
}
|
provider.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 ensureSystemPackageName(mContext.getString(
|
R.string.config_defaultTextClassifierPackage));
|
}
|
|
@Override
|
public @Nullable String getAttentionServicePackageName() {
|
final String flattenedComponentName =
|
mContext.getString(R.string.config_defaultAttentionService);
|
if (flattenedComponentName != null) {
|
ComponentName componentName = ComponentName.unflattenFromString(flattenedComponentName);
|
if (componentName != null && componentName.getPackageName() != null) {
|
return ensureSystemPackageName(componentName.getPackageName());
|
}
|
}
|
return null;
|
}
|
|
private @Nullable String getDocumenterPackageName() {
|
final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
intent.setType("*/*");
|
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
|
|
final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, resolvedType,
|
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
|
| MATCH_DISABLED_COMPONENTS,
|
UserHandle.myUserId());
|
if (matches.size() == 1) {
|
return matches.get(0).getComponentInfo().packageName;
|
} else {
|
Slog.e(TAG, "There should probably be exactly one documenter; found "
|
+ matches.size() + ": matches=" + matches);
|
return null;
|
}
|
}
|
|
@Nullable
|
private String getDeviceConfiguratorPackageName() {
|
return ensureSystemPackageName(mContext.getString(
|
R.string.config_deviceConfiguratorPackageName));
|
}
|
|
@Override
|
public String getWellbeingPackageName() {
|
return ensureSystemPackageName(mContext.getString(R.string.config_defaultWellbeingPackage));
|
}
|
|
@Override
|
public String getAppPredictionServicePackageName() {
|
String flattenedAppPredictionServiceComponentName =
|
mContext.getString(R.string.config_defaultAppPredictionService);
|
if (flattenedAppPredictionServiceComponentName == null) {
|
return null;
|
}
|
ComponentName appPredictionServiceComponentName =
|
ComponentName.unflattenFromString(flattenedAppPredictionServiceComponentName);
|
if (appPredictionServiceComponentName == null) {
|
return null;
|
}
|
return ensureSystemPackageName(appPredictionServiceComponentName.getPackageName());
|
}
|
|
@Override
|
public String getSystemCaptionsServicePackageName() {
|
String flattenedSystemCaptionsServiceComponentName =
|
mContext.getString(R.string.config_defaultSystemCaptionsService);
|
|
if (TextUtils.isEmpty(flattenedSystemCaptionsServiceComponentName)) {
|
return null;
|
}
|
|
ComponentName systemCaptionsServiceComponentName =
|
ComponentName.unflattenFromString(flattenedSystemCaptionsServiceComponentName);
|
if (systemCaptionsServiceComponentName == null) {
|
return null;
|
}
|
return ensureSystemPackageName(systemCaptionsServiceComponentName.getPackageName());
|
}
|
|
public String getIncidentReportApproverPackageName() {
|
return ensureSystemPackageName(mContext.getString(
|
R.string.config_incidentReportApproverPackage));
|
}
|
|
@Nullable
|
private String ensureSystemPackageName(@Nullable String packageName) {
|
if (packageName == null) {
|
return null;
|
}
|
long token = Binder.clearCallingIdentity();
|
try {
|
if (getPackageInfo(packageName, MATCH_FACTORY_ONLY, UserHandle.USER_SYSTEM) == null) {
|
PackageInfo packageInfo = getPackageInfo(packageName, 0, UserHandle.USER_SYSTEM);
|
if (packageInfo != null) {
|
EventLog.writeEvent(0x534e4554, "145981139", packageInfo.applicationInfo.uid,
|
"");
|
}
|
return null;
|
}
|
} finally {
|
Binder.restoreCallingIdentity(token);
|
}
|
return packageName;
|
}
|
|
@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)) {
|
if (!enableCompressedPackage(deletedPkg)) {
|
return;
|
}
|
}
|
if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
|
// Don't care about who enables an app.
|
callingPackage = null;
|
}
|
synchronized (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;
|
}
|
// InstallerPackageName for Apex is not stored in PackageManager
|
if (ps == null && mApexManager.isApexPackage(packageName)) {
|
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);
|
|
final PermissionPolicyInternal permissionPolicyInternal =
|
LocalServices.getService(PermissionPolicyInternal.class);
|
permissionPolicyInternal.setOnInitializedCallback(userId -> {
|
// The SDK updated case is already handled when we run during the ctor.
|
synchronized (mPackages) {
|
mPermissionManager.updateAllPermissions(
|
StorageManager.UUID_PRIVATE_INTERNAL, false /*sdkUpdated*/,
|
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);
|
if (opti < args.length && "noperm".equals(args[opti])) {
|
dumpState.setOptionEnabled(DumpState.OPTION_SKIP_PERMISSIONS);
|
}
|
} else if ("prov".equals(cmd) || "providers".equals(cmd)) {
|
dumpState.setDump(DumpState.DUMP_PROVIDERS);
|
} else if ("m".equals(cmd) || "messages".equals(cmd)) {
|
dumpState.setDump(DumpState.DUMP_MESSAGES);
|
} else if ("v".equals(cmd) || "verifiers".equals(cmd)) {
|
dumpState.setDump(DumpState.DUMP_VERIFIERS);
|
} else if ("i".equals(cmd) || "ifv".equals(cmd)
|
|| "intent-filter-verifiers".equals(cmd)) {
|
dumpState.setDump(DumpState.DUMP_INTENT_FILTER_VERIFIERS);
|
} else if ("version".equals(cmd)) {
|
dumpState.setDump(DumpState.DUMP_VERSION);
|
} else if ("k".equals(cmd) || "keysets".equals(cmd)) {
|
dumpState.setDump(DumpState.DUMP_KEYSETS);
|
} else if ("installs".equals(cmd)) {
|
dumpState.setDump(DumpState.DUMP_INSTALLS);
|
} else if ("frozen".equals(cmd)) {
|
dumpState.setDump(DumpState.DUMP_FROZEN);
|
} else if ("volumes".equals(cmd)) {
|
dumpState.setDump(DumpState.DUMP_VOLUMES);
|
} else if ("dexopt".equals(cmd)) {
|
dumpState.setDump(DumpState.DUMP_DEXOPT);
|
} else if ("compiler-stats".equals(cmd)) {
|
dumpState.setDump(DumpState.DUMP_COMPILER_STATS);
|
} else if ("changes".equals(cmd)) {
|
dumpState.setDump(DumpState.DUMP_CHANGES);
|
} else if ("service-permissions".equals(cmd)) {
|
dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS);
|
} else if ("write".equals(cmd)) {
|
synchronized (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, FLAG_STORAGE_DE | FLAG_STORAGE_CE
|
| FLAG_STORAGE_EXTERNAL | 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);
|
// AW:BOOTEVENT
|
sAwSystemServerIns.logBootEvent("PMS:reconcileAppsDataLI start");
|
|
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");
|
// AW:BOOTEVENT
|
sAwSystemServerIns.logBootEvent("PMS:reconcileAppsDataLI end");
|
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 UserManagerService um = sUserManager;
|
UserManagerInternal umInternal = getUserManagerInternal();
|
for (UserInfo user : um.getUsers(false /* excludeDying */)) {
|
final int flags;
|
if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
|
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
|
} else if (umInternal.isUserRunning(user.id)) {
|
flags = StorageManager.FLAG_STORAGE_DE;
|
} else {
|
continue;
|
}
|
|
if (ps.getInstalled(user.id)) {
|
// TODO: when user data is locked, mark that we're still dirty
|
prepareAppDataLIF(pkg, user.id, flags);
|
}
|
}
|
}
|
|
/**
|
* 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);
|
}
|
|
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, PackageManager.VERSION_CODE_HIGHEST);
|
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 or static shared library
|
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0
|
|| !TextUtils.isEmpty(ps.pkg.staticSharedLibName)) {
|
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 int getTargetSdkVersionForPackage(String packageName)
|
throws RemoteException {
|
int callingUser = UserHandle.getUserId(Binder.getCallingUid());
|
ApplicationInfo info = getApplicationInfo(packageName, 0, callingUser);
|
if (info == null) {
|
throw new RemoteException(
|
"Couldn't get ApplicationInfo for package " + packageName);
|
}
|
return info.targetSdkVersion;
|
}
|
|
@Override
|
public boolean[] isAudioPlaybackCaptureAllowed(String[] packageNames)
|
throws RemoteException {
|
int callingUser = UserHandle.getUserId(Binder.getCallingUid());
|
boolean[] results = new boolean[packageNames.length];
|
for (int i = results.length - 1; i >= 0; --i) {
|
ApplicationInfo appInfo = getApplicationInfo(packageNames[i], 0, callingUser);
|
results[i] = appInfo == null ? false : appInfo.isAudioPlaybackCaptureAllowed();
|
}
|
return results;
|
}
|
|
@Override
|
public int getLocationFlags(String packageName) throws RemoteException {
|
int callingUser = UserHandle.getUserId(Binder.getCallingUid());
|
ApplicationInfo appInfo = getApplicationInfo(packageName,
|
/*flags*/ 0,
|
/*userId*/ callingUser);
|
if (appInfo == null) {
|
throw new RemoteException(
|
"Couldn't get ApplicationInfo for package " + packageName);
|
}
|
return ((appInfo.isSystemApp() ? IPackageManagerNative.LOCATION_SYSTEM : 0)
|
| (appInfo.isVendor() ? IPackageManagerNative.LOCATION_VENDOR : 0)
|
| (appInfo.isProduct() ? IPackageManagerNative.LOCATION_PRODUCT : 0));
|
}
|
|
@Override
|
public String getModuleMetadataPackageName() throws RemoteException {
|
return PackageManagerService.this.mModuleInfoProvider.getPackageName();
|
}
|
}
|
|
private class PackageManagerInternalImpl extends PackageManagerInternal {
|
@Override
|
public 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 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) {
|
final PackageParser.Package pkg = mPackages.get(packageName);
|
if (pkg == null) {
|
return false;
|
}
|
|
return mPermissionManager.isPermissionsReviewRequired(pkg, userId);
|
}
|
}
|
|
@Override
|
public PackageInfo getPackageInfo(
|
String packageName, int flags, int filterCallingUid, int userId) {
|
return PackageManagerService.this
|
.getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
|
flags, filterCallingUid, userId);
|
}
|
|
@Override
|
public 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 void forEachInstalledPackage(@NonNull Consumer<PackageParser.Package> actionLocked,
|
@UserIdInt int userId) {
|
PackageManagerService.this.forEachInstalledPackage(actionLocked, userId);
|
}
|
|
@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 setDefaultDialerProvider(@NonNull DefaultDialerProvider provider) {
|
synchronized (mPackages) {
|
mDefaultDialerProvider = 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.getPackageInfo(packageName,
|
ApexManager.MATCH_ACTIVE_PACKAGE);
|
if (activePackage == null) {
|
adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
|
packageName + " is not an apex package");
|
return;
|
}
|
if (versionCode != PackageManager.VERSION_CODE_HIGHEST
|
&& activePackage.getLongVersionCode() != versionCode) {
|
adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
|
"Active version " + activePackage.getLongVersionCode()
|
+ " is not equal to " + versionCode + "]");
|
return;
|
}
|
if (!am.uninstallApex(activePackage.applicationInfo.sourceDir)) {
|
adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
|
"Failed to uninstall apex " + packageName);
|
} else {
|
adapter.onPackageDeleted(packageName, PackageManager.DELETE_SUCCEEDED,
|
null);
|
}
|
}
|
|
@Override
|
public boolean wereDefaultPermissionsGrantedSinceBoot(int userId) {
|
synchronized (mPackages) {
|
return mDefaultPermissionPolicy.wereDefaultPermissionsGrantedSinceBoot(userId);
|
}
|
}
|
|
@Override
|
public void setRuntimePermissionsFingerPrint(@NonNull String fingerPrint,
|
@UserIdInt int userId) {
|
synchronized (mPackages) {
|
mSettings.setRuntimePermissionsFingerPrintLPr(fingerPrint, userId);
|
}
|
}
|
|
@Override
|
public void migrateLegacyObbData() {
|
try {
|
mInstaller.migrateLegacyObbData();
|
} catch (Exception e) {
|
Slog.wtf(TAG, e);
|
}
|
}
|
}
|
|
@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));
|
}
|
}
|
}
|
|
void forEachInstalledPackage(@NonNull Consumer<PackageParser.Package> actionLocked,
|
@UserIdInt int userId) {
|
synchronized (mPackages) {
|
int numPackages = mPackages.size();
|
for (int i = 0; i < numPackages; i++) {
|
PackageParser.Package pkg = mPackages.valueAt(i);
|
PackageSetting setting = mSettings.getPackageLPr(pkg.packageName);
|
if (setting == null || !setting.getInstalled(userId)) {
|
continue;
|
}
|
actionLocked.accept(pkg);
|
}
|
}
|
}
|
|
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);
|
}
|