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_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.WRITE_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
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.INSTALL_EXTERNAL;
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
import static android.content.pm.PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
-import static android.content.pm.PackageManager.INSTALL_FAILED_DEXOPT;
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_MISSING_SHARED_LIBRARY;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE;
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_UID_CHANGED;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_USER_RESTRICTED;
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.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.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
import static android.content.pm.PackageManager.MATCH_ALL;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+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.PackageParser.PARSE_IS_PRIVILEGED;
import static android.content.pm.PackageParser.isApkFile;
-import static android.os.Process.PACKAGE_INFO_GID;
-import static android.os.Process.SYSTEM_UID;
+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.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDWR;
+
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
-import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER;
+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.internal.util.ArrayUtils.appendInt;
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.getCompilerFilterForReason;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter;
+
import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
-import android.app.AppGlobals;
+import android.app.AppOpsManager;
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.app.usage.UsageStats;
-import android.app.usage.UsageStatsManager;
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.ServiceConnection;
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.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.InstantAppResolveInfo;
import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
-import android.content.pm.ManifestDigest;
import android.content.pm.PackageCleanItem;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
-import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
+import android.content.pm.VersionedPackage;
import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.graphics.Bitmap;
import android.hardware.display.DisplayManager;
import android.net.Uri;
-import android.os.Debug;
+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.Environment.UserEnvironment;
import android.os.FileUtils;
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.PatternMatcher;
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.storage.IMountService;
-import android.os.storage.MountServiceInternal;
+import android.os.UserManagerInternal;
+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.Settings.Global;
+import android.provider.Settings.Secure;
import android.security.KeyStore;
import android.security.SystemKeyStore;
+import android.service.pm.PackageServiceDumpProto;
import android.system.ErrnoException;
import android.system.Os;
-import android.system.StructStat;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.AtomicFile;
+import android.util.Base64;
+import android.util.TimingsTraceLog;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.ExceptionUtils;
import android.util.Log;
import android.util.LogPrinter;
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.Xml;
+import android.util.jar.StrictJarFile;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
-import dalvik.system.DexFile;
-import dalvik.system.VMRuntime;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
-
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IMediaContainerService;
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.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.IParcelFileDescriptorFactory;
+import com.android.internal.os.RoSystemProperties;
import com.android.internal.os.SomeArgs;
import com.android.internal.os.Zygote;
+import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ConcurrentUtils;
+import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+import com.android.server.AttributeCache;
+import com.android.server.DeviceIdleController;
import com.android.server.EventLogTags;
import com.android.server.FgThread;
import com.android.server.IntentResolver;
import com.android.server.LocalServices;
+import com.android.server.LockGuard;
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.PermissionsState.PermissionState;
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.pm.Settings.VersionInfo;
+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.storage.DeviceStorageMonitorInternal;
+import dalvik.system.CloseGuard;
+import dalvik.system.DexFile;
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+import libcore.util.EmptyArray;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
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.concurrent.atomic.AtomicLong;
+import java.util.zip.GZIPInputStream;
/**
- * Keep track of all those .apks everywhere.
- *
- * This is very central to the platform's security; please run the unit
- * tests whenever making modifications here:
- *
-mmm frameworks/base/tests/AndroidTests
-adb install -r -f out/target/product/passion/data/app/AndroidTests.apk
-adb shell am instrument -w -e class com.android.unit_tests.PackageManagerTests com.android.unit_tests/android.test.InstrumentationTestRunner
+ * 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:
*
- * {@hide}
+ * <pre>
+ * $ runtest -c android.content.pm.PackageManagerTests frameworks-core
+ * $ cts-tradefed run commandAndExit cts -m CtsAppSecurityHostTestCases
+ * </pre>
*/
-public class PackageManagerService extends IPackageManager.Stub {
+public class PackageManagerService extends IPackageManager.Stub
+ implements PackageSender {
static final String TAG = "PackageManager";
static final boolean DEBUG_SETTINGS = false;
static final boolean DEBUG_PREFERRED = false;
private static final boolean DEBUG_INTENT_MATCHING = false;
private static final boolean DEBUG_PACKAGE_SCANNING = false;
private static final boolean DEBUG_VERIFY = false;
- private static final boolean DEBUG_DEXOPT = false;
private static final boolean DEBUG_FILTERS = false;
+ private static final boolean DEBUG_PERMISSIONS = false;
+ private static final boolean DEBUG_SHARED_LIBRARIES = false;
+ private 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_EPHEMERAL = Build.IS_DEBUGGABLE;
+ private static final boolean DEBUG_TRIAGED_MISSING = false;
+ 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 int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
private static final int NFC_UID = Process.NFC_UID;
static final int SCAN_FORCE_DEX = 1<<2;
static final int SCAN_UPDATE_SIGNATURE = 1<<3;
static final int SCAN_NEW_INSTALL = 1<<4;
- static final int SCAN_NO_PATHS = 1<<5;
- static final int SCAN_UPDATE_TIME = 1<<6;
- static final int SCAN_DEFER_DEX = 1<<7;
- static final int SCAN_BOOTING = 1<<8;
- static final int SCAN_TRUSTED_OVERLAY = 1<<9;
- static final int SCAN_DELETE_DATA_ON_FAILURES = 1<<10;
- static final int SCAN_REPLACING = 1<<11;
- static final int SCAN_REQUIRE_KNOWN = 1<<12;
- static final int SCAN_MOVE = 1<<13;
- static final int SCAN_INITIAL = 1<<14;
-
- static final int REMOVE_CHATTY = 1<<16;
+ static final int SCAN_UPDATE_TIME = 1<<5;
+ static final int SCAN_BOOTING = 1<<6;
+ static final int SCAN_TRUSTED_OVERLAY = 1<<7;
+ static final int SCAN_DELETE_DATA_ON_FAILURES = 1<<8;
+ static final int SCAN_REPLACING = 1<<9;
+ static final int SCAN_REQUIRE_KNOWN = 1<<10;
+ static final int SCAN_MOVE = 1<<11;
+ static final int SCAN_INITIAL = 1<<12;
+ static final int SCAN_CHECK_ONLY = 1<<13;
+ static final int SCAN_DONT_KILL_APP = 1<<14;
+ static final int SCAN_IGNORE_FROZEN = 1<<15;
+ static final int SCAN_FIRST_BOOT_OR_UPGRADE = 1<<16;
+ static final int SCAN_AS_INSTANT_APP = 1<<17;
+ static final int SCAN_AS_FULL_APP = 1<<18;
+ static final int SCAN_AS_VIRTUAL_PRELOAD = 1<<19;
+ /** Should not be with the scan flags */
+ static final int FLAGS_REMOVE_CHATTY = 1<<31;
+
+ private static final String STATIC_SHARED_LIB_DELIMITER = "_";
+ /** Extension of the compressed packages */
+ private final static String COMPRESSED_EXTENSION = ".gz";
+ /** Suffix of stub packages on the system partition */
+ private 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.
*/
- private static final long WATCHDOG_TIMEOUT = 1000*60*10; // ten minutes
+ static final long WATCHDOG_TIMEOUT = 1000*60*10; // ten minutes
/**
* Wall-clock timeout (in milliseconds) after which we *require* that an fstrim
*/
private static final int DEFAULT_VERIFICATION_RESPONSE = PackageManager.VERIFICATION_ALLOW;
+ static final String PLATFORM_PACKAGE_NAME = "android";
+
static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
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";
/** Permission grant: not grant the permission. */
/** Permission grant: grant the permission as an install permission. */
private static final int GRANT_INSTALL = 2;
- /** Permission grant: grant the permission as an install permission for a legacy app. */
- private static final int GRANT_INSTALL_LEGACY = 3;
-
/** Permission grant: grant the permission as a runtime one. */
- private static final int GRANT_RUNTIME = 4;
+ private static final int GRANT_RUNTIME = 3;
/** Permission grant: grant as runtime a permission that was granted as an install time one. */
- private static final int GRANT_UPGRADE = 5;
+ private static final int GRANT_UPGRADE = 4;
/** Canonical intent used to identify what counts as a "web browser" app */
private static final Intent sBrowserIntent;
sBrowserIntent.setData(Uri.parse("http:"));
}
+ /**
+ * The set of all protected actions [i.e. those actions for which a high priority
+ * intent filter is disallowed].
+ */
+ private static final Set<String> PROTECTED_ACTIONS = new ArraySet<>();
+ static {
+ PROTECTED_ACTIONS.add(Intent.ACTION_SEND);
+ PROTECTED_ACTIONS.add(Intent.ACTION_SENDTO);
+ PROTECTED_ACTIONS.add(Intent.ACTION_SEND_MULTIPLE);
+ PROTECTED_ACTIONS.add(Intent.ACTION_VIEW);
+ }
+
+ // Compilation reasons.
+ 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;
+
+ /** All dangerous permission names in the same order as the events in MetricsEvent */
+ private static final List<String> ALL_DANGEROUS_PERMISSIONS = Arrays.asList(
+ Manifest.permission.READ_CALENDAR,
+ Manifest.permission.WRITE_CALENDAR,
+ Manifest.permission.CAMERA,
+ Manifest.permission.READ_CONTACTS,
+ Manifest.permission.WRITE_CONTACTS,
+ Manifest.permission.GET_ACCOUNTS,
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.RECORD_AUDIO,
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.CALL_PHONE,
+ Manifest.permission.READ_CALL_LOG,
+ Manifest.permission.WRITE_CALL_LOG,
+ Manifest.permission.ADD_VOICEMAIL,
+ Manifest.permission.USE_SIP,
+ Manifest.permission.PROCESS_OUTGOING_CALLS,
+ Manifest.permission.READ_CELL_BROADCASTS,
+ Manifest.permission.BODY_SENSORS,
+ Manifest.permission.SEND_SMS,
+ Manifest.permission.RECEIVE_SMS,
+ Manifest.permission.READ_SMS,
+ Manifest.permission.RECEIVE_WAP_PUSH,
+ Manifest.permission.RECEIVE_MMS,
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.READ_PHONE_NUMBERS,
+ Manifest.permission.ANSWER_PHONE_CALLS);
+
+
+ /**
+ * Version number for the package parser cache. Increment this whenever the format or
+ * extent of cached data changes. See {@code PackageParser#setCacheDir}.
+ */
+ private static final String PACKAGE_PARSER_CACHE_VERSION = "1";
+
+ /**
+ * Whether the package parser cache is enabled.
+ */
+ private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true;
+
final ServiceThread mHandlerThread;
final PackageHandler mHandler;
+ private final ProcessLoggingHandler mProcessLoggingHandler;
+
/**
* Messages for {@link #mHandler} that need to wait for system ready before
* being dispatched.
final Context mContext;
final boolean mFactoryTest;
final boolean mOnlyCore;
- final boolean mLazyDexOpt;
- final long mDexOptLRUThresholdInMills;
final DisplayMetrics mMetrics;
final int mDefParseFlags;
final String[] mSeparateProcesses;
final boolean mIsUpgrade;
+ final boolean mIsPreNUpgrade;
+ final boolean mIsPreNMR1Upgrade;
- // This is where all application persistent data goes.
- final File mAppDataDir;
+ // Have we told the Activity Manager to whitelist the default container service by uid yet?
+ @GuardedBy("mPackages")
+ boolean mDefaultContainerWhitelisted = false;
- // This is where all application persistent data goes for secondary users.
- final File mUserAppDataDir;
+ @GuardedBy("mPackages")
+ private boolean mDexOptDialogShown;
/** The location for ASEC container files on internal storage. */
final String mAsecInternalPath;
final ArrayMap<String, PackageParser.Package> mPackages =
new ArrayMap<String, PackageParser.Package>();
- // Tracks available target package names -> overlay package paths.
- final ArrayMap<String, ArrayMap<String, PackageParser.Package>> mOverlays =
- new ArrayMap<String, ArrayMap<String, PackageParser.Package>>();
+ final ArrayMap<String, Set<String>> mKnownCodebase =
+ new ArrayMap<String, Set<String>>();
+
+ // 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
* are package location.
*/
final private ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
+ /**
+ * Tracks high priority intent filters for protected actions. During boot, certain
+ * filter actions are protected and should never be allowed to have a high priority
+ * intent filter for them. However, there is one, and only one exception -- the
+ * setup wizard. It must be able to define a high priority intent filter for these
+ * actions to ensure there are no escapes from the wizard. We need to delay processing
+ * of these during boot as we need to look at all of the system packages in order
+ * to know which component is the setup wizard.
+ */
+ private final List<PackageParser.ActivityIntentInfo> mProtectedFilters = new ArrayList<>();
+ /**
+ * Whether or not processing protected filters should be deferred.
+ */
+ private boolean mDeferProtectedFilters = true;
/**
* Tracks existing system packages prior to receiving an OTA. Keys are package name.
*/
boolean mPromoteSystemApps;
+ @GuardedBy("mPackages")
final Settings mSettings;
- boolean mRestoredSettings;
+
+ /**
+ * 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;
// System configuration read by SystemConfig.
final int[] mGlobalGids;
final SparseArray<ArraySet<String>> mSystemPermissions;
+ @GuardedBy("mAvailableFeatures")
final ArrayMap<String, FeatureInfo> mAvailableFeatures;
// If mac_permissions.xml was found for seinfo labeling.
boolean mFoundPolicyFile;
- // If a recursive restorecon of /data/data/<pkg> is needed.
- private boolean mShouldRestoreconData = SELinuxMMAC.shouldRestorecon();
+ 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<>();
+
+ class PackageParserCallback implements PackageParser.Callback {
+ @Override public final boolean hasFeature(String feature) {
+ return PackageManagerService.this.hasSystemFeature(feature, 0);
+ }
+
+ final List<PackageParser.Package> getStaticOverlayPackagesLocked(
+ Collection<PackageParser.Package> allPackages, String targetPackageName) {
+ List<PackageParser.Package> overlayPackages = null;
+ for (PackageParser.Package p : allPackages) {
+ if (targetPackageName.equals(p.mOverlayTarget) && p.mIsStaticOverlay) {
+ if (overlayPackages == null) {
+ overlayPackages = new ArrayList<PackageParser.Package>();
+ }
+ overlayPackages.add(p);
+ }
+ }
+ if (overlayPackages != null) {
+ Comparator<PackageParser.Package> cmp = new Comparator<PackageParser.Package>() {
+ public int compare(PackageParser.Package p1, PackageParser.Package p2) {
+ return p1.mOverlayPriority - p2.mOverlayPriority;
+ }
+ };
+ Collections.sort(overlayPackages, cmp);
+ }
+ return overlayPackages;
+ }
+
+ final String[] getStaticOverlayPathsLocked(Collection<PackageParser.Package> allPackages,
+ String targetPackageName, String targetPath) {
+ 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 =
+ getStaticOverlayPackagesLocked(allPackages, targetPackageName);
+ 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<String>();
+ }
+ 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<String>();
+ }
+ 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) {
+ synchronized (mPackages) {
+ return getStaticOverlayPathsLocked(
+ mPackages.values(), targetPackageName, 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.mIsStaticOverlay) {
+ if (mOverlayPackages == null) {
+ mOverlayPackages = new ArrayList<PackageParser.Package>();
+ }
+ 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.
+ // Moreover holding mPackages on each parsing thread causes dead-lock.
+ return mOverlayPackages == null ? null :
+ getStaticOverlayPathsLocked(mOverlayPackages, targetPackageName, targetPath);
+ }
+ }
+
+ final PackageParser.Callback mPackageParserCallback = new PackageParserCallback();
+ final ParallelPackageParserCallback mParallelPackageParserCallback =
+ new ParallelPackageParserCallback();
public static final class SharedLibraryEntry {
- public final String path;
- public final String apk;
+ public final @Nullable String path;
+ public final @Nullable String apk;
+ public final @NonNull SharedLibraryInfo info;
- SharedLibraryEntry(String _path, String _apk) {
+ SharedLibraryEntry(String _path, String _apk, String name, int version, int type,
+ String declaringPackageName, int declaringPackageVersionCode) {
path = _path;
apk = _apk;
+ info = new SharedLibraryInfo(name, version, type, new VersionedPackage(
+ declaringPackageName, declaringPackageVersionCode), null);
}
}
// Currently known shared libraries.
- final ArrayMap<String, SharedLibraryEntry> mSharedLibraries =
- new ArrayMap<String, SharedLibraryEntry>();
+ final ArrayMap<String, SparseArray<SharedLibraryEntry>> mSharedLibraries = new ArrayMap<>();
+ final ArrayMap<String, SparseArray<SharedLibraryEntry>> mStaticLibsByDeclaringPackage =
+ new ArrayMap<>();
// All available activities, for your resolving pleasure.
final ActivityIntentResolver mActivities =
final ArraySet<String> mTransferedPackages = new ArraySet<String>();
// Broadcast actions that are only available to the system.
- final ArraySet<String> mProtectedBroadcasts = new ArraySet<String>();
+ @GuardedBy("mProtectedBroadcasts")
+ final ArraySet<String> mProtectedBroadcasts = new ArraySet<>();
/** List of packages waiting for verification. */
final SparseArray<PackageVerificationState> mPendingVerification
final PackageInstallerService mInstallerService;
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 AtomicInteger mNextMoveId = new AtomicInteger();
private final MoveCallbacks mMoveCallbacks;
volatile boolean mSystemReady;
volatile boolean mSafeMode;
volatile boolean mHasSystemUidErrors;
+ private volatile boolean mEphemeralAppsDisabled;
ApplicationInfo mAndroidApplication;
final ActivityInfo mResolveActivity = new ActivityInfo();
boolean mResolverReplaced = false;
- private final ComponentName mIntentFilterVerifierComponent;
+ private final @Nullable ComponentName mIntentFilterVerifierComponent;
+ private final @Nullable IntentFilterVerifier<ActivityIntentInfo> mIntentFilterVerifier;
+
private int mIntentFilterVerificationToken = 0;
+ /** The service connection to the ephemeral resolver */
+ final EphemeralResolverConnection mInstantAppResolverConnection;
+ /** Component used to show resolver settings for Instant Apps */
+ final ComponentName mInstantAppResolverSettingsComponent;
+
+ /** Activity used to install instant applications */
+ ActivityInfo mInstantAppInstallerActivity;
+ final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();
+
final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
= new SparseArray<IntentFilterVerificationState>();
- final DefaultPermissionGrantPolicy mDefaultPermissionPolicy =
- new DefaultPermissionGrantPolicy(this);
+ final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
+
+ // List of packages names to keep cached, even if they are uninstalled for all users
+ private List<String> mKeepUninstalledPackages;
+
+ private UserManagerInternal mUserManagerInternal;
+
+ private DeviceIdleController.LocalService mDeviceIdleController;
+
+ private File mCacheDir;
+
+ private ArraySet<String> mPrivappPermissionsViolations;
+
+ private Future<?> mPrepareAppDataFuture;
private static class IFVerificationParams {
PackageParser.Package pkg;
PackageParser.ActivityIntentInfo filter = filters.get(m);
domainsSet.addAll(filter.getHostsList());
}
- ArrayList<String> domainsList = new ArrayList<>(domainsSet);
synchronized (mPackages) {
if (mSettings.createIntentFilterVerificationIfNeededLPw(
- packageName, domainsList) != null) {
+ packageName, domainsSet) != null) {
scheduleWriteSettingsLocked();
}
}
- sendVerificationRequest(userId, verificationId, ivs);
+ sendVerificationRequest(verificationId, ivs);
}
mCurrentIntentFilterVerifications.clear();
}
- private void sendVerificationRequest(int userId, int verificationId,
- IntentFilterVerificationState ivs) {
-
+ 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,
verificationIntent.setComponent(mIntentFilterVerifierComponent);
verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- UserHandle user = new UserHandle(userId);
- mContext.sendBroadcastAsUser(verificationIntent, user);
+ DeviceIdleController.LocalService idleController = getDeviceIdleController();
+ idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
+ mIntentFilterVerifierComponent.getPackageName(), getVerificationTimeout(),
+ UserHandle.USER_SYSTEM, true, "intent filter verifier");
+
+ mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM);
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"Sending IntentFilter verification broadcast");
}
packageName);
}
if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG, "Adding verification filter for " + packageName + " : " + filter);
+ Slog.d(TAG, "Adding verification filter for " + packageName + ": " + filter);
}
ivs.addFilter(filter);
return true;
filter.hasDataScheme(IntentFilter.SCHEME_HTTPS));
}
- private IntentFilterVerifier mIntentFilterVerifier;
-
// 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>
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 WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds
// Delay time in millisecs
static final int BROADCAST_DELAY = 10 * 1000;
+ 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
class DefaultContainerConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
- IMediaContainerService imcs =
- IMediaContainerService.Stub.asInterface(service);
+ final IMediaContainerService imcs = IMediaContainerService.Stub
+ .asInterface(Binder.allowBlocking(service));
mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
}
// Recordkeeping of restore-after-install operations that are currently in flight
// between the Package Manager and the Backup Manager
- class PostInstallData {
+ static class PostInstallData {
public InstallArgs args;
public PackageInstalledInfo res;
private static final String TAG_DEFAULT_APPS = "da";
private static final String TAG_INTENT_FILTER_VERIFICATION = "iv";
- final String mRequiredVerifierPackage;
- final String mRequiredInstallerPackage;
+ 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 @Nullable String mSetupWizardPackage;
+ final @Nullable String mStorageManagerPackage;
+ final @NonNull String mServicesSystemSharedLibraryPackageName;
+ final @NonNull String mSharedSystemSharedLibraryPackageName;
+
+ final boolean mPermissionReviewRequired;
private final PackageUsage mPackageUsage = new PackageUsage();
-
- private class PackageUsage {
- private static final int WRITE_INTERVAL
- = (DEBUG_DEXOPT) ? 0 : 30*60*1000; // 30m in ms
-
- private final Object mFileLock = new Object();
- private final AtomicLong mLastWritten = new AtomicLong(0);
- private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false);
-
- private boolean mIsHistoricalPackageUsageAvailable = true;
-
- boolean isHistoricalPackageUsageAvailable() {
- return mIsHistoricalPackageUsageAvailable;
- }
-
- void write(boolean force) {
- if (force) {
- writeInternal();
- return;
- }
- if (SystemClock.elapsedRealtime() - mLastWritten.get() < WRITE_INTERVAL
- && !DEBUG_DEXOPT) {
- return;
- }
- if (mBackgroundWriteRunning.compareAndSet(false, true)) {
- new Thread("PackageUsage_DiskWriter") {
- @Override
- public void run() {
- try {
- writeInternal();
- } finally {
- mBackgroundWriteRunning.set(false);
- }
- }
- }.start();
- }
- }
-
- private void writeInternal() {
- synchronized (mPackages) {
- synchronized (mFileLock) {
- AtomicFile file = getFile();
- FileOutputStream f = null;
- try {
- f = file.startWrite();
- BufferedOutputStream out = new BufferedOutputStream(f);
- FileUtils.setPermissions(file.getBaseFile().getPath(), 0640, SYSTEM_UID, PACKAGE_INFO_GID);
- StringBuilder sb = new StringBuilder();
- for (PackageParser.Package pkg : mPackages.values()) {
- if (pkg.mLastPackageUsageTimeInMills == 0) {
- continue;
- }
- sb.setLength(0);
- sb.append(pkg.packageName);
- sb.append(' ');
- sb.append((long)pkg.mLastPackageUsageTimeInMills);
- sb.append('\n');
- out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
- }
- out.flush();
- file.finishWrite(f);
- } catch (IOException e) {
- if (f != null) {
- file.failWrite(f);
- }
- Log.e(TAG, "Failed to write package usage times", e);
- }
- }
- }
- mLastWritten.set(SystemClock.elapsedRealtime());
- }
-
- void readLP() {
- synchronized (mFileLock) {
- AtomicFile file = getFile();
- BufferedInputStream in = null;
- try {
- in = new BufferedInputStream(file.openRead());
- StringBuffer sb = new StringBuffer();
- while (true) {
- String packageName = readToken(in, sb, ' ');
- if (packageName == null) {
- break;
- }
- String timeInMillisString = readToken(in, sb, '\n');
- if (timeInMillisString == null) {
- throw new IOException("Failed to find last usage time for package "
- + packageName);
- }
- PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- continue;
- }
- long timeInMillis;
- try {
- timeInMillis = Long.parseLong(timeInMillisString.toString());
- } catch (NumberFormatException e) {
- throw new IOException("Failed to parse " + timeInMillisString
- + " as a long.", e);
- }
- pkg.mLastPackageUsageTimeInMills = timeInMillis;
- }
- } catch (FileNotFoundException expected) {
- mIsHistoricalPackageUsageAvailable = false;
- } catch (IOException e) {
- Log.w(TAG, "Failed to read package usage times", e);
- } finally {
- IoUtils.closeQuietly(in);
- }
- }
- mLastWritten.set(SystemClock.elapsedRealtime());
- }
-
- private String readToken(InputStream in, StringBuffer sb, char endOfToken)
- throws IOException {
- sb.setLength(0);
- while (true) {
- int ch = in.read();
- if (ch == -1) {
- if (sb.length() == 0) {
- return null;
- }
- throw new IOException("Unexpected EOF");
- }
- if (ch == endOfToken) {
- return sb.toString();
- }
- sb.append((char)ch);
- }
- }
-
- private AtomicFile getFile() {
- File dataDir = Environment.getDataDirectory();
- File systemDir = new File(dataDir, "system");
- File fname = new File(systemDir, "package-usage.list");
- return new AtomicFile(fname);
- }
- }
+ private final CompilerStats mCompilerStats = new CompilerStats();
class PackageHandler extends Handler {
private boolean mBound = false;
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
if (mContext.bindServiceAsUser(service, mDefContainerConn,
- Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
+ Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mBound = true;
return true;
// need to do anything. The pending install
// will be processed later on.
if (!mBound) {
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
+ System.identityHashCode(mHandler));
// If this is the only one pending we might
// have to bind to the service again.
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
params.serviceError();
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
+ System.identityHashCode(mHandler));
+ if (params.traceMethod != null) {
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod,
+ params.traceCookie);
+ }
return;
} else {
// Once we bind to the service, the first
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
+ System.identityHashCode(mHandler));
}
if (mContainerService == null) {
if (!mBound) {
for (HandlerParams params : mPendingInstalls) {
// Indicate service bind error
params.serviceError();
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+ System.identityHashCode(params));
+ if (params.traceMethod != null) {
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER,
+ params.traceMethod, params.traceCookie);
+ }
+ return;
}
mPendingInstalls.clear();
} else {
} else if (mPendingInstalls.size() > 0) {
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+ System.identityHashCode(params));
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
if (params.startCopy()) {
// We are done... look for more work or to
// go idle.
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
} else {
// Should never happen ideally.
for (HandlerParams params : mPendingInstalls) {
// Indicate service bind error
params.serviceError();
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+ System.identityHashCode(params));
}
mPendingInstalls.clear();
}
}
case MCS_GIVE_UP: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_giveup too many retries");
- mPendingInstalls.remove(0);
+ HandlerParams params = mPendingInstalls.remove(0);
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+ System.identityHashCode(params));
break;
}
case SEND_PENDING_BROADCAST: {
} 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);
- boolean deleteOld = false;
if (data != null) {
InstallArgs args = data.args;
- PackageInstalledInfo res = data.res;
-
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- final String packageName = res.pkg.applicationInfo.packageName;
- res.removedInfo.sendBroadcast(false, true, false);
- Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, res.uid);
-
- // Now that we successfully installed the package, grant runtime
- // permissions if requested before broadcasting the install.
- if ((args.installFlags
- & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0) {
- grantRequestedRuntimePermissions(res.pkg, args.user.getIdentifier(),
- args.installGrantPermissions);
- }
-
- // Determine the set of users who are adding this
- // package for the first time vs. those who are seeing
- // an update.
- int[] firstUsers;
- int[] updateUsers = new int[0];
- if (res.origUsers == null || res.origUsers.length == 0) {
- firstUsers = res.newUsers;
- } else {
- firstUsers = new int[0];
- for (int i=0; i<res.newUsers.length; i++) {
- int user = res.newUsers[i];
- boolean isNew = true;
- for (int j=0; j<res.origUsers.length; j++) {
- if (res.origUsers[j] == user) {
- isNew = false;
- break;
- }
- }
- if (isNew) {
- int[] newFirst = new int[firstUsers.length+1];
- System.arraycopy(firstUsers, 0, newFirst, 0,
- firstUsers.length);
- newFirst[firstUsers.length] = user;
- firstUsers = newFirst;
- } else {
- int[] newUpdate = new int[updateUsers.length+1];
- System.arraycopy(updateUsers, 0, newUpdate, 0,
- updateUsers.length);
- newUpdate[updateUsers.length] = user;
- updateUsers = newUpdate;
- }
- }
- }
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- packageName, extras, null, null, firstUsers);
- final boolean update = res.removedInfo.removedPackage != null;
- if (update) {
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
- }
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- packageName, extras, null, null, updateUsers);
- if (update) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, null, null, updateUsers);
- sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
- null, null, packageName, null, updateUsers);
-
- // treat asec-hosted packages like removable media on upgrade
- if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
- if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + res.pkg
- + " is ASEC-hosted -> AVAILABLE");
- }
- int[] uidArray = new int[] { res.pkg.applicationInfo.uid };
- ArrayList<String> pkgList = new ArrayList<String>(1);
- pkgList.add(packageName);
- sendResourcesChangedBroadcast(true, true,
- pkgList,uidArray, null);
- }
- }
- if (res.removedInfo.args != null) {
- // Remove the replaced package's older resources safely now
- deleteOld = true;
- }
-
- // If this app is a browser and it's newly-installed for some
- // users, clear any default-browser state in those users
- if (firstUsers.length > 0) {
- // 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, firstUsers[0])) {
- synchronized (mPackages) {
- for (int userId : firstUsers) {
- mSettings.setDefaultBrowserPackageNameLPw(null, userId);
- }
- }
- }
- }
- // Log current value of "unknown sources" setting
- EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
- getUnknownSourcesSettings());
+ 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;
+
+ // Handle the parent package
+ handlePackagePostInstall(parentRes, grantPermissions, killApp,
+ virtualPreload, grantedPermissions, 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, false /*didRestore*/,
+ args.installerPackageName, args.observer);
}
- // Force a gc to clear up things
- Runtime.getRuntime().gc();
- // We delete after a gc for applications on sdcard.
- if (deleteOld) {
- synchronized (mInstallLock) {
- res.removedInfo.args.doPostDeleteLI(true);
- }
- }
- if (args.observer != null) {
- try {
- Bundle extras = extrasForInstallResult(res);
- args.observer.onPackageInstalled(res.name, res.returnCode,
- res.returnMsg, extras);
- } catch (RemoteException e) {
- Slog.i(TAG, "Observer no longer exists.");
- }
+
+ // Log tracing if needed
+ if (args.traceMethod != null) {
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.traceMethod,
+ args.traceCookie);
}
} else {
Slog.e(TAG, "Bogus post-install token " + msg.arg1);
}
+
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
} break;
case UPDATED_MEDIA_STATUS: {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Got message UPDATED_MEDIA_STATUS");
}
if (reportStatus) {
try {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Invoking MountService call back");
- PackageHelper.getMountService().finishMediaUpdate();
+ if (DEBUG_SD_INSTALL) Log.i(TAG,
+ "Invoking StorageManagerService call back");
+ PackageHelper.getStorageManager().finishMediaUpdate();
} catch (RemoteException e) {
- Log.e(TAG, "MountService not running?");
+ Log.e(TAG, "StorageManagerService not running?");
}
}
} break;
}
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);
int ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
- if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) {
+ 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,
- state.getInstallArgs().getUser());
+ PackageManager.VERIFICATION_ALLOW, user);
try {
ret = args.copyApk(mContainerService, true);
} catch (RemoteException e) {
}
} else {
broadcastPackageVerified(verificationId, originUri,
- PackageManager.VERIFICATION_REJECT,
- state.getInstallArgs().getUser());
+ PackageManager.VERIFICATION_REJECT, user);
}
+ Trace.asyncTraceEnd(
+ TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
+
processPendingInstall(args, ret);
mHandler.sendEmptyMessage(MCS_UNBIND);
}
ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
}
- processPendingInstall(args, ret);
+ Trace.asyncTraceEnd(
+ TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
+ processPendingInstall(args, ret);
mHandler.sendEmptyMessage(MCS_UNBIND);
}
break;
}
+ case INSTANT_APP_RESOLUTION_PHASE_TWO: {
+ InstantAppResolver.doInstantAppResolutionPhaseTwo(mContext,
+ mInstantAppResolverConnection,
+ (InstantAppRequest) msg.obj,
+ mInstantAppInstallerActivity,
+ mHandler);
+ }
}
}
}
- 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
- reconcileUsers(volumeUuid);
- reconcileApps(volumeUuid);
+ private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
+ boolean killApp, boolean virtualPreload, String[] grantedPermissions,
+ boolean launchedForRestore, String installerPackage,
+ IPackageInstallObserver2 installObserver) {
+ if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ // Send the removed broadcasts
+ if (res.removedInfo != null) {
+ res.removedInfo.sendPackageRemovedBroadcasts(killApp);
+ }
- // Clean up any install sessions that expired or were
- // cancelled while this volume was missing
- mInstallerService.onPrivateVolumeMounted(volumeUuid);
+ // 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) {
+ grantRequestedRuntimePermissions(res.pkg, res.newUsers, grantedPermissions);
+ }
- loadPrivatePackages(vol);
+ final boolean update = res.removedInfo != null
+ && res.removedInfo.removedPackage != null;
+ final String installerPackageName =
+ res.installerPackageName != null
+ ? res.installerPackageName
+ : res.removedInfo != null
+ ? res.removedInfo.installerPackageName
+ : null;
- } else if (vol.state == VolumeInfo.STATE_EJECTING) {
- unloadPrivatePackages(vol);
+ // 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) {
+ synchronized (mPackages) {
+ grantRuntimePermissionsGrantedToDisabledPrivSysPackageParentLPw(res.pkg);
}
}
- if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.isPrimary()) {
- if (vol.state == VolumeInfo.STATE_MOUNTED) {
- updateExternalMediaStatus(true, false);
- } else if (vol.state == VolumeInfo.STATE_EJECTING) {
- updateExternalMediaStatus(false, false);
- }
+ synchronized (mPackages) {
+ mInstantAppRegistry.onPackageInstalledLPw(res.pkg, res.newUsers);
}
- }
- @Override
- public void onVolumeForgotten(String fsUuid) {
- if (TextUtils.isEmpty(fsUuid)) {
- Slog.w(TAG, "Forgetting internal storage is probably a mistake; ignoring");
- return;
- }
+ final String packageName = res.pkg.applicationInfo.packageName;
- // Remove any apps installed on the forgotten volume
- synchronized (mPackages) {
- final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(fsUuid);
+ // Determine the set of users who are adding this package for
+ // the first time vs. those who are seeing an update.
+ int[] firstUsers = EMPTY_INT_ARRAY;
+ int[] updateUsers = 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) {
+ if (ps.getInstantApp(newUser)) {
+ continue;
+ }
+ if (allNewUsers) {
+ firstUsers = ArrayUtils.appendInt(firstUsers, newUser);
+ continue;
+ }
+ boolean isNew = true;
+ for (int origUser : res.origUsers) {
+ if (origUser == newUser) {
+ isNew = false;
+ break;
+ }
+ }
+ if (isNew) {
+ firstUsers = ArrayUtils.appendInt(firstUsers, newUser);
+ } else {
+ updateUsers = ArrayUtils.appendInt(updateUsers, 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, firstUsers);
+
+ // 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*/, updateUsers);
+ if (installerPackageName != null) {
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/, updateUsers);
+ }
+
+ // 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*/,
+ updateUsers);
+ if (installerPackageName != null) {
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/, updateUsers);
+ }
+ sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
+ null /*package*/, null /*extras*/, 0 /*flags*/,
+ packageName /*targetPackage*/,
+ null /*finishedReceiver*/, updateUsers);
+ } 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(firstUsers));
+ }
+ sendFirstLaunchBroadcast(packageName, installerPackage, firstUsers);
+ }
+
+ // Send broadcast package appeared if forward locked/external for all users
+ // treat asec-hosted packages like removable media on upgrade
+ if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + res.pkg
+ + " is ASEC-hosted -> AVAILABLE");
+ }
+ final int[] uidArray = new int[]{res.pkg.applicationInfo.uid};
+ ArrayList<String> pkgList = new ArrayList<>(1);
+ pkgList.add(packageName);
+ sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);
+ }
+ }
+
+ // Work that needs to happen on first install within each user
+ if (firstUsers != null && firstUsers.length > 0) {
+ synchronized (mPackages) {
+ for (int userId : firstUsers) {
+ // 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)) {
+ mSettings.setDefaultBrowserPackageNameLPw(null, userId);
+ }
+
+ // We may also need to apply pending (restored) runtime
+ // permission grants within these users.
+ mSettings.applyPendingPermissionGrantsLPw(packageName, userId);
+ }
+ }
+ }
+
+ // Log current value of "unknown sources" setting
+ EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
+ getUnknownSourcesSettings());
+
+ // Remove the replaced package's older resources safely now
+ // We delete after a gc for applications on sdcard.
+ if (res.removedInfo != null && res.removedInfo.args != null) {
+ Runtime.getRuntime().gc();
+ synchronized (mInstallLock) {
+ res.removedInfo.args.doPostDeleteLI(true);
+ }
+ } else {
+ // Force a gc to clear up things. Ask for a background one, it's fine to go on
+ // and not block here.
+ VMRuntime.getRuntime().requestConcurrentGC();
+ }
+
+ // Notify DexManager that the package was installed for new users.
+ // The updated users should already be indexed and the package code paths
+ // should not change.
+ // Don't notify the manager for ephemeral apps as they are not expected to
+ // survive long enough to benefit of background optimizations.
+ for (int userId : firstUsers) {
+ PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);
+ // There's a race currently where some install events may interleave with an uninstall.
+ // This can lead to package info being null (b/36642664).
+ if (info != null) {
+ mDexManager.notifyPackageInstalled(info, userId);
+ }
+ }
+ }
+
+ // If someone is watching installs - notify them
+ if (installObserver != null) {
+ try {
+ Bundle extras = extrasForInstallResult(res);
+ installObserver.onPackageInstalled(res.name, res.returnCode,
+ res.returnMsg, extras);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Observer no longer exists.");
+ }
+ }
+ }
+
+ private void grantRuntimePermissionsGrantedToDisabledPrivSysPackageParentLPw(
+ PackageParser.Package pkg) {
+ if (pkg.parentPackage == null) {
+ return;
+ }
+ if (pkg.requestedPermissions == null) {
+ return;
+ }
+ final PackageSetting disabledSysParentPs = mSettings
+ .getDisabledSystemPkgLPr(pkg.parentPackage.packageName);
+ if (disabledSysParentPs == null || disabledSysParentPs.pkg == null
+ || !disabledSysParentPs.isPrivileged()
+ || (disabledSysParentPs.childPackageNames != null
+ && !disabledSysParentPs.childPackageNames.isEmpty())) {
+ return;
+ }
+ final int[] allUserIds = sUserManager.getUserIds();
+ final int permCount = pkg.requestedPermissions.size();
+ for (int i = 0; i < permCount; i++) {
+ String permission = pkg.requestedPermissions.get(i);
+ BasePermission bp = mSettings.mPermissions.get(permission);
+ if (bp == null || !(bp.isRuntime() || bp.isDevelopment())) {
+ continue;
+ }
+ for (int userId : allUserIds) {
+ if (disabledSysParentPs.getPermissionsState().hasRuntimePermission(
+ permission, userId)) {
+ grantRuntimePermission(pkg.packageName, permission, userId);
+ }
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+
+ if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.isPrimary()) {
+ if (vol.state == VolumeInfo.STATE_MOUNTED) {
+ updateExternalMediaStatus(true, false);
+ } else if (vol.state == VolumeInfo.STATE_EJECTING) {
+ updateExternalMediaStatus(false, false);
+ }
+ }
+ }
+
+ @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");
- deletePackage(ps.name, new LegacyPackageDeleteObserver(null).getBinder(),
- UserHandle.USER_OWNER, PackageManager.DELETE_ALL_USERS);
+ 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);
}
};
- private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int userId,
+ private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
String[] grantedPermissions) {
- if (userId >= UserHandle.USER_OWNER) {
+ for (int userId : userIds) {
grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions);
- } else if (userId == UserHandle.USER_ALL) {
- final int[] userIds;
- synchronized (mPackages) {
- userIds = UserManagerService.getInstance().getUserIds();
- }
- for (int someUserId : userIds) {
- grantRequestedRuntimePermissionsForUser(pkg, someUserId, grantedPermissions);
- }
- }
-
- // We could have touched GID membership, so flush out packages.list
- synchronized (mPackages) {
- mSettings.writePackageListLPr();
}
}
private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId,
String[] grantedPermissions) {
- SettingBase sb = (SettingBase) pkg.mExtras;
- if (sb == null) {
+ PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps == null) {
return;
}
- synchronized (mPackages) {
- for (String permission : pkg.requestedPermissions) {
- BasePermission bp = mSettings.mPermissions.get(permission);
- if (bp != null && (bp.isRuntime() || bp.isDevelopment())
- && (grantedPermissions == null
- || ArrayUtils.contains(grantedPermissions, permission))
- && (getPermissionFlags(permission, pkg.packageName, userId)
- & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) == 0) {
- grantRuntimePermission(pkg.packageName, permission, userId);
+ PermissionsState permissionsState = ps.getPermissionsState();
+
+ final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
+ | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+
+ final boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
+ >= Build.VERSION_CODES.M;
+
+ final boolean instantApp = isInstantApp(pkg.packageName, userId);
+
+ for (String permission : pkg.requestedPermissions) {
+ final BasePermission bp;
+ synchronized (mPackages) {
+ bp = mSettings.mPermissions.get(permission);
+ }
+ if (bp != null && (bp.isRuntime() || bp.isDevelopment())
+ && (!instantApp || bp.isInstant())
+ && (supportsRuntimePermissions || !bp.isRuntimeOnly())
+ && (grantedPermissions == null
+ || ArrayUtils.contains(grantedPermissions, permission))) {
+ final int flags = permissionsState.getPermissionFlags(permission, userId);
+ if (supportsRuntimePermissions) {
+ // Installer cannot change immutable permissions.
+ if ((flags & immutableFlags) == 0) {
+ grantRuntimePermission(pkg.packageName, permission, userId);
+ }
+ } else if (mPermissionReviewRequired) {
+ // In permission review mode we clear the review flag when we
+ // are asked to install the app with all permissions granted.
+ if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ updatePermissionFlags(permission, pkg.packageName,
+ PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, userId);
+ }
}
}
}
}
}
+ 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) {
- if (!sUserManager.exists(userId)) return;
- mDirtyUsers.add(userId);
- if (!mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
- mHandler.sendEmptyMessageDelayed(WRITE_PACKAGE_RESTRICTIONS, WRITE_SETTINGS_DELAY);
+ 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;
}
- static String[] splitString(String str, char sep) {
- int count = 1;
- int i = 0;
- while ((i=str.indexOf(sep, i)) >= 0) {
- count++;
- i++;
+ private void enableSystemUserPackages() {
+ if (!UserManager.isSplitSystemUser()) {
+ return;
}
-
- String[] res = new String[count];
- i=0;
- count = 0;
- int lastI=0;
- while ((i=str.indexOf(sep, i)) >= 0) {
- res[count] = str.substring(lastI, i);
- count++;
- i++;
- lastI = i;
+ // 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);
}
- res[count] = str.substring(lastI, str.length());
- return res;
}
private static void getDefaultDisplayMetrics(Context context, DisplayMetrics metrics) {
displayManager.getDisplay(Display.DEFAULT_DISPLAY).getMetrics(metrics);
}
+ /**
+ * Requests that files preopted on a secondary system partition be copied to the data partition
+ * if possible. Note that the actual copying of the files is accomplished by init for security
+ * reasons. This simply requests that the copy takes place and awaits confirmation of its
+ * completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy.
+ */
+ private static void requestCopyPreoptedFiles() {
+ final int WAIT_TIME_MS = 100;
+ final String CP_PREOPT_PROPERTY = "sys.cppreopt";
+ if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
+ SystemProperties.set(CP_PREOPT_PROPERTY, "requested");
+ // We will wait for up to 100 seconds.
+ final long timeStart = SystemClock.uptimeMillis();
+ final long timeEnd = timeStart + 100 * 1000;
+ long timeNow = timeStart;
+ while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) {
+ try {
+ Thread.sleep(WAIT_TIME_MS);
+ } catch (InterruptedException e) {
+ // Do nothing
+ }
+ timeNow = SystemClock.uptimeMillis();
+ if (timeNow > timeEnd) {
+ SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out");
+ Slog.wtf(TAG, "cppreopt did not finish!");
+ break;
+ }
+ }
+
+ Slog.i(TAG, "cppreopts took " + (timeNow - timeStart) + " ms");
+ }
+ }
+
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());
}
mContext = context;
+
+ mPermissionReviewRequired = context.getResources().getBoolean(
+ R.bool.config_permissionReviewRequired);
+
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
- mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
mSettings = new Settings(mPackages);
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
- // TODO: add a property to control this?
- long dexOptLRUThresholdInMinutes;
- if (mLazyDexOpt) {
- dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds.
- } else {
- dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users.
- }
- mDexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000;
-
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
if ("*".equals(separateProcesses)) {
}
mInstaller = installer;
- mPackageDexOptimizer = new PackageDexOptimizer(this);
+ mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
+ "*dexopt*");
+ mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
getDefaultDisplayMetrics(context, mMetrics);
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "get system config");
SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+ mProtectedPackages = new ProtectedPackages(mContext);
synchronized (mInstallLock) {
// writer
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
+ mProcessLoggingHandler = new ProcessLoggingHandler();
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
+ mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this);
+ mInstantAppRegistry = new InstantAppRegistry(this);
+
File dataDir = Environment.getDataDirectory();
- mAppDataDir = new File(dataDir, "data");
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
- mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
-
sUserManager = new UserManagerService(context, this,
- mInstallLock, mPackages);
+ new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
// Propagate permission configuration in to package manager.
ArrayMap<String, SystemConfig.PermissionEntry> permConfig
}
ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
- for (int i=0; i<libConfig.size(); i++) {
- mSharedLibraries.put(libConfig.keyAt(i),
- new SharedLibraryEntry(libConfig.valueAt(i), null));
+ final int builtInLibCount = libConfig.size();
+ for (int i = 0; i < builtInLibCount; i++) {
+ String name = libConfig.keyAt(i);
+ String path = libConfig.valueAt(i);
+ addSharedLibraryLPw(path, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
+ SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
}
mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
- mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
- mSdkVersion, mOnlyCore);
+ 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 (mFirstBoot) {
+ requestCopyPreoptedFiles();
+ }
String customResolverActivity = Resources.getSystem().getString(
R.string.config_customResolverActivity);
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
startTime);
- // Set flag to monitor and not change apk file paths when
- // scanning install directories.
- final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;
-
- final ArraySet<String> alreadyDexOpted = new ArraySet<String>();
-
- /**
- * Add everything in the in the boot class path to the
- * list of process files because dexopt will have been run
- * if necessary during zygote startup.
- */
final String bootClassPath = System.getenv("BOOTCLASSPATH");
final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
- if (bootClassPath != null) {
- String[] bootClassPathElements = splitString(bootClassPath, ':');
- for (String element : bootClassPathElements) {
- alreadyDexOpted.add(element);
- }
- } else {
+ if (bootClassPath == null) {
Slog.w(TAG, "No BOOTCLASSPATH found!");
}
- if (systemServerClassPath != null) {
- String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');
- for (String element : systemServerClassPathElements) {
- alreadyDexOpted.add(element);
- }
- } else {
+ if (systemServerClassPath == null) {
Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
}
- final List<String> allInstructionSets = InstructionSets.getAllInstructionSets();
- final String[] dexCodeInstructionSets =
- getDexCodeInstructionSets(
- allInstructionSets.toArray(new String[allInstructionSets.size()]));
-
- /**
- * Ensure all external libraries have had dexopt run on them.
- */
- if (mSharedLibraries.size() > 0) {
- // NOTE: For now, we're compiling these system "shared libraries"
- // (and framework jars) into all available architectures. It's possible
- // to compile them only when we come across an app that uses them (there's
- // already logic for that in scanPackageLI) but that adds some complexity.
- for (String dexCodeInstructionSet : dexCodeInstructionSets) {
- for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {
- final String lib = libEntry.path;
- if (lib == null) {
- continue;
- }
-
- try {
- int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);
- if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
- alreadyDexOpted.add(lib);
- mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded, false);
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Library not found: " + lib);
- } catch (IOException e) {
- Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
- + e.getMessage());
- }
- }
- }
- }
-
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
- // Gross hack for now: we know this file doesn't contain any
- // code, so don't dexopt it to avoid the resulting log spew.
- alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");
-
- // Gross hack for now: we know this file is only part of
- // the boot class path for art, so don't dexopt it to
- // avoid the resulting log spew.
- alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
-
- /**
- * There are a number of commands implemented in Java, which
- * we currently need to do the dexopt on so that they can be
- * run from a non-root shell.
- */
- String[] frameworkFiles = frameworkDir.list();
- if (frameworkFiles != null) {
- // TODO: We could compile these only for the most preferred ABI. We should
- // first double check that the dex files for these commands are not referenced
- // by other system apps.
- for (String dexCodeInstructionSet : dexCodeInstructionSets) {
- for (int i=0; i<frameworkFiles.length; i++) {
- File libPath = new File(frameworkDir, frameworkFiles[i]);
- String path = libPath.getPath();
- // Skip the file if we already did it.
- if (alreadyDexOpted.contains(path)) {
- continue;
- }
- // Skip the file if it is not a type we want to dexopt.
- if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
- continue;
- }
- try {
- int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
- if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
- mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded, false);
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Jar not found: " + path);
- } catch (IOException e) {
- Slog.w(TAG, "Exception reading jar: " + path, e);
- }
- }
- }
- }
-
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;
+
// 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) {
}
}
- // Collect vendor overlay packages.
- // (Do this before scanning any apps.)
+ mCacheDir = preparePackageParserCache(mIsUpgrade);
+
+ // 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 overlay packages. (Do this before scanning any apps.)
// For security and version matching reason, only consider
- // overlay packages if they reside in VENDOR_OVERLAY_DIR.
- File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
- scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
+ // overlay packages if they reside in the right directory.
+ scanDirTracedLI(new File(VENDOR_OVERLAY_DIR), mDefParseFlags
+ | PackageParser.PARSE_IS_SYSTEM
+ | PackageParser.PARSE_IS_SYSTEM_DIR
+ | PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
+
+ mParallelPackageParserCallback.findStaticOverlayPackages();
// Find base frameworks (resource packages without code).
- scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
+ scanDirTracedLI(frameworkDir, mDefParseFlags
+ | PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);
// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
- scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
+ scanDirTracedLI(privilegedAppDir, mDefParseFlags
+ | PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
- scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
+ scanDirTracedLI(systemAppDir, mDefParseFlags
+ | PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all vendor packages.
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
- scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
+ scanDirTracedLI(vendorAppDir, mDefParseFlags
+ | PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
- scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
+ scanDirTracedLI(oemAppDir, mDefParseFlags
+ | PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
- if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
- mInstaller.moveFiles();
-
// Prune any system packages that no longer exist.
- final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
+ 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) {
- Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
+ // 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();
+ ps.codePathString + ", installStatus=" + ps.installStatus
+ ", versionCode=" + ps.versionCode + "; scanned versionCode="
+ scannedPkg.mVersionCode);
- removePackageLI(ps, true);
+ removePackageLI(scannedPkg, true);
mExpectingBetter.put(ps.name, ps.codePath);
}
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
psit.remove();
logCriticalInfo(Log.WARN, "System package " + ps.name
- + " no longer exists; wiping its data");
- removeDataDirsLI(null, 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 {
- final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
- if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
+ // 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);
}
}
//look for any incomplete package installations
ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
- //clean up list
- for(int i = 0; i < deletePkgsList.size(); i++) {
- //clean up here
- cleanupInstallFailedPackage(deletePkgsList.get(i));
+ for (int i = 0; i < deletePkgsList.size(); i++) {
+ // Actual deletion of code and data will be handled by later
+ // reconciliation step
+ final String packageName = deletePkgsList.get(i).name;
+ logCriticalInfo(Log.WARN, "Cleaning up incompletely installed app: " + packageName);
+ synchronized (mPackages) {
+ mSettings.removePackageLPw(packageName);
+ }
}
+
//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());
- scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
+ scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
- scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
+ scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags
+ | PackageParser.PARSE_FORWARD_LOCK,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
- /**
- * Remove disable package settings for any updated system
- * apps that were removed via an OTA. If they're not a
- * previously-updated app, remove them completely.
- * Otherwise, just revoke their system-level permissions.
- */
+ // Remove disable package settings for updated system apps that were
+ // removed via an OTA. If the update is no longer present, remove the
+ // app completely. Otherwise, revoke their system privileges.
for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
- String msg;
+ final String msg;
if (deletedPkg == null) {
+ // should have found an update, but, we didn't; remove everything
msg = "Updated system package " + deletedAppName
- + " no longer exists; wiping its data";
- removeDataDirsLI(null, deletedAppName);
+ + " no longer exists; removing its data";
+ // Actual deletion of code and data will be handled by later
+ // reconciliation step
} else {
- msg = "Updated system app + " + deletedAppName
- + " no longer present; removing system privileges for "
- + deletedAppName;
+ // found an update; revoke system privileges
+ msg = "Updated system package + " + deletedAppName
+ + " no longer exists; revoking system privileges";
- deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
+ // Don't do anything if a stub is removed from the system image. If
+ // we were to remove the uncompressed version from the /data partition,
+ // this is where it'd be done.
- PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
+ final PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
+ deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
}
logCriticalInfo(Log.WARN, msg);
}
- /**
+ /*
* Make sure all system apps that we expected to appear on
* the userdata partition actually showed up. If they never
* appeared, crawl back and revive the system version.
logCriticalInfo(Log.WARN, "Expected better " + packageName
+ " but never showed up; reverting to system");
- final int reparseFlags;
+ int reparseFlags = mDefParseFlags;
if (FileUtils.contains(privilegedAppDir, scanFile)) {
reparseFlags = PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
mSettings.enableSystemPackageLPw(packageName);
try {
- scanPackageLI(scanFile, reparseFlags, scanFlags, 0, null);
+ scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse original system package: "
+ e.getMessage());
}
}
}
+
+ // Uncompress and install any stubbed system applications.
+ // This must be done last to ensure all stubs are replaced or disabled.
+ decompressSystemApplications(stubSystemApps, scanFlags);
+
+ final int cachedNonSystemApps = PackageParser.sCachedPackageReadCount.get()
+ - cachedSystemApps;
+
+ final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
+ final int dataPackagesCount = mPackages.size() - systemPackagesCount;
+ Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
+ + " ms, packageCount: " + dataPackagesCount
+ + " , timePerPackage: "
+ + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
+ + " , cached: " + cachedNonSystemApps);
+ if (mIsUpgrade && dataPackagesCount > 0) {
+ MetricsLogger.histogram(null, "ota_package_manager_data_app_avg_scan_time",
+ ((int) dataScanTime) / dataPackagesCount);
+ }
}
mExpectingBetter.clear();
+ // Resolve the storage manager.
+ mStorageManagerPackage = getStorageManagerPackageName();
+
+ // Resolve protected action filters. Only the setup wizard is allowed to
+ // have a high priority filter for these actions.
+ mSetupWizardPackage = getSetupWizardPackageName();
+ if (mProtectedFilters.size() > 0) {
+ if (DEBUG_FILTERS && mSetupWizardPackage == null) {
+ Slog.i(TAG, "No setup wizard;"
+ + " All protected intents capped to priority 0");
+ }
+ for (ActivityIntentInfo filter : mProtectedFilters) {
+ if (filter.activity.info.packageName.equals(mSetupWizardPackage)) {
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Found setup wizard;"
+ + " allow priority " + filter.getPriority() + ";"
+ + " package: " + filter.activity.info.packageName
+ + " activity: " + filter.activity.className
+ + " priority: " + filter.getPriority());
+ }
+ // skip setup wizard; allow it to keep the high priority filter
+ continue;
+ }
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Protected action; cap priority to 0;"
+ + " package: " + filter.activity.info.packageName
+ + " activity: " + filter.activity.className
+ + " origPrio: " + filter.getPriority());
+ }
+ filter.setPriority(0);
+ }
+ }
+ mDeferProtectedFilters = false;
+ mProtectedFilters.clear();
+
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
- updateAllSharedLibrariesLPw();
+ updateAllSharedLibrariesLPw(null);
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.
- adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,
- false /* force dexopt */, false /* defer dexopt */,
- false /* boot complete */);
+ adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/);
}
// Now that we know all the packages we are keeping,
// read and update their last usage times.
- mPackageUsage.readLP();
+ mPackageUsage.read(mPackages);
+ mCompilerStats.read();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
// 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 || !mRestoredSettings)) {
+ if (!onlyCore && (mPromoteSystemApps || mFirstBoot)) {
for (UserInfo user : sUserManager.getUsers(true)) {
mSettings.applyDefaultPreferredAppsLPw(this, user.id);
applyFactoryDefaultBrowserLPw(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)) {
- deleteCodeCacheDirsLI(ps.volumeUuid, ps.name);
+ // No apps are running this early, so no need to freeze
+ clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
+ StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE
+ | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
}
ver.fingerprint = Build.FINGERPRINT;
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());
- mRequiredVerifierPackage = getRequiredVerifierLPr();
- mRequiredInstallerPackage = getRequiredInstallerLPr();
-
- mInstallerService = new PackageInstallerService(context, this);
-
- mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
- mIntentFilterVerifier = new IntentVerifierProxy(mContext,
- mIntentFilterVerifierComponent);
+ 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;
+ }
+ mInstallerService = new PackageInstallerService(context, this);
+ final Pair<ComponentName, String> instantAppResolverComponent =
+ getInstantAppResolverLPr();
+ if (instantAppResolverComponent != null) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent);
+ }
+ mInstantAppResolverConnection = new EphemeralResolverConnection(
+ 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<>();
+ final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
+ for (int userId : currentUserIds) {
+ 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)
// 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);
+
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "loadFallbacks");
+ FallbackCategoryProvider.loadFallbacks();
+ 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);
// Expose private service for system components to use.
LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ /**
+ * Uncompress and install stub applications.
+ * <p>In order to save space on the system partition, some applications are shipped in a
+ * compressed form. In addition the compressed bits for the full application, the
+ * system image contains a tiny stub comprised of only the Android manifest.
+ * <p>During the first boot, attempt to uncompress and install the full application. If
+ * the application can't be installed for any reason, disable the stub and prevent
+ * uncompressing the full application during future boots.
+ * <p>In order to forcefully attempt an installation of a full application, go to app
+ * settings and enable the application.
+ */
+ private void decompressSystemApplications(@NonNull List<String> stubSystemApps, int scanFlags) {
+ for (int i = stubSystemApps.size() - 1; i >= 0; --i) {
+ final String pkgName = stubSystemApps.get(i);
+ // skip if the system package is already disabled
+ if (mSettings.isDisabledSystemPackageLPr(pkgName)) {
+ stubSystemApps.remove(i);
+ continue;
+ }
+ // skip if the package isn't installed (?!); this should never happen
+ final PackageParser.Package pkg = mPackages.get(pkgName);
+ if (pkg == null) {
+ stubSystemApps.remove(i);
+ continue;
+ }
+ // skip if the package has been disabled by the user
+ final PackageSetting ps = mSettings.mPackages.get(pkgName);
+ if (ps != null) {
+ final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM);
+ if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ stubSystemApps.remove(i);
+ continue;
+ }
+ }
+
+ if (DEBUG_COMPRESSION) {
+ Slog.i(TAG, "Uncompressing system stub; pkg: " + pkgName);
+ }
+
+ // uncompress the binary to its eventual destination on /data
+ final File scanFile = decompressPackage(pkg);
+ if (scanFile == null) {
+ continue;
+ }
+
+ // install the package to replace the stub on /system
+ try {
+ mSettings.disableSystemPackageLPw(pkgName, true /*replaced*/);
+ removePackageLI(pkg, true /*chatty*/);
+ scanPackageTracedLI(scanFile, 0 /*reparseFlags*/, scanFlags, 0, null);
+ ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+ UserHandle.USER_SYSTEM, "android");
+ stubSystemApps.remove(i);
+ continue;
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage());
+ }
+
+ // any failed attempt to install the package will be cleaned up later
+ }
+
+ // disable any stub still left; these failed to install the full application
+ for (int i = stubSystemApps.size() - 1; i >= 0; --i) {
+ final String pkgName = stubSystemApps.get(i);
+ final PackageSetting ps = mSettings.mPackages.get(pkgName);
+ ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ UserHandle.USER_SYSTEM, "android");
+ logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName);
+ }
+ }
+
+ private int decompressFile(File srcFile, File dstFile) throws ErrnoException {
+ if (DEBUG_COMPRESSION) {
+ Slog.i(TAG, "Decompress file"
+ + "; src: " + srcFile.getAbsolutePath()
+ + ", dst: " + dstFile.getAbsolutePath());
+ }
+ try (
+ InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile));
+ OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/);
+ ) {
+ Streams.copy(fileIn, fileOut);
+ Os.chmod(dstFile.getAbsolutePath(), 0644);
+ return PackageManager.INSTALL_SUCCEEDED;
+ } catch (IOException e) {
+ logCriticalInfo(Log.ERROR, "Failed to decompress file"
+ + "; src: " + srcFile.getAbsolutePath()
+ + ", dst: " + dstFile.getAbsolutePath());
+ }
+ return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ }
+
+ private File[] getCompressedFiles(String codePath) {
+ final File stubCodePath = new File(codePath);
+ final String stubName = stubCodePath.getName();
+
+ // The layout of a compressed package on a given partition is as follows :
+ //
+ // Compressed artifacts:
+ //
+ // /partition/ModuleName/foo.gz
+ // /partation/ModuleName/bar.gz
+ //
+ // Stub artifact:
+ //
+ // /partition/ModuleName-Stub/ModuleName-Stub.apk
+ //
+ // In other words, stub is on the same partition as the compressed artifacts
+ // and in a directory that's suffixed with "-Stub".
+ int idx = stubName.lastIndexOf(STUB_SUFFIX);
+ if (idx < 0 || (stubName.length() != (idx + STUB_SUFFIX.length()))) {
+ return null;
+ }
+
+ final File stubParentDir = stubCodePath.getParentFile();
+ if (stubParentDir == null) {
+ Slog.e(TAG, "Unable to determine stub parent dir for codePath: " + codePath);
+ return null;
+ }
+
+ final File compressedPath = new File(stubParentDir, stubName.substring(0, idx));
+ final File[] files = compressedPath.listFiles(new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.toLowerCase().endsWith(COMPRESSED_EXTENSION);
+ }
+ });
+
+ if (DEBUG_COMPRESSION && files != null && files.length > 0) {
+ Slog.i(TAG, "getCompressedFiles[" + codePath + "]: " + Arrays.toString(files));
+ }
+
+ return files;
+ }
+
+ private boolean compressedFileExists(String codePath) {
+ final File[] compressedFiles = getCompressedFiles(codePath);
+ return compressedFiles != null && compressedFiles.length > 0;
+ }
+
+ /**
+ * Decompresses the given package on the system image onto
+ * the /data partition.
+ * @return The directory the package was decompressed into. Otherwise, {@code null}.
+ */
+ private File decompressPackage(PackageParser.Package pkg) {
+ final File[] compressedFiles = getCompressedFiles(pkg.codePath);
+ if (compressedFiles == null || compressedFiles.length == 0) {
+ if (DEBUG_COMPRESSION) {
+ Slog.i(TAG, "No files to decompress: " + pkg.baseCodePath);
+ }
+ return null;
+ }
+ final File dstCodePath =
+ getNextCodePath(Environment.getDataAppDirectory(null), pkg.packageName);
+ int ret = PackageManager.INSTALL_SUCCEEDED;
+ try {
+ Os.mkdir(dstCodePath.getAbsolutePath(), 0755);
+ Os.chmod(dstCodePath.getAbsolutePath(), 0755);
+ for (File srcFile : compressedFiles) {
+ final String srcFileName = srcFile.getName();
+ final String dstFileName = srcFileName.substring(
+ 0, srcFileName.length() - COMPRESSED_EXTENSION.length());
+ final File dstFile = new File(dstCodePath, dstFileName);
+ ret = decompressFile(srcFile, dstFile);
+ if (ret != PackageManager.INSTALL_SUCCEEDED) {
+ logCriticalInfo(Log.ERROR, "Failed to decompress"
+ + "; pkg: " + pkg.packageName
+ + ", file: " + dstFileName);
+ break;
+ }
+ }
+ } catch (ErrnoException e) {
+ logCriticalInfo(Log.ERROR, "Failed to decompress"
+ + "; pkg: " + pkg.packageName
+ + ", err: " + e.errno);
+ }
+ if (ret == PackageManager.INSTALL_SUCCEEDED) {
+ final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
+ NativeLibraryHelper.Handle handle = null;
+ try {
+ handle = NativeLibraryHelper.Handle.create(dstCodePath);
+ ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
+ null /*abiOverride*/);
+ } catch (IOException e) {
+ logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
+ + "; pkg: " + pkg.packageName);
+ ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ } finally {
+ IoUtils.closeQuietly(handle);
+ }
+ }
+ if (ret != PackageManager.INSTALL_SUCCEEDED) {
+ if (dstCodePath == null || !dstCodePath.exists()) {
+ return null;
+ }
+ removeCodePathLI(dstCodePath);
+ return null;
+ }
+
+ return dstCodePath;
+ }
+
+ 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 File preparePackageParserCache(boolean isUpgrade) {
+ 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 = FileUtils.createDir(Environment.getDataSystemDirectory(),
+ "package_cache");
+ if (cacheBaseDir == null) {
+ return null;
+ }
+
+ // If this is a system upgrade scenario, delete the contents of the package cache dir.
+ // This also serves to "GC" unused entries when the package cache version changes (which
+ // can only happen during upgrades).
+ if (isUpgrade) {
+ FileUtils.deleteContents(cacheBaseDir);
+ }
+
+
+ // Return the versioned package cache directory. This is something like
+ // "/data/system/package_cache/1"
+ File cacheDir = FileUtils.createDir(cacheBaseDir, PACKAGE_PARSER_CACHE_VERSION);
+
+ // 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, PACKAGE_PARSER_CACHE_VERSION);
+ }
+ }
+
+ return cacheDir;
}
@Override
public boolean isFirstBoot() {
- return !mRestoredSettings;
+ // allow instant applications
+ return mFirstBoot;
}
@Override
public boolean isOnlyCoreApps() {
+ // allow instant applications
return mOnlyCore;
}
@Override
public boolean isUpgrade() {
+ // allow instant applications
return mIsUpgrade;
}
- private String getRequiredVerifierLPr() {
- final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
- final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE,
- PackageManager.GET_DISABLED_COMPONENTS, 0 /* TODO: Which userId? */);
+ private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() {
+ final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
- String requiredVerifier = null;
-
- final int N = receivers.size();
- for (int i = 0; i < N; i++) {
- final ResolveInfo info = receivers.get(i);
+ 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);
+ }
- if (info.activityInfo == null) {
- continue;
+ private @NonNull String getRequiredSharedLibraryLPr(String name, int version) {
+ synchronized (mPackages) {
+ SharedLibraryEntry libraryEntry = getSharedLibraryEntryLPr(name, version);
+ if (libraryEntry == null) {
+ throw new IllegalStateException("Missing required shared library:" + name);
}
+ return libraryEntry.apk;
+ }
+ }
- final String packageName = info.activityInfo.packageName;
-
- if (checkPermission(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
- packageName, UserHandle.USER_OWNER) != PackageManager.PERMISSION_GRANTED) {
- continue;
- }
+ private @NonNull String getRequiredInstallerLPr() {
+ final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
- if (requiredVerifier != null) {
- throw new RuntimeException("There can be only one required verifier");
+ 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");
}
-
- requiredVerifier = packageName;
+ return matches.get(0).getComponentInfo().packageName;
+ } else {
+ throw new RuntimeException("There must be exactly one installer; found " + matches);
}
-
- return requiredVerifier;
}
- private String getRequiredInstallerLPr() {
- Intent installerIntent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
- installerIntent.addCategory(Intent.CATEGORY_DEFAULT);
- installerIntent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
+ 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 List<ResolveInfo> installers = queryIntentActivities(installerIntent,
- PACKAGE_MIME_TYPE, 0, 0);
+ 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;
+ }
- String requiredInstaller = null;
+ private @NonNull ComponentName getIntentFilterVerifierComponentNameLPr() {
+ final Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
- final int N = installers.size();
+ 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 info = installers.get(i);
- final String packageName = info.activityInfo.packageName;
-
- if (!info.activityInfo.applicationInfo.isSystemApp()) {
+ 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 (requiredInstaller != null) {
- throw new RuntimeException("There must be one required installer");
+ if (best == null || cur.priority > best.priority) {
+ best = cur;
}
-
- requiredInstaller = packageName;
}
- if (requiredInstaller == null) {
- throw new RuntimeException("There must be one required installer");
+ if (best != null) {
+ return best.getComponentInfo().getComponentName();
}
+ Slog.w(TAG, "Intent filter verifier not found");
+ return null;
+ }
- return requiredInstaller;
+ @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 ComponentName getIntentFilterVerifierComponentNameLPr() {
- final Intent verification = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
- final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE,
- PackageManager.GET_DISABLED_COMPONENTS, 0 /* userId */);
+ 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_EPHEMERAL) {
+ Slog.d(TAG, "Ephemeral resolver NOT found; empty package list");
+ }
+ return null;
+ }
- ComponentName verifierComponentName = 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*/);
+ // temporarily look for the old action
+ if (resolvers.size() == 0) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Ephemeral resolver not found with new action; try old one");
+ }
+ actionName = Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE;
+ resolverIntent.setAction(actionName);
+ resolvers = queryIntentServicesInternal(resolverIntent, null,
+ resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
+ }
+ final int N = resolvers.size();
+ if (N == 0) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Ephemeral resolver NOT found; no matching intent filters");
+ }
+ return null;
+ }
- int priority = -1000;
- final int N = receivers.size();
+ final Set<String> possiblePackages = new ArraySet<>(Arrays.asList(packageArray));
for (int i = 0; i < N; i++) {
- final ResolveInfo info = receivers.get(i);
+ final ResolveInfo info = resolvers.get(i);
- if (info.activityInfo == null) {
+ if (info.serviceInfo == null) {
continue;
}
- final String packageName = info.activityInfo.packageName;
-
- final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
+ final String packageName = info.serviceInfo.packageName;
+ if (!possiblePackages.contains(packageName) && !Build.IS_DEBUGGABLE) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Ephemeral resolver not in allowed package list;"
+ + " pkg: " + packageName + ", info:" + info);
+ }
continue;
}
- if (checkPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
- packageName, UserHandle.USER_OWNER) != PackageManager.PERMISSION_GRANTED) {
- continue;
+ if (DEBUG_EPHEMERAL) {
+ Slog.v(TAG, "Ephemeral resolver found;"
+ + " pkg: " + packageName + ", info:" + info);
}
+ return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName);
+ }
+ if (DEBUG_EPHEMERAL) {
+ Slog.v(TAG, "Ephemeral resolver NOT found");
+ }
+ return null;
+ }
- // Select the IntentFilterVerifier with the highest priority
- if (priority < info.priority) {
- priority = info.priority;
- verifierComponentName = new ComponentName(packageName, info.activityInfo.name);
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Selecting IntentFilterVerifier: "
- + verifierComponentName + " with priority: " + info.priority);
+ private @Nullable ActivityInfo getInstantAppInstallerLPr() {
+ final Intent intent = new Intent(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
+
+ final int resolveFlags =
+ MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_UNAWARE
+ | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
+ List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
+ resolveFlags, UserHandle.USER_SYSTEM);
+ // temporarily look for the old action
+ if (matches.isEmpty()) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Ephemeral installer not found with new action; try old one");
+ }
+ intent.setAction(Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE);
+ matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
+ resolveFlags, UserHandle.USER_SYSTEM);
+ }
+ Iterator<ResolveInfo> iter = matches.iterator();
+ while (iter.hasNext()) {
+ final ResolveInfo rInfo = iter.next();
+ final PackageSetting ps = mSettings.mPackages.get(rInfo.activityInfo.packageName);
+ if (ps != null) {
+ final PermissionsState permissionsState = ps.getPermissionsState();
+ if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0)) {
+ 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);
}
+ }
- return verifierComponentName;
+ 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);
+ // temporarily look for the old action
+ if (matches.isEmpty()) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Ephemeral resolver settings not found with new action; try old one");
+ }
+ intent.setAction(Intent.ACTION_EPHEMERAL_RESOLVER_SETTINGS);
+ matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
+ UserHandle.USER_SYSTEM);
+ }
+ if (matches.isEmpty()) {
+ return null;
+ }
+ return matches.get(0).getComponentInfo().getComponentName();
}
private void primeDomainVerificationsLPw(int userId) {
SystemConfig systemConfig = SystemConfig.getInstance();
ArraySet<String> packages = systemConfig.getLinkedApps();
- ArraySet<String> domains = new ArraySet<String>();
for (String packageName : packages) {
PackageParser.Package pkg = mPackages.get(packageName);
continue;
}
- domains.clear();
+ ArraySet<String> domains = null;
for (PackageParser.Activity a : pkg.activities) {
for (ActivityIntentInfo filter : a.intents) {
if (hasValidDomains(filter)) {
+ if (domains == null) {
+ domains = new ArraySet<String>();
+ }
domains.addAll(filter.getHostsList());
}
}
}
- if (domains.size() > 0) {
+ if (domains != null && domains.size() > 0) {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.v(TAG, " + " + packageName);
}
// 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,
- new ArrayList<String>(domains));
+ ivi = mSettings.createIntentFilterVerificationIfNeededLPw(packageName, domains);
ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
mSettings.updateIntentFilterVerificationStatusLPw(packageName,
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, userId);
+ "' does not handle web links");
}
} else {
- Slog.w(TAG, "Unknown package '" + packageName + "' in sysconfig <app-link>");
+ Slog.w(TAG, "Unknown package " + packageName + " in sysconfig <app-link>");
}
}
private List<String> resolveAllBrowserApps(int userId) {
// Resolve the canonical browser intent and check that the handleAllWebDataURI boolean is set
- List<ResolveInfo> list = queryIntentActivities(sBrowserIntent, null,
+ List<ResolveInfo> list = queryIntentActivitiesInternal(sBrowserIntent, null,
PackageManager.MATCH_ALL, userId);
final int count = list.size();
}
private boolean packageIsBrowser(String packageName, int userId) {
- List<ResolveInfo> list = queryIntentActivities(sBrowserIntent, null,
+ List<ResolveInfo> list = queryIntentActivitiesInternal(sBrowserIntent, null,
PackageManager.MATCH_ALL, userId);
final int N = list.size();
for (int i = 0; i < N; i++) {
}
}
- void cleanupInstallFailedPackage(PackageSetting ps) {
- logCriticalInfo(Log.WARN, "Cleaning up incompletely installed app: " + ps.name);
-
- removeDataDirsLI(ps.volumeUuid, ps.name);
- if (ps.codePath != null) {
- if (ps.codePath.isDirectory()) {
- mInstaller.rmPackageDir(ps.codePath.getAbsolutePath());
- } else {
- ps.codePath.delete();
- }
- }
- if (ps.resourcePath != null && !ps.resourcePath.equals(ps.codePath)) {
- if (ps.resourcePath.isDirectory()) {
- FileUtils.deleteContents(ps.resourcePath);
- }
- ps.resourcePath.delete();
- }
- mSettings.removePackageLPw(ps.name);
- }
-
static int[] appendInts(int[] cur, int[] add) {
if (add == null) return cur;
if (cur == null) return add;
return cur;
}
- PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) {
+ /**
+ * Returns whether or not a full application can see an instant application.
+ * <p>
+ * Currently, there are three 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>
+ * </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;
+ }
+ }
+ return false;
+ }
+
+ private PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
- final PackageSetting ps = (PackageSetting) p.mExtras;
if (ps == null) {
return null;
}
+ PackageParser.Package p = ps.pkg;
+ if (p == 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;
+ }
final PermissionsState permissionsState = ps.getPermissionsState();
- final int[] gids = permissionsState.computeGids(userId);
- final Set<String> permissions = permissionsState.getPermissions(userId);
+ // Compute GIDs only if requested
+ final int[] gids = (flags & PackageManager.GET_GIDS) == 0
+ ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
+ // Compute granted permissions only if package has requested permissions
+ final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions)
+ ? Collections.<String>emptySet() : permissionsState.getPermissions(userId);
final PackageUserState state = ps.readUserState(userId);
- return PackageParser.generatePackageInfo(p, gids, flags,
+ if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0
+ && ps.isSystem()) {
+ flags |= MATCH_ANY_USER;
+ }
+
+ 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;
}
@Override
- public boolean isPackageFrozen(String packageName) {
+ 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) {
- return ps.frozen;
+ 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!");
}
}
- Slog.w(TAG, "Package " + packageName + " is missing; assuming frozen");
- return true;
}
@Override
public boolean isPackageAvailable(String packageName, int userId) {
if (!sUserManager.exists(userId)) return false;
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "is package available");
+ final int callingUid = Binder.getCallingUid();
+ 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) {
@Override
public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
- if (!sUserManager.exists(userId)) return null;
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get package info");
- // reader
- synchronized (mPackages) {
- PackageParser.Package p = mPackages.get(packageName);
- if (DEBUG_PACKAGE_INFO)
- Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
- if (p != null) {
- return generatePackageInfo(p, flags, userId);
- }
- if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
- return generatePackageInfoFromSettingsLPw(packageName, flags, userId);
- }
- }
- return null;
+ return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
+ flags, Binder.getCallingUid(), userId);
}
@Override
- public String[] currentToCanonicalPackageNames(String[] names) {
- String[] out = new String[names.length];
- // reader
- synchronized (mPackages) {
- for (int i=names.length-1; i>=0; i--) {
- PackageSetting ps = mSettings.mPackages.get(names[i]);
- out[i] = ps != null && ps.realName != null ? ps.realName : names[i];
- }
- }
- return out;
+ public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage,
+ int flags, int userId) {
+ return getPackageInfoInternal(versionedPackage.getPackageName(),
+ versionedPackage.getVersionCode(), 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, int versionCode,
+ int flags, int filterCallingUid, int userId) {
+ if (!sUserManager.exists(userId)) return null;
+ flags = updateFlagsForPackage(flags, userId, packageName);
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ false /* requireFullPermission */, false /* checkShell */, "get package info");
+
+ // reader
+ synchronized (mPackages) {
+ // Normalize package name to handle renamed packages and static libs
+ packageName = resolveInternalPackageNameLPr(packageName, versionCode);
+
+ final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
+ if (matchFactoryOnly) {
+ final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
+ if (ps != null) {
+ if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
+ return null;
+ }
+ if (filterAppAccessLPr(ps, filterCallingUid, userId)) {
+ return null;
+ }
+ return generatePackageInfo(ps, flags, userId);
+ }
+ }
+
+ 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);
+ }
+ }
+ 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 = mActivities.mActivities.get(component);
+ return activity != null
+ ? (activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0
+ : false;
+ } else if (type == TYPE_RECEIVER) {
+ final PackageParser.Activity activity = mReceivers.mActivities.get(component);
+ return activity != null
+ ? (activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0
+ : false;
+ } else if (type == TYPE_SERVICE) {
+ final PackageParser.Service service = mServices.mServices.get(component);
+ return service != null
+ ? (service.info.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0
+ : false;
+ } else if (type == TYPE_PROVIDER) {
+ final PackageParser.Provider provider = mProviders.mProviders.get(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 #canAccessInstantApps(int)
+ */
+ 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) {
+ // request for a specific component; if it hasn't been explicitly exposed, filter
+ if (component != null) {
+ return !isComponentVisibleToInstantApp(component, componentType);
+ }
+ // request for application; if no components have been explicitly exposed, filter
+ return ps.getInstantApp(userId) || !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, boolean, int)
+ */
+ private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid, int userId) {
+ return filterAppAccessLPr(ps, callingUid, null, TYPE_UNKNOWN, userId);
+ }
+
+ 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;
+ }
+ }
+
+ // 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 SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(ps.pkg.staticSharedLibName,
+ ps.pkg.staticSharedLibVersion);
+ if (libEntry == 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,
+ libEntry.info.getName());
+ if (index < 0) {
+ continue;
+ }
+ if (uidPs.pkg.usesStaticLibrariesVersions[index] == libEntry.info.getVersion()) {
+ return false;
+ }
+ }
+ }
+ return true;
}
@Override
- public String[] canonicalToCurrentPackageNames(String[] names) {
- String[] out = new String[names.length];
+ 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--) {
- String cur = mSettings.mRenamedPackages.get(names[i]);
- out[i] = cur != null ? cur : names[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 int getPackageUid(String packageName, int userId) {
- return getPackageUidEtc(packageName, 0, userId);
+ 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 getPackageUidEtc(String packageName, int flags, int userId) {
+ public int getPackageUid(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return -1;
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get package uid");
+ final int callingUid = Binder.getCallingUid();
+ flags = updateFlagsForPackage(flags, userId, packageName);
+ enforceCrossUserPermission(callingUid, userId,
+ false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid");
// reader
synchronized (mPackages) {
final PackageParser.Package p = mPackages.get(packageName);
- if (p != null) {
+ 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 & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
+ if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps != null) {
+ if (ps != null && ps.isMatch(flags)
+ && !filterAppAccessLPr(ps, callingUid, userId)) {
return UserHandle.getUid(userId, ps.appId);
}
}
}
@Override
- public int[] getPackageGids(String packageName, int userId) {
- return getPackageGidsEtc(packageName, 0, userId);
- }
-
- @Override
- public int[] getPackageGidsEtc(String packageName, int flags, int userId) {
- if (!sUserManager.exists(userId)) {
- return null;
- }
-
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
- "getPackageGids");
+ 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);
+ enforceCrossUserPermission(callingUid, userId,
+ false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids");
// reader
synchronized (mPackages) {
final PackageParser.Package p = mPackages.get(packageName);
- if (p != null) {
+ 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 & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
+ if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps != null) {
+ if (ps != null && ps.isMatch(flags)
+ && !filterAppAccessLPr(ps, callingUid, userId)) {
return ps.getPermissionsState().computeGids(userId);
}
}
return null;
}
- static PermissionInfo generatePermissionInfo(
- BasePermission bp, int flags) {
+ static PermissionInfo generatePermissionInfo(BasePermission bp, int flags) {
if (bp.perm != null) {
return PackageParser.generatePermissionInfo(bp.perm, flags);
}
}
@Override
- public PermissionInfo getPermissionInfo(String name, int flags) {
+ public PermissionInfo getPermissionInfo(String name, String packageName, int flags) {
+ final int callingUid = Binder.getCallingUid();
+ if (getInstantAppPackageName(callingUid) != null) {
+ return null;
+ }
// reader
synchronized (mPackages) {
final BasePermission p = mSettings.mPermissions.get(name);
- if (p != null) {
- return generatePermissionInfo(p, flags);
+ if (p == null) {
+ return null;
}
- return null;
+ // If the caller is an app that targets pre 26 SDK drop protection flags.
+ PermissionInfo permissionInfo = generatePermissionInfo(p, flags);
+ if (permissionInfo != null) {
+ final int protectionLevel = adjustPermissionProtectionFlagsLPr(
+ permissionInfo.protectionLevel, packageName, callingUid);
+ if (permissionInfo.protectionLevel != protectionLevel) {
+ // If we return different protection level, don't use the cached info
+ if (p.perm != null && p.perm.info == permissionInfo) {
+ permissionInfo = new PermissionInfo(permissionInfo);
+ }
+ permissionInfo.protectionLevel = protectionLevel;
+ }
+ }
+ return permissionInfo;
+ }
+ }
+
+ private int adjustPermissionProtectionFlagsLPr(int protectionLevel,
+ String packageName, int uid) {
+ // Signature permission flags area always reported
+ final int protectionLevelMasked = protectionLevel
+ & (PermissionInfo.PROTECTION_NORMAL
+ | PermissionInfo.PROTECTION_DANGEROUS
+ | PermissionInfo.PROTECTION_SIGNATURE);
+ if (protectionLevelMasked == PermissionInfo.PROTECTION_SIGNATURE) {
+ return protectionLevel;
+ }
+
+ // System sees all flags.
+ final int appId = UserHandle.getAppId(uid);
+ if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID
+ || appId == Process.SHELL_UID) {
+ return protectionLevel;
+ }
+
+ // Normalize package name to handle renamed packages and static libs
+ packageName = resolveInternalPackageNameLPr(packageName,
+ PackageManager.VERSION_CODE_HIGHEST);
+
+ // Apps that target O see flags for all protection levels.
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps == null) {
+ return protectionLevel;
}
+ if (ps.appId != appId) {
+ return protectionLevel;
+ }
+
+ final PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ return protectionLevel;
+ }
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
+ return protectionLevelMasked;
+ }
+
+ return protectionLevel;
}
@Override
- public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) {
+ public @Nullable ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String group,
+ int flags) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return null;
+ }
// reader
synchronized (mPackages) {
+ if (group != null && !mPermissionGroups.containsKey(group)) {
+ // This is thrown as NameNotFoundException
+ return null;
+ }
+
ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
for (BasePermission p : mSettings.mPermissions.values()) {
if (group == null) {
}
}
}
-
- if (out.size() > 0) {
- return out;
- }
- return mPermissionGroups.containsKey(group) ? out : null;
+ return new ParceledListSlice<>(out);
}
}
@Override
public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return null;
+ }
// reader
synchronized (mPackages) {
return PackageParser.generatePermissionGroupInfo(
}
@Override
- public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
+ public @NonNull ParceledListSlice<PermissionGroupInfo> getAllPermissionGroups(int flags) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return ParceledListSlice.emptyList();
+ }
// reader
synchronized (mPackages) {
final int N = mPermissionGroups.size();
for (PackageParser.PermissionGroup pg : mPermissionGroups.values()) {
out.add(PackageParser.generatePermissionGroupInfo(pg, flags));
}
- return out;
+ return new ParceledListSlice<>(out);
}
}
private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
- int userId) {
+ 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) {
- PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName,
- flags, userId);
+ final PackageInfo pInfo = generatePackageInfo(ps, flags, userId);
if (pInfo != null) {
return pInfo.applicationInfo;
}
return null;
}
- return PackageParser.generateApplicationInfo(ps.pkg, flags,
+ ApplicationInfo ai = PackageParser.generateApplicationInfo(ps.pkg, flags,
ps.readUserState(userId), userId);
- }
- return null;
- }
-
- private PackageInfo generatePackageInfoFromSettingsLPw(String packageName, int flags,
- int userId) {
- if (!sUserManager.exists(userId)) return null;
- PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps != null) {
- PackageParser.Package pkg = ps.pkg;
- if (pkg == null) {
- if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) {
- return null;
- }
- // Only data remains, so we aren't worried about code paths
- pkg = new PackageParser.Package(packageName);
- pkg.applicationInfo.packageName = packageName;
- pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY;
- pkg.applicationInfo.privateFlags = ps.pkgPrivateFlags;
- pkg.applicationInfo.dataDir = Environment
- .getDataUserPackageDirectory(ps.volumeUuid, userId, packageName)
- .getAbsolutePath();
- pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString;
- pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
+ if (ai != null) {
+ ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
}
- return generatePackageInfo(pkg, flags, userId);
+ 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;
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get application info");
+ flags = updateFlagsForApplication(flags, userId, packageName);
+ 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
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
- return PackageParser.generateApplicationInfo(
+ 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 & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
- return generateApplicationInfoFromSettingsLPw(packageName, flags, userId);
+ if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
+ // Already generates the external package name
+ return generateApplicationInfoFromSettingsLPw(packageName,
+ flags, filterCallingUid, userId);
}
}
return null;
}
+ private String normalizePackageNameLPr(String packageName) {
+ String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
+ return normalizedPackageName != null ? normalizedPackageName : packageName;
+ }
+
+ @Override
+ public void deletePreloadsFileCache() {
+ if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID)) {
+ throw new SecurityException("Only system or settings may call 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 IPackageDataObserver observer) {
+ final int storageFlags, final IPackageDataObserver observer) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CLEAR_APP_CACHE, null);
- // Queue up an async operation since clearing cache may take a little while.
- mHandler.post(new Runnable() {
- public void run() {
- mHandler.removeCallbacks(this);
- int retCode = -1;
- synchronized (mInstallLock) {
- retCode = mInstaller.freeCache(volumeUuid, freeStorageSize);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't clear application caches");
- }
- }
- if (observer != null) {
- try {
- observer.onRemoveCompleted(null, (retCode >= 0));
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoveException when invoking call back");
- }
+ 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 IntentSender pi) {
+ final int storageFlags, final IntentSender pi) {
mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CLEAR_APP_CACHE, null);
- // Queue up an async operation since clearing cache may take a little while.
- mHandler.post(new Runnable() {
- public void run() {
- mHandler.removeCallbacks(this);
- int retCode = -1;
- synchronized (mInstallLock) {
- retCode = mInstaller.freeCache(volumeUuid, freeStorageSize);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't clear application caches");
- }
- }
- if(pi != null) {
- try {
- // Callback via pending intent
- int code = (retCode >= 0) ? 1 : 0;
- pi.sendIntent(null, code, null,
- null, null);
- } catch (SendIntentException e1) {
- Slog.i(TAG, "Failed to send pending intent");
- }
+ 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);
}
}
});
}
- void freeStorage(String volumeUuid, long freeStorageSize) throws IOException {
- synchronized (mInstallLock) {
- if (mInstaller.freeCache(volumeUuid, freeStorageSize) < 0) {
- throw new IOException("Failed to free enough space");
- }
- }
- }
+ /**
+ * 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;
- @Override
- public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
- if (!sUserManager.exists(userId)) return null;
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get activity info");
- synchronized (mPackages) {
- PackageParser.Activity a = mActivities.mActivities.get(component);
+ 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);
- if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
- if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
- PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
- if (ps == null) return null;
- return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
- userId);
+ // 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;
}
- if (mResolveComponentName.equals(component)) {
- return PackageParser.generateActivityInfo(mResolveActivity, flags,
- new PackageUserState(), userId);
+
+ // 3. Consider parsed APK data (aggressive only)
+ if (internalVolume && aggressive) {
+ FileUtils.deleteContents(mCacheDir);
+ if (file.getUsableSpace() >= bytes) return;
}
- }
- return null;
- }
- @Override
+ // 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 SparseArray<SharedLibraryEntry> 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).info;
+ // 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.getVersionCode());
+ 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 (packagesToDelete == null) {
+ packagesToDelete = new ArrayList<>();
+ }
+ packagesToDelete.add(new VersionedPackage(internalPackageName,
+ declaringPackage.getVersionCode()));
+ }
+ }
+ }
+
+ 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.getVersionCode(),
+ 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 DeviceIdleController.LocalService getDeviceIdleController() {
+ if (mDeviceIdleController == null) {
+ mDeviceIdleController =
+ LocalServices.getService(DeviceIdleController.LocalService.class);
+ }
+ return mDeviceIdleController;
+ }
+
+ /**
+ * 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;
+ boolean triaged = true;
+ if ((flags & (PackageManager.GET_ACTIVITIES | PackageManager.GET_RECEIVERS
+ | PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS)) != 0) {
+ // Caller is asking for component details, so they'd better be
+ // asking for specific encryption matching behavior, or be triaged
+ if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
+ triaged = false;
+ }
+ }
+ if ((flags & (PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_SYSTEM_ONLY
+ | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
+ triaged = false;
+ }
+ if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
+ "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;
+ }
+ if (DEBUG_TRIAGED_MISSING && (Binder.getCallingUid() == Process.SYSTEM_UID) && !triaged) {
+ Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
+ + " with flags 0x" + Integer.toHexString(flags), new Throwable());
+ }
+ 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) {
+ if (cookie instanceof Intent) {
+ if ((((Intent) cookie).getFlags() & Intent.FLAG_DEBUG_TRIAGED_MISSING) != 0) {
+ flags |= PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+ }
+ }
+
+ boolean triaged = true;
+ // Caller is asking for component details, so they'd better be
+ // asking for specific encryption matching behavior, or be triaged
+ if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
+ triaged = false;
+ }
+ if (DEBUG_TRIAGED_MISSING && (Binder.getCallingUid() == Process.SYSTEM_UID) && !triaged) {
+ Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
+ + " with flags 0x" + Integer.toHexString(flags), new Throwable());
+ }
+
+ 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
+ && Intent.ACTION_VIEW.equals(intent.getAction())
+ && hasWebURI(intent))
+ || (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);
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ false /* requireFullPermission */, false /* checkShell */, "get activity info");
+ synchronized (mPackages) {
+ PackageParser.Activity a = mActivities.mActivities.get(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;
+ }
+
+ @Override
public boolean activitySupportsIntent(ComponentName component, Intent intent,
String resolvedType) {
synchronized (mPackages) {
// The resolver supports EVERYTHING!
return true;
}
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
PackageParser.Activity a = mActivities.mActivities.get(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) {
@Override
public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get receiver info");
+ final int callingUid = Binder.getCallingUid();
+ flags = updateFlagsForComponent(flags, userId, component);
+ enforceCrossUserPermission(callingUid, userId,
+ false /* requireFullPermission */, false /* checkShell */, "get receiver info");
synchronized (mPackages) {
PackageParser.Activity a = mReceivers.mActivities.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getReceiverInfo " + component + ": " + a);
- if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
+ if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
- return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
- userId);
+ 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;
+
+ synchronized (mPackages) {
+ List<SharedLibraryInfo> result = null;
+
+ final int libCount = mSharedLibraries.size();
+ for (int i = 0; i < libCount; i++) {
+ SparseArray<SharedLibraryEntry> 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).info;
+ 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.getName(),
+ libInfo.getVersion(), libInfo.getType(),
+ libInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr(libInfo,
+ flags, userId));
+
+ if (result == null) {
+ result = new ArrayList<>();
+ }
+ result.add(resLibInfo);
+ }
+ }
+
+ return result != null ? new ParceledListSlice<>(result) : null;
+ }
+ }
+
+ 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.getUserState().get(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.getVersion()) {
+ 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;
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get service info");
+ final int callingUid = Binder.getCallingUid();
+ flags = updateFlagsForComponent(flags, userId, component);
+ enforceCrossUserPermission(callingUid, userId,
+ false /* requireFullPermission */, false /* checkShell */, "get service info");
synchronized (mPackages) {
PackageParser.Service s = mServices.mServices.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getServiceInfo " + component + ": " + s);
- if (s != null && mSettings.isEnabledLPr(s.info, flags, userId)) {
+ if (s != null && mSettings.isEnabledAndMatchLPr(s.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
- return PackageParser.generateServiceInfo(s, flags, ps.readUserState(userId),
- userId);
+ 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;
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get provider info");
+ final int callingUid = Binder.getCallingUid();
+ flags = updateFlagsForComponent(flags, userId, component);
+ enforceCrossUserPermission(callingUid, userId,
+ false /* requireFullPermission */, false /* checkShell */, "get provider info");
synchronized (mPackages) {
PackageParser.Provider p = mProviders.mProviders.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getProviderInfo " + component + ": " + p);
- if (p != null && mSettings.isEnabledLPr(p.info, flags, userId)) {
+ if (p != null && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
- return PackageParser.generateProviderInfo(p, flags, ps.readUserState(userId),
- userId);
+ if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) {
+ return null;
+ }
+ return PackageParser.generateProviderInfo(
+ p, flags, ps.readUserState(userId), userId);
}
}
return null;
@Override
public String[] getSystemSharedLibraryNames() {
- Set<String> libSet;
+ // allow instant applications
synchronized (mPackages) {
- libSet = mSharedLibraries.keySet();
- int size = libSet.size();
- if (size > 0) {
- String[] libs = new String[size];
- libSet.toArray(libs);
- return libs;
+ Set<String> libs = null;
+ final int libCount = mSharedLibraries.size();
+ for (int i = 0; i < libCount; i++) {
+ SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
+ if (versionedLib == null) {
+ continue;
+ }
+ final int versionCount = versionedLib.size();
+ for (int j = 0; j < versionCount; j++) {
+ SharedLibraryEntry libEntry = versionedLib.valueAt(j);
+ if (!libEntry.info.isStatic()) {
+ if (libs == null) {
+ libs = new ArraySet<>();
+ }
+ libs.add(libEntry.info.getName());
+ break;
+ }
+ PackageSetting ps = mSettings.getPackageLPr(libEntry.apk);
+ if (ps != null && !filterSharedLibPackageLPr(ps, Binder.getCallingUid(),
+ UserHandle.getUserId(Binder.getCallingUid()),
+ PackageManager.MATCH_STATIC_SHARED_LIBRARIES)) {
+ if (libs == null) {
+ libs = new ArraySet<>();
+ }
+ libs.add(libEntry.info.getName());
+ break;
+ }
+ }
+ }
+
+ if (libs != null) {
+ String[] libsArray = new String[libs.size()];
+ libs.toArray(libsArray);
+ return libsArray;
}
+
+ return null;
}
- return null;
}
- /**
- * @hide
- */
- PackageParser.Package findSharedNonSystemLibrary(String libName) {
+ @Override
+ public @NonNull String getServicesSystemSharedLibraryPackageName() {
+ // allow instant applications
synchronized (mPackages) {
- PackageManagerService.SharedLibraryEntry lib = mSharedLibraries.get(libName);
- if (lib != null && lib.apk != null) {
- return mPackages.get(lib.apk);
- }
+ return mServicesSystemSharedLibraryPackageName;
}
- return null;
}
@Override
- public FeatureInfo[] getSystemAvailableFeatures() {
- Collection<FeatureInfo> featSet;
+ public @NonNull String getSharedSystemSharedLibraryPackageName() {
+ // allow instant applications
synchronized (mPackages) {
- featSet = mAvailableFeatures.values();
- int size = featSet.size();
- if (size > 0) {
- FeatureInfo[] features = new FeatureInfo[size+1];
- featSet.toArray(features);
- FeatureInfo fi = new FeatureInfo();
- fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version",
- FeatureInfo.GL_ES_VERSION_UNDEFINED);
- features[size] = fi;
- return features;
+ return mSharedSystemSharedLibraryPackageName;
+ }
+ }
+
+ 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);
}
- return null;
+ mChangedPackagesSequenceNumber++;
}
@Override
- public boolean hasSystemFeature(String name) {
+ public ChangedPackages getChangedPackages(int sequenceNumber, int userId) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return null;
+ }
synchronized (mPackages) {
- return mAvailableFeatures.containsKey(name);
+ 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);
}
}
- private void checkValidCaller(int uid, int userId) {
- if (UserHandle.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0)
- return;
+ @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);
- throw new SecurityException("Caller uid=" + uid
- + " is not privileged to communicate with user=" + userId);
+ 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
if (!sUserManager.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
}
+ final int callingUid = Binder.getCallingUid();
synchronized (mPackages) {
final PackageParser.Package p = mPackages.get(pkgName);
if (p != null && p.mExtras != null) {
final PackageSetting ps = (PackageSetting) p.mExtras;
+ if (filterAppAccessLPr(ps, callingUid, userId)) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ final boolean instantApp = ps.getInstantApp(userId);
final PermissionsState permissionsState = ps.getPermissionsState();
if (permissionsState.hasPermission(permName, userId)) {
- return PackageManager.PERMISSION_GRANTED;
+ if (instantApp) {
+ BasePermission bp = mSettings.mPermissions.get(permName);
+ if (bp != null && bp.isInstant()) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ } else {
+ return PackageManager.PERMISSION_GRANTED;
+ }
}
// Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
@Override
public int checkUidPermission(String permName, int uid) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
+ final boolean isUidInstantApp = getInstantAppPackageName(uid) != null;
final int userId = UserHandle.getUserId(uid);
-
if (!sUserManager.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
}
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj != null) {
- final SettingBase ps = (SettingBase) obj;
- final PermissionsState permissionsState = ps.getPermissionsState();
+ if (obj instanceof SharedUserSetting) {
+ if (isCallerInstantApp) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ } else if (obj instanceof PackageSetting) {
+ final PackageSetting ps = (PackageSetting) obj;
+ if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ }
+ final SettingBase settingBase = (SettingBase) obj;
+ final PermissionsState permissionsState = settingBase.getPermissionsState();
if (permissionsState.hasPermission(permName, userId)) {
- return PackageManager.PERMISSION_GRANTED;
+ if (isUidInstantApp) {
+ BasePermission bp = mSettings.mPermissions.get(permName);
+ if (bp != null && bp.isInstant()) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ } else {
+ return PackageManager.PERMISSION_GRANTED;
+ }
}
// Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
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);
/**
* Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS
* or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller.
- * @param checkShell TODO(yamasani):
+ * @param checkShell whether to prevent shell from access if there's a debugging restriction
* @param message the message to log on security exception
*/
void enforceCrossUserPermission(int callingUid, int userId, boolean requireFullPermission,
}
boolean addPermissionLocked(PermissionInfo info, boolean async) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ throw new SecurityException("Instant apps can't add permissions");
+ }
if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
throw new SecurityException("Label must be specified in permission");
}
@Override
public void removePermission(String name) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ throw new SecurityException("Instant applications don't have access to this method");
+ }
synchronized (mPackages) {
checkPermissionTreeLP(name);
BasePermission bp = mSettings.mPermissions.get(name);
}
}
- private static void enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(PackageParser.Package pkg,
- BasePermission bp) {
+ private static void enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(
+ PackageParser.Package pkg, BasePermission bp) {
int index = pkg.requestedPermissions.indexOf(bp.name);
if (index == -1) {
throw new SecurityException("Package " + pkg.packageName
@Override
public void grantRuntimePermission(String packageName, String name, final int userId) {
+ grantRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
+ }
+
+ private void grantRuntimePermission(String packageName, String name, final int userId,
+ boolean overridePolicy) {
if (!sUserManager.exists(userId)) {
Log.e(TAG, "No such user:" + userId);
return;
}
+ final int callingUid = Binder.getCallingUid();
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
"grantRuntimePermission");
- enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, true /* checkShell */,
"grantRuntimePermission");
final int uid;
- final SettingBase sb;
+ final PackageSetting ps;
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
-
final BasePermission bp = mSettings.mPermissions.get(name);
if (bp == null) {
throw new IllegalArgumentException("Unknown permission: " + name);
}
+ ps = (PackageSetting) pkg.mExtras;
+ if (ps == null
+ || filterAppAccessLPr(ps, callingUid, userId)) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp);
- uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
- sb = (SettingBase) pkg.mExtras;
- if (sb == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
+ // If a permission review is required for legacy apps we represent
+ // their permissions as always granted runtime ones since we need
+ // to keep the review required permission flag per user while an
+ // install permission's state is shared across all users.
+ if (mPermissionReviewRequired
+ && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+ && bp.isRuntime()) {
+ return;
}
- final PermissionsState permissionsState = sb.getPermissionsState();
+ uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
+
+ final PermissionsState permissionsState = ps.getPermissionsState();
final int flags = permissionsState.getPermissionFlags(name, userId);
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
- throw new SecurityException("Cannot grant system fixed permission: "
- + name + " for package: " + packageName);
+ throw new SecurityException("Cannot grant system fixed permission "
+ + name + " for package " + packageName);
+ }
+ if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
+ throw new SecurityException("Cannot grant policy fixed permission "
+ + name + " for package " + packageName);
}
if (bp.isDevelopment()) {
return;
}
+ if (ps.getInstantApp(userId) && !bp.isInstant()) {
+ throw new SecurityException("Cannot grant non-ephemeral permission"
+ + name + " for package " + packageName);
+ }
+
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+ Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
+ return;
+ }
+
final int result = permissionsState.grantRuntimePermission(bp, userId);
switch (result) {
case PermissionsState.PERMISSION_OPERATION_FAILURE: {
break;
}
+ if (bp.isRuntime()) {
+ logPermissionGranted(mContext, name, packageName);
+ }
+
mOnPermissionChangeListeners.onPermissionsChanged(uid);
// Not critical if that is lost - app has to request again.
final long token = Binder.clearCallingIdentity();
try {
if (sUserManager.isInitialized(userId)) {
- MountServiceInternal mountServiceInternal = LocalServices.getService(
- MountServiceInternal.class);
- mountServiceInternal.onExternalStoragePolicyChanged(uid, packageName);
+ StorageManagerInternal storageManagerInternal = LocalServices.getService(
+ StorageManagerInternal.class);
+ storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);
}
} finally {
Binder.restoreCallingIdentity(token);
@Override
public void revokeRuntimePermission(String packageName, String name, int userId) {
+ revokeRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
+ }
+
+ private void revokeRuntimePermission(String packageName, String name, int userId,
+ boolean overridePolicy) {
if (!sUserManager.exists(userId)) {
Log.e(TAG, "No such user:" + userId);
return;
android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
"revokeRuntimePermission");
- enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ true /* requireFullPermission */, true /* checkShell */,
"revokeRuntimePermission");
final int appId;
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
-
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps == null
+ || filterAppAccessLPr(ps, Binder.getCallingUid(), userId)) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
final BasePermission bp = mSettings.mPermissions.get(name);
if (bp == null) {
throw new IllegalArgumentException("Unknown permission: " + name);
enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp);
- SettingBase sb = (SettingBase) pkg.mExtras;
- if (sb == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
+ // If a permission review is required for legacy apps we represent
+ // their permissions as always granted runtime ones since we need
+ // to keep the review required permission flag per user while an
+ // install permission's state is shared across all users.
+ if (mPermissionReviewRequired
+ && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+ && bp.isRuntime()) {
+ return;
}
- final PermissionsState permissionsState = sb.getPermissionsState();
+ final PermissionsState permissionsState = ps.getPermissionsState();
final int flags = permissionsState.getPermissionFlags(name, userId);
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
- throw new SecurityException("Cannot revoke system fixed permission: "
- + name + " for package: " + packageName);
+ throw new SecurityException("Cannot revoke system fixed permission "
+ + name + " for package " + packageName);
+ }
+ if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
+ throw new SecurityException("Cannot revoke policy fixed permission "
+ + name + " for package " + packageName);
}
if (bp.isDevelopment()) {
return;
}
+ if (bp.isRuntime()) {
+ logPermissionRevoked(mContext, name, packageName);
+ }
+
mOnPermissionChangeListeners.onPermissionsChanged(pkg.applicationInfo.uid);
// Critical, after this call app should never have the permission.
killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
}
+ /**
+ * We might auto-grant permissions if any permission of the group is already granted. Hence if
+ * the group of a granted permission changes we need to revoke it to avoid having permissions of
+ * the new group auto-granted.
+ *
+ * @param newPackage The new package that was installed
+ * @param oldPackage The old package that was updated
+ * @param allPackageNames All package names
+ */
+ private void revokeRuntimePermissionsIfGroupChanged(
+ PackageParser.Package newPackage,
+ PackageParser.Package oldPackage,
+ ArrayList<String> allPackageNames) {
+ final int numOldPackagePermissions = oldPackage.permissions.size();
+ final ArrayMap<String, String> oldPermissionNameToGroupName
+ = new ArrayMap<>(numOldPackagePermissions);
+
+ for (int i = 0; i < numOldPackagePermissions; i++) {
+ final PackageParser.Permission permission = oldPackage.permissions.get(i);
+
+ if (permission.group != null) {
+ oldPermissionNameToGroupName.put(permission.info.name,
+ permission.group.info.name);
+ }
+ }
+
+ final int numNewPackagePermissions = newPackage.permissions.size();
+ for (int newPermissionNum = 0; newPermissionNum < numNewPackagePermissions;
+ newPermissionNum++) {
+ final PackageParser.Permission newPermission =
+ newPackage.permissions.get(newPermissionNum);
+ final int newProtection = newPermission.info.protectionLevel;
+
+ if ((newProtection & PermissionInfo.PROTECTION_DANGEROUS) != 0) {
+ final String permissionName = newPermission.info.name;
+ final String newPermissionGroupName =
+ newPermission.group == null ? null : newPermission.group.info.name;
+ final String oldPermissionGroupName = oldPermissionNameToGroupName.get(
+ permissionName);
+
+ if (newPermissionGroupName != null
+ && !newPermissionGroupName.equals(oldPermissionGroupName)) {
+ final List<UserInfo> users = mContext.getSystemService(UserManager.class)
+ .getUsers();
+
+ final int numUsers = users.size();
+ for (int userNum = 0; userNum < numUsers; userNum++) {
+ final int userId = users.get(userNum).id;
+ final int numPackages = allPackageNames.size();
+
+ for (int packageNum = 0; packageNum < numPackages; packageNum++) {
+ final String packageName = allPackageNames.get(packageNum);
+
+ if (checkPermission(permissionName, packageName, userId)
+ == PackageManager.PERMISSION_GRANTED) {
+ EventLog.writeEvent(0x534e4554, "72710897",
+ newPackage.applicationInfo.uid,
+ "Revoking permission", permissionName, "from package",
+ packageName, "as the group changed from",
+ oldPermissionGroupName, "to", newPermissionGroupName);
+
+ try {
+ revokeRuntimePermission(packageName, permissionName, userId,
+ false);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Could not revoke " + permissionName + " from "
+ + packageName, e);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Get the first event id for the permission.
+ *
+ * <p>There are four events for each permission: <ul>
+ * <li>Request permission: first id + 0</li>
+ * <li>Grant permission: first id + 1</li>
+ * <li>Request for permission denied: first id + 2</li>
+ * <li>Revoke permission: first id + 3</li>
+ * </ul></p>
+ *
+ * @param name name of the permission
+ *
+ * @return The first event id for the permission
+ */
+ private static int getBaseEventId(@NonNull String name) {
+ int eventIdIndex = ALL_DANGEROUS_PERMISSIONS.indexOf(name);
+
+ if (eventIdIndex == -1) {
+ if (AppOpsManager.permissionToOpCode(name) == AppOpsManager.OP_NONE
+ || Build.IS_USER) {
+ Log.i(TAG, "Unknown permission " + name);
+
+ return MetricsEvent.ACTION_PERMISSION_REQUEST_UNKNOWN;
+ } else {
+ // Most likely #ALL_DANGEROUS_PERMISSIONS needs to be updated.
+ //
+ // Also update
+ // - EventLogger#ALL_DANGEROUS_PERMISSIONS
+ // - metrics_constants.proto
+ throw new IllegalStateException("Unknown permission " + name);
+ }
+ }
+
+ return MetricsEvent.ACTION_PERMISSION_REQUEST_READ_CALENDAR + eventIdIndex * 4;
+ }
+
+ /**
+ * Log that a permission was revoked.
+ *
+ * @param context Context of the caller
+ * @param name name of the permission
+ * @param packageName package permission if for
+ */
+ private static void logPermissionRevoked(@NonNull Context context, @NonNull String name,
+ @NonNull String packageName) {
+ MetricsLogger.action(context, getBaseEventId(name) + 3, packageName);
+ }
+
+ /**
+ * Log that a permission request was granted.
+ *
+ * @param context Context of the caller
+ * @param name name of the permission
+ * @param packageName package permission if for
+ */
+ private static void logPermissionGranted(@NonNull Context context, @NonNull String name,
+ @NonNull String packageName) {
+ MetricsLogger.action(context, getBaseEventId(name) + 1, packageName);
+ }
+
@Override
public void resetRuntimePermissions() {
mContext.enforceCallingOrSelfPermission(
enforceGrantRevokeRuntimePermissionPermissions("getPermissionFlags");
- enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+ final int callingUid = Binder.getCallingUid();
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, false /* checkShell */,
"getPermissionFlags");
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
+ return 0;
}
-
final BasePermission bp = mSettings.mPermissions.get(name);
if (bp == null) {
- throw new IllegalArgumentException("Unknown permission: " + name);
+ return 0;
}
-
- SettingBase sb = (SettingBase) pkg.mExtras;
- if (sb == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps == null
+ || filterAppAccessLPr(ps, callingUid, userId)) {
+ return 0;
}
-
- PermissionsState permissionsState = sb.getPermissionsState();
+ PermissionsState permissionsState = ps.getPermissionsState();
return permissionsState.getPermissionFlags(name, userId);
}
}
enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlags");
- enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+ final int callingUid = Binder.getCallingUid();
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, true /* checkShell */,
"updatePermissionFlags");
// Only the system can change these flags and nothing else.
flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+ flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
}
synchronized (mPackages) {
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps == null
+ || filterAppAccessLPr(ps, callingUid, userId)) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
final BasePermission bp = mSettings.mPermissions.get(name);
if (bp == null) {
throw new IllegalArgumentException("Unknown permission: " + name);
}
- SettingBase sb = (SettingBase) pkg.mExtras;
- if (sb == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
-
- PermissionsState permissionsState = sb.getPermissionsState();
-
- // Only the package manager can change flags for system component permissions.
- final int flags = permissionsState.getPermissionFlags(bp.name, userId);
- if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
- return;
- }
+ PermissionsState permissionsState = ps.getPermissionsState();
boolean hadState = permissionsState.getRuntimePermissionState(name, userId) != null;
enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlagsForAllApps");
- enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ true /* requireFullPermission */, true /* checkShell */,
"updatePermissionFlagsForAllApps");
// Only the system can change system fixed flags.
final int packageCount = mPackages.size();
for (int pkgIndex = 0; pkgIndex < packageCount; pkgIndex++) {
final PackageParser.Package pkg = mPackages.valueAt(pkgIndex);
- SettingBase sb = (SettingBase) pkg.mExtras;
- if (sb == null) {
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps == null) {
continue;
}
- PermissionsState permissionsState = sb.getPermissionsState();
+ PermissionsState permissionsState = ps.getPermissionsState();
changed |= permissionsState.updatePermissionFlagsForAllPermissions(
userId, flagMask, flagValues);
}
"canShowRequestPermissionRationale for user " + userId);
}
- final int uid = getPackageUid(packageName, userId);
+ final int uid = getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId);
if (UserHandle.getAppId(getCallingUid()) != UserHandle.getAppId(uid)) {
return false;
}
@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) {
- synchronized (mPackages) {
- return mProtectedBroadcasts.contains(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
|| 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.mSignatures, p2.mSignatures);
}
}
@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.
uid1 = UserHandle.getAppId(uid1);
uid2 = UserHandle.getAppId(uid2);
Object obj = mSettings.getUserIdLPr(uid1);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
+ if (isCallerInstantApp) {
+ return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
+ }
s1 = ((SharedUserSetting)obj).signatures.mSignatures;
} else if (obj instanceof PackageSetting) {
- s1 = ((PackageSetting)obj).signatures.mSignatures;
+ final PackageSetting ps = (PackageSetting) obj;
+ if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+ return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
+ }
+ s1 = ps.signatures.mSignatures;
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
obj = mSettings.getUserIdLPr(uid2);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
+ if (isCallerInstantApp) {
+ return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
+ }
s2 = ((SharedUserSetting)obj).signatures.mSignatures;
} else if (obj instanceof PackageSetting) {
- s2 = ((PackageSetting)obj).signatures.mSignatures;
+ final PackageSetting ps = (PackageSetting) obj;
+ if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+ return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
+ }
+ s2 = ps.signatures.mSignatures;
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
}
}
+ /**
+ * 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 = ActivityManagerNative.getDefault();
+ IActivityManager am = ActivityManager.getService();
if (am != null) {
try {
am.killUid(appId, userId, reason);
}
@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<String>(mPackages.keySet());
+ }
+ final String instantAppPkgName = getInstantAppPackageName(callingUid);
+ final List<String> result = new ArrayList<>();
+ if (instantAppPkgName != null) {
+ // caller is an instant application; filter unexposed applications
+ for (PackageParser.Package pkg : mPackages.values()) {
+ if (!pkg.visibleToInstantApps) {
+ continue;
+ }
+ result.add(pkg.packageName);
+ }
+ } else {
+ // caller is a normal application; filter instant applications
+ for (PackageParser.Package pkg : mPackages.values()) {
+ final PackageSetting ps =
+ pkg.mExtras != null ? (PackageSetting) pkg.mExtras : null;
+ if (ps != null
+ && ps.getInstantApp(callingUserId)
+ && !mInstantAppRegistry.isInstantAccessGranted(
+ callingUserId, UserHandle.getAppId(callingUid), ps.appId)) {
+ continue;
+ }
+ result.add(pkg.packageName);
+ }
+ }
+ return result;
+ }
+ }
+
+ @Override
public String[] getPackagesForUid(int uid) {
+ final int callingUid = Binder.getCallingUid();
+ final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
+ final int userId = UserHandle.getUserId(uid);
uid = UserHandle.getAppId(uid);
// reader
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(uid);
if (obj instanceof SharedUserSetting) {
+ if (isCallerInstantApp) {
+ return null;
+ }
final SharedUserSetting sus = (SharedUserSetting) obj;
final int N = sus.packages.size();
- final String[] res = new String[N];
+ String[] res = new String[N];
final Iterator<PackageSetting> it = sus.packages.iterator();
int i = 0;
while (it.hasNext()) {
- res[i++] = it.next().name;
+ 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;
- return new String[] { ps.name };
+ if (ps.getInstalled(userId) && !filterAppAccessLPr(ps, callingUid, userId)) {
+ return new String[]{ps.name};
+ }
}
}
return null;
@Override
public String getNameForUid(int uid) {
- // reader
+ final int callingUid = Binder.getCallingUid();
+ if (getInstantAppPackageName(callingUid) != null) {
+ return null;
+ }
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
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;
}
- 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 uid = uids[i];
+ Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+ 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(sharedUserName == null) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return -1;
+ }
+ if (sharedUserName == null) {
return -1;
}
// reader
synchronized (mPackages) {
- final SharedUserSetting suid = mSettings.getSharedUserLPw(sharedUserName, 0, 0, false);
- if (suid == null) {
- return -1;
+ 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 suid.userId;
+ return -1;
}
}
@Override
public int getFlagsForUid(int uid) {
+ final int callingUid = Binder.getCallingUid();
+ if (getInstantAppPackageName(callingUid) != null) {
+ return 0;
+ }
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
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;
}
}
@Override
public int getPrivateFlagsForUid(int uid) {
+ final int callingUid = Binder.getCallingUid();
+ if (getInstantAppPackageName(callingUid) != null) {
+ return 0;
+ }
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
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;
}
}
@Override
public boolean isUidPrivileged(int uid) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return false;
+ }
uid = UserHandle.getAppId(uid);
// reader
synchronized (mPackages) {
@Override
public String[] getAppOpPermissionPackages(String permissionName) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return null;
+ }
synchronized (mPackages) {
ArraySet<String> pkgs = mAppOpPermissionPackages.get(permissionName);
if (pkgs == null) {
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
- if (!sUserManager.exists(userId)) return null;
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "resolve intent");
- List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
- return chooseBestActivity(intent, resolvedType, flags, query, userId);
+ return resolveIntentInternal(
+ intent, resolvedType, flags, userId, false /*includeInstantApps*/);
+ }
+
+ private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
+ int flags, int userId, boolean resolveForStart) {
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
+
+ if (!sUserManager.exists(userId)) return null;
+ final int callingUid = Binder.getCallingUid();
+ flags = updateFlagsForResolve(flags, userId, intent, callingUid, resolveForStart);
+ enforceCrossUserPermission(callingUid, userId,
+ false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
+
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
+ final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
+ flags, callingUid, 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
filter.dump(new PrintStreamPrinter(System.out), " ");
}
intent.setComponent(null);
- List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
+ final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
+ userId);
// Find any earlier preferred or last chosen entries and nuke them
findPreferredActivity(intent, resolvedType,
flags, query, 0, false, true, false, userId);
@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);
- List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
+ final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
+ userId);
return findPreferredActivity(intent, resolvedType, flags, query, 0,
false, false, false, userId);
}
+ /**
+ * Returns whether or not instant apps have been disabled remotely.
+ */
+ private boolean isEphemeralDisabled() {
+ return mEphemeralAppsDisabled;
+ }
+
+ private boolean isInstantAppAllowed(
+ 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;
+ }
+ final boolean isWebUri = hasWebURI(intent);
+ if (!isWebUri || intent.getData().getHost() == null) {
+ 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_EPHEMERAL) {
+ Slog.v(TAG, "DENY instant app;"
+ + " pkg: " + packageName + ", status: " + status);
+ }
+ return false;
+ }
+ }
+ if (ps.getInstantApp(userId)) {
+ if (DEBUG_EPHEMERAL) {
+ 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) {
+ r1.activityInfo.name + "=" + r1.priority);
}
// If the first activity has a higher priority, or a different
- // default, then it is always desireable to pick it.
+ // default, then it is always desirable to pick it.
if (r0.priority != r1.priority
|| r0.preferredOrder != r1.preferredOrder
|| r0.isDefault != r1.isDefault) {
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) {
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;
+ }
+
private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType,
int flags, List<ResolveInfo> query, boolean debug, int userId) {
final int N = query.size();
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)
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+ userId)
: null;
if (pprefs != null && pprefs.size() > 0) {
final int M = pprefs.size();
ppa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
}
final ActivityInfo ai = getActivityInfo(ppa.mComponent,
- flags | PackageManager.GET_DISABLED_COMPONENTS, userId);
+ flags | MATCH_DISABLED_COMPONENTS, userId);
if (DEBUG_PREFERRED || debug) {
Slog.v(TAG, "Found persistent preferred activity:");
if (ai != null) {
return null;
}
+ // TODO: handle preferred activities missing while user has amnesia
ResolveInfo findPreferredActivity(Intent intent, String resolvedType, int flags,
List<ResolveInfo> query, int priority, boolean always,
boolean removeMatches, boolean debug, int userId) {
if (!sUserManager.exists(userId)) return null;
+ final int callingUid = Binder.getCallingUid();
+ flags = updateFlagsForResolve(
+ flags, userId, intent, callingUid, false /*includeInstantApps*/);
+ intent = updateIntentForResolve(intent);
// writer
synchronized (mPackages) {
- if (intent.getSelector() != null) {
- intent = intent.getSelector();
- }
- if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
-
// Try to find a matching persistent preferred activity.
ResolveInfo pri = findPersistentPreferredActivityLP(intent, resolvedType, flags, query,
debug, userId);
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)
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+ userId)
: null;
if (prefs != null && prefs.size() > 0) {
boolean changed = false;
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping mAlways=false entry");
continue;
}
- final ActivityInfo ai = getActivityInfo(pa.mPref.mComponent,
- flags | PackageManager.GET_DISABLED_COMPONENTS, userId);
+ 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) {
// Okay we found a previously set preferred or last chosen app.
// If the result set is different from when this
- // was created, we need to clear it and re-ask the
- // user their preference, if we're looking for an "always" type entry.
+ // 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)) {
- 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);
+ if (pa.mPref.isSuperset(query)) {
+ // 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 {
+ 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;
}
- 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 (hasWebURI(intent)) {
// 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, 0, sourceUserId, parent.id);
+ intent, resolvedType, flags, sourceUserId, parent.id);
return xpDomainInfo != null;
}
}
String resolvedType, int userId) {
CrossProfileIntentResolver resolver = mSettings.mCrossProfileIntentResolvers.get(userId);
if (resolver != null) {
- return resolver.queryIntent(intent, resolvedType, false, userId);
+ return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
}
return null;
}
@Override
- public List<ResolveInfo> queryIntentActivities(Intent intent,
+ 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.getUserIdLPr(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();
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "query intent activities");
+ final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
+ 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) {
}
}
+ flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart,
+ comp != null || pkgName != null /*onlyExposedExplicitly*/);
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
final ActivityInfo ai = getActivityInfo(comp, flags, userId);
if (ai != null) {
- final ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = ai;
- list.add(ri);
+ // 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 list;
+ return applyPostResolutionFilter(
+ list, instantAppPkgName, allowDynamicSplits, filterCallingUid, userId);
}
// reader
+ boolean sortResult = false;
+ boolean addEphemeral = false;
+ List<ResolveInfo> result;
+ final boolean ephemeralDisabled = isEphemeralDisabled();
synchronized (mPackages) {
- final String pkgName = intent.getPackage();
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 && isUserEnabled(xpResolveInfo.targetUserId)) {
- List<ResolveInfo> result = new ArrayList<ResolveInfo>(1);
- result.add(xpResolveInfo);
- return filterIfNotPrimaryUser(result, userId);
+ if (xpResolveInfo != null) {
+ List<ResolveInfo> xpResult = new ArrayList<ResolveInfo>(1);
+ xpResult.add(xpResolveInfo);
+ return applyPostResolutionFilter(
+ filterIfNotSystemUser(xpResult, userId), instantAppPkgName,
+ allowDynamicSplits, filterCallingUid, userId);
}
// Check for results in the current profile.
- List<ResolveInfo> result = mActivities.queryIntent(
- intent, resolvedType, flags, userId);
-
+ result = filterIfNotSystemUser(mActivities.queryIntent(
+ intent, resolvedType, flags, userId), userId);
+ addEphemeral = !ephemeralDisabled
+ && isInstantAppAllowed(intent, result, userId, false /*skipPackageCheck*/);
// Check for cross profile results.
+ boolean hasNonNegativePriorityResult = hasNonNegativePriority(result);
xpResolveInfo = queryCrossProfileIntents(
- matchingFilters, intent, resolvedType, flags, userId);
+ matchingFilters, intent, resolvedType, flags, userId,
+ hasNonNegativePriorityResult);
if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) {
- result.add(xpResolveInfo);
- Collections.sort(result, mResolvePrioritySorter);
+ boolean isVisibleToUser = filterIfNotSystemUser(
+ Collections.singletonList(xpResolveInfo), userId).size() > 0;
+ if (isVisibleToUser) {
+ result.add(xpResolveInfo);
+ sortResult = true;
+ }
}
- result = filterIfNotPrimaryUser(result, userId);
if (hasWebURI(intent)) {
CrossProfileDomainInfo xpDomainInfo = null;
final UserInfo parent = getProfileParent(userId);
// in the result.
result.remove(xpResolveInfo);
}
- if (result.size() == 0) {
+ if (result.size() == 0 && !addEphemeral) {
+ // 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 result;
+ return applyPostResolutionFilter(result, instantAppPkgName,
+ allowDynamicSplits, filterCallingUid, userId);
}
- } else if (result.size() <= 1) {
- return result;
+ } else if (result.size() <= 1 && !addEphemeral) {
+ // 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, userId);
+ }
+ // 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(
+ mActivities.queryIntentForPackage(
+ 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
+ addEphemeral = !ephemeralDisabled
+ && isInstantAppAllowed(
+ intent, null /*result*/, userId, true /*skipPackageCheck*/);
+ if (result == null) {
+ result = new ArrayList<>();
}
- result = filterCandidatesWithDomainPreferredActivitiesLPr(intent, flags, result,
- xpDomainInfo, userId);
- Collections.sort(result, mResolvePrioritySorter);
}
- return result;
}
- final PackageParser.Package pkg = mPackages.get(pkgName);
- if (pkg != null) {
- return filterIfNotPrimaryUser(
- mActivities.queryIntentForPackage(
- intent, resolvedType, flags, pkg.activities, userId),
- userId);
+ }
+ if (addEphemeral) {
+ result = maybeAddInstantAppInstaller(
+ result, intent, resolvedType, flags, userId, resolveForStart);
+ }
+ if (sortResult) {
+ Collections.sort(result, mResolvePrioritySorter);
+ }
+ return applyPostResolutionFilter(
+ result, instantAppPkgName, allowDynamicSplits, filterCallingUid, userId);
+ }
+
+ 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 = mActivities.queryIntent(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);
+ final int linkGeneration = (int)(packedStatus & 0xFFFFFFFF);
+ 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_EPHEMERAL) {
+ 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_EPHEMERAL) {
+ 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(
+ mContext, 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(
+ ai.packageName, null /*splitName*/, null /*failureActivity*/,
+ ai.versionCode, null /*failureIntent*/);
+ }
+ }
+ if (auxiliaryResponse != null) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
+ }
+ final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
+ final PackageSetting ps =
+ mSettings.mPackages.get(mInstantAppInstallerActivity.packageName);
+ if (ps != null) {
+ ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo(
+ mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId);
+ ephemeralInstaller.activityInfo.launchToken = auxiliaryResponse.token;
+ ephemeralInstaller.auxiliaryInfo = auxiliaryResponse;
+ // make sure this resolver is the default
+ ephemeralInstaller.isDefault = true;
+ ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
+ | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
+ // add a non-generic filter
+ ephemeralInstaller.filter = new IntentFilter(intent.getAction());
+ ephemeralInstaller.filter.addDataPath(
+ intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL);
+ ephemeralInstaller.isInstantAppAvailable = true;
+ result.add(ephemeralInstaller);
}
- return new ArrayList<ResolveInfo>();
}
+ return result;
}
private static class CrossProfileDomainInfo {
int status = (int)(verificationState >> 32);
if (result == null) {
result = new CrossProfileDomainInfo();
- result.resolveInfo =
- createForwardingResolveInfo(new IntentFilter(), sourceUserId, parentUserId);
+ result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(),
+ sourceUserId, parentUserId);
result.bestDomainVerificationStatus = status;
} else {
result.bestDomainVerificationStatus = bestDomainVerificationStatus(status,
}
/**
- * Filter out activities with primaryUserOnly flag set, when current user is not the owner.
+ * Filter out activities with systemUserOnly flag set, when current user is not System.
*
* @return filtered list
*/
- private List<ResolveInfo> filterIfNotPrimaryUser(List<ResolveInfo> resolveInfos, int userId) {
- if (userId == UserHandle.USER_OWNER) {
+ 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_PRIMARY_USER_ONLY) != 0) {
+ 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.
+ * @return A filtered list of resolved activities.
+ */
+ private List<ResolveInfo> applyPostResolutionFilter(List<ResolveInfo> resolveInfos,
+ String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid, int userId) {
+ for (int i = resolveInfos.size() - 1; i >= 0; i--) {
+ final ResolveInfo info = resolveInfos.get(i);
+ // allow activities that are defined in the provided package
+ if (allowDynamicSplits
+ && info.activityInfo.splitName != null
+ && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames,
+ info.activityInfo.splitName)) {
+ if (mInstantAppInstallerInfo == null) {
+ if (DEBUG_INSTALL) {
+ Slog.v(TAG, "No installer - not adding it to the ResolveInfo list");
+ }
+ 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(
+ info.activityInfo.packageName, info.activityInfo.splitName,
+ installFailureActivity,
+ info.activityInfo.applicationInfo.versionCode,
+ null /*failureIntent*/);
+ installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
+ | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
+ // 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();
+
+ // propagate priority/preferred order/default
+ installerInfo.priority = info.priority;
+ installerInfo.preferredOrder = info.preferredOrder;
+ installerInfo.isDefault = info.isDefault;
+ 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;
+ }
+ // 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 static boolean hasWebURI(Intent intent) {
if (intent.getData() == null) {
return false;
int status = (int)(packedStatus >> 32);
int linkGeneration = (int)(packedStatus & 0xFFFFFFFF);
if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
- if (DEBUG_DOMAIN_VERIFICATION) {
+ if (DEBUG_DOMAIN_VERIFICATION || debug) {
Slog.i(TAG, " + always: " + info.activityInfo.packageName
+ " : linkgen=" + linkGeneration);
}
info.preferredOrder = linkGeneration;
alwaysList.add(info);
} else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
- if (DEBUG_DOMAIN_VERIFICATION) {
+ 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) {
+ 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) {
+ if (DEBUG_DOMAIN_VERIFICATION || debug) {
Slog.i(TAG, " + ask: " + info.activityInfo.packageName);
}
undefinedList.add(info);
if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0) {
// Checking if there are activities in the target user that can handle the
// intent.
- ResolveInfo resolveInfo = checkTargetCanHandle(filter, intent, resolvedType,
- flags, sourceUserId);
+ ResolveInfo resolveInfo = createForwardingResolveInfo(filter, intent,
+ resolvedType, flags, sourceUserId);
if (resolveInfo != null) {
return resolveInfo;
}
return null;
}
- // Return matching ResolveInfo if any for skip current profile intent filters.
+ // Return matching ResolveInfo in target user if any.
private ResolveInfo queryCrossProfileIntents(
List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
- int flags, int sourceUserId) {
+ 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
for (int i = 0; i < size; i++) {
CrossProfileIntentFilter filter = matchingFilters.get(i);
int targetUserId = filter.getTargetUserId();
- if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) == 0
- && !alreadyTriedUserIds.get(targetUserId)) {
+ 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 = checkTargetCanHandle(filter, intent, resolvedType,
- flags, sourceUserId);
+ ResolveInfo resolveInfo = createForwardingResolveInfo(filter, intent,
+ resolvedType, flags, sourceUserId);
if (resolveInfo != null) return resolveInfo;
alreadyTriedUserIds.put(targetUserId, true);
}
return null;
}
- private ResolveInfo checkTargetCanHandle(CrossProfileIntentFilter filter, Intent intent,
+ /**
+ * 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 = mActivities.queryIntent(intent,
- resolvedType, flags, filter.getTargetUserId());
- if (resultTargetUser != null && !resultTargetUser.isEmpty()) {
- return createForwardingResolveInfo(filter, sourceUserId, filter.getTargetUserId());
+ 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 createForwardingResolveInfo(IntentFilter filter,
+ 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 (targetUserId == UserHandle.USER_OWNER) {
- className = FORWARD_INTENT_TO_USER_OWNER;
- } else {
+ 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 (targetUserId == UserHandle.USER_OWNER) {
- forwardingActivityInfo.showUserIcon = UserHandle.USER_OWNER;
+ if (!targetIsProfile) {
+ forwardingActivityInfo.showUserIcon = targetUserId;
forwardingResolveInfo.noResourceId = true;
}
forwardingResolveInfo.activityInfo = forwardingActivityInfo;
}
@Override
- public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
+ 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();
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false,
- false, "query intent activity options");
+ final int callingUid = Binder.getCallingUid();
+ flags = updateFlagsForResolve(flags, userId, intent, callingUid,
+ false /*includeInstantApps*/);
+ enforceCrossUserPermission(callingUid, userId,
+ false /*requireFullPermission*/, false /*checkShell*/,
+ "query intent activity options");
final String resultsAction = intent.getAction();
- List<ResolveInfo> results = queryIntentActivities(intent, resolvedType, flags
+ final List<ResolveInfo> results = queryIntentActivitiesInternal(intent, resolvedType, flags
| PackageManager.GET_RESOLVED_FILTER, userId);
if (DEBUG_INTENT_MATCHING) {
}
@Override
- public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags,
- int userId) {
+ 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();
+ 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) {
}
}
if (comp != null) {
- List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
- ActivityInfo ai = getReceiverInfo(comp, flags, userId);
+ final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+ final ActivityInfo ai = getReceiverInfo(comp, flags, userId);
if (ai != null) {
- ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = ai;
- list.add(ri);
+ // 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 list;
+ return applyPostResolutionFilter(
+ list, instantAppPkgName, allowDynamicSplits, callingUid, userId);
}
// reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
- return mReceivers.queryIntent(intent, resolvedType, flags, userId);
+ final List<ResolveInfo> result =
+ mReceivers.queryIntent(intent, resolvedType, flags, userId);
+ return applyPostResolutionFilter(
+ result, instantAppPkgName, allowDynamicSplits, callingUid, userId);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
- return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers,
- userId);
+ final List<ResolveInfo> result = mReceivers.queryIntentForPackage(
+ intent, resolvedType, flags, pkg.receivers, userId);
+ return applyPostResolutionFilter(
+ result, instantAppPkgName, allowDynamicSplits, callingUid, userId);
}
- return null;
+ return Collections.emptyList();
}
}
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {
- List<ResolveInfo> query = queryIntentServices(intent, resolvedType, flags, 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,
}
@Override
- public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags,
- int userId) {
+ 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();
+ 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) {
final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
final ServiceInfo si = getServiceInfo(comp, flags, userId);
if (si != null) {
- final ResolveInfo ri = new ResolveInfo();
- ri.serviceInfo = si;
- list.add(ri);
+ // 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;
}
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
- return mServices.queryIntent(intent, resolvedType, flags, userId);
+ return applyPostServiceResolutionFilter(
+ mServices.queryIntent(intent, resolvedType, flags, userId),
+ instantAppPkgName);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
- return mServices.queryIntentForPackage(intent, resolvedType, flags, pkg.services,
- userId);
+ return applyPostServiceResolutionFilter(
+ mServices.queryIntentForPackage(intent, resolvedType, flags, pkg.services,
+ userId),
+ instantAppPkgName);
}
- return null;
+ 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_EPHEMERAL) {
+ Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
+ }
+ final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo);
+ installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
+ info.serviceInfo.packageName, info.serviceInfo.splitName,
+ null /*failureActivity*/, info.serviceInfo.applicationInfo.versionCode,
+ null /*failureIntent*/);
+ // make sure this resolver is the default
+ installerInfo.isDefault = true;
+ installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
+ | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
+ // 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 List<ResolveInfo> queryIntentContentProviders(
+ 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) {
final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
final ProviderInfo pi = getProviderInfo(comp, flags, userId);
if (pi != null) {
- final ResolveInfo ri = new ResolveInfo();
- ri.providerInfo = pi;
- list.add(ri);
+ // 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;
}
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
- return mProviders.queryIntent(intent, resolvedType, flags, userId);
+ return applyPostContentProviderResolutionFilter(
+ mProviders.queryIntent(intent, resolvedType, flags, userId),
+ instantAppPkgName);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
- return mProviders.queryIntentForPackage(
- intent, resolvedType, flags, pkg.providers, userId);
+ return applyPostContentProviderResolutionFilter(
+ mProviders.queryIntentForPackage(
+ intent, resolvedType, flags, pkg.providers, userId),
+ instantAppPkgName);
}
- return null;
+ 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_EPHEMERAL) {
+ Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
+ }
+ final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo);
+ installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
+ info.providerInfo.packageName, info.providerInfo.splitName,
+ null /*failureActivity*/, info.providerInfo.applicationInfo.versionCode,
+ null /*failureIntent*/);
+ // make sure this resolver is the default
+ installerInfo.isDefault = true;
+ installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
+ | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
+ // 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 boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
-
- enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "get installed packages");
+ 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;
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, false /* checkShell */,
+ "get installed packages");
// writer
synchronized (mPackages) {
ArrayList<PackageInfo> list;
if (listUninstalled) {
- list = new ArrayList<PackageInfo>(mSettings.mPackages.size());
+ list = new ArrayList<>(mSettings.mPackages.size());
for (PackageSetting ps : mSettings.mPackages.values()) {
- PackageInfo pi;
- if (ps.pkg != null) {
- pi = generatePackageInfo(ps.pkg, flags, userId);
- } else {
- pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId);
+ 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<PackageInfo>(mPackages.size());
+ list = new ArrayList<>(mPackages.size());
for (PackageParser.Package p : mPackages.values()) {
- PackageInfo pi = generatePackageInfo(p, flags, userId);
+ 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);
}
}
}
- return new ParceledListSlice<PackageInfo>(list);
+ return new ParceledListSlice<>(list);
}
}
if (numMatch == 0) {
return;
}
- PackageInfo pi;
- if (ps.pkg != null) {
- pi = generatePackageInfo(ps.pkg, flags, userId);
- } else {
- pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId);
- }
+ 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) {
@Override
public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
String[] permissions, int flags, int userId) {
- if (!sUserManager.exists(userId)) return null;
- final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
+ if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
+ flags = updateFlagsForPackage(flags, userId, permissions);
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ true /* requireFullPermission */, false /* checkShell */,
+ "get packages holding permissions");
+ final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
// writer
synchronized (mPackages) {
boolean[] tmpBools = new boolean[permissions.length];
if (listUninstalled) {
for (PackageSetting ps : mSettings.mPackages.values()) {
- addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId);
+ addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags,
+ userId);
}
} else {
for (PackageParser.Package pkg : mPackages.values()) {
@Override
public ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags, int userId) {
- if (!sUserManager.exists(userId)) return null;
- final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
+ final int callingUid = Binder.getCallingUid();
+ if (getInstantAppPackageName(callingUid) != null) {
+ return ParceledListSlice.emptyList();
+ }
+ if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
+ flags = updateFlagsForApplication(flags, userId, null);
+ final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
// writer
synchronized (mPackages) {
ArrayList<ApplicationInfo> list;
if (listUninstalled) {
- list = new ArrayList<ApplicationInfo>(mSettings.mPackages.size());
+ 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) {
- ai = PackageParser.generateApplicationInfo(ps.pkg, flags,
+ 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 {
- ai = generateApplicationInfoFromSettingsLPw(ps.name, flags, userId);
+ // 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<ApplicationInfo>(mPackages.size());
+ 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,
- ((PackageSetting)p.mExtras).readUserState(userId), userId);
+ ps.readUserState(userId), userId);
if (ai != null) {
+ ai.packageName = resolveExternalPackageNameLPr(p);
list.add(ai);
}
}
}
}
- return new ParceledListSlice<ApplicationInfo>(list);
+ return new ParceledListSlice<>(list);
+ }
+ }
+
+ @Override
+ public ParceledListSlice<InstantAppInfo> getInstantApps(int userId) {
+ if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
+ return null;
+ }
+ if (!canViewInstantApps(Binder.getCallingUid(), userId)) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
+ "getEphemeralApplications");
+ }
+ 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) {
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ true /* requireFullPermission */, false /* checkShell */,
+ "isInstantApp");
+ if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
+ 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 || isEphemeralDisabled()) {
+ return null;
+ }
+
+ 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 || isEphemeralDisabled()) {
+ return true;
+ }
+
+ 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 || isEphemeralDisabled()) {
+ return null;
+ }
+
+ if (!canViewInstantApps(Binder.getCallingUid(), userId)) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
+ "getInstantAppIcon");
+ }
+ 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));
}
- public List<ApplicationInfo> getPersistentApplications(int flags) {
+ private @NonNull List<ApplicationInfo> getPersistentApplicationsInternal(int flags) {
final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
// reader
final int userId = UserHandle.getCallingUserId();
while (i.hasNext()) {
final PackageParser.Package p = i.next();
- if (p.applicationInfo != null
- && (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
- && (!mSafeMode || isSystemApp(p))) {
+ 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,
@Override
public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ flags = updateFlagsForComponent(flags, userId, name);
+ final String instantAppPkgName = getInstantAppPackageName(Binder.getCallingUid());
// reader
synchronized (mPackages) {
final PackageParser.Provider provider = mProvidersByAuthority.get(name);
PackageSetting ps = provider != null
? mSettings.mPackages.get(provider.owner.packageName)
: null;
- return ps != null
- && mSettings.isEnabledLPr(provider.info, flags, userId)
- && (!mSafeMode || (provider.info.applicationInfo.flags
- &ApplicationInfo.FLAG_SYSTEM) != 0)
- ? PackageParser.generateProviderInfo(provider, flags,
- ps.readUserState(userId), userId)
- : null;
- }
- }
-
- /**
- * @deprecated
- */
+ if (ps != null) {
+ final boolean isInstantApp = ps.getInstantApp(userId);
+ // normal application; filter out instant application provider
+ if (instantAppPkgName == null && isInstantApp) {
+ return null;
+ }
+ // instant application; filter out other instant applications
+ if (instantAppPkgName != null
+ && isInstantApp
+ && !provider.owner.packageName.equals(instantAppPkgName)) {
+ return null;
+ }
+ // instant application; filter out non-exposed provider
+ if (instantAppPkgName != null
+ && !isInstantApp
+ && (provider.info.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0) {
+ return null;
+ }
+ // provider not enabled
+ if (!mSettings.isEnabledAndMatchLPr(provider.info, flags, userId)) {
+ return null;
+ }
+ return PackageParser.generateProviderInfo(
+ provider, flags, ps.readUserState(userId), userId);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * @deprecated
+ */
@Deprecated
public void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return;
+ }
// reader
synchronized (mPackages) {
final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProvidersByAuthority
}
@Override
- public ParceledListSlice<ProviderInfo> queryContentProviders(String processName,
- int uid, int flags) {
+ 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;
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Provider> i = mProviders.mProviders.values().iterator();
- final int userId = processName != null ?
- UserHandle.getUserId(uid) : UserHandle.getCallingUserId();
while (i.hasNext()) {
final PackageParser.Provider p = i.next();
PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
&& (processName == null
|| (p.info.processName.equals(processName)
&& UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))
- && mSettings.isEnabledLPr(p.info, flags, userId)
- && (!mSafeMode
- || (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
+ && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) {
+
+ // See PM.queryContentProviders()'s javadoc for why we have the metaData
+ // parameter.
+ if (metaDataKey != null
+ && (p.metaData == null || !p.metaData.containsKey(metaDataKey))) {
+ continue;
+ }
+ final ComponentName component =
+ new ComponentName(p.info.packageName, p.info.name);
+ if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) {
+ continue;
+ }
if (finalList == null) {
finalList = new ArrayList<ProviderInfo>(3);
}
return new ParceledListSlice<ProviderInfo>(finalList);
}
- return null;
+ return ParceledListSlice.emptyList();
}
@Override
- public InstrumentationInfo getInstrumentationInfo(ComponentName name,
- int flags) {
+ public InstrumentationInfo getInstrumentationInfo(ComponentName component, int flags) {
// reader
synchronized (mPackages) {
- final PackageParser.Instrumentation i = mInstrumentation.get(name);
+ 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 List<InstrumentationInfo> queryInstrumentation(String targetPackage,
+ 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<InstrumentationInfo>();
+ ArrayList<InstrumentationInfo> finalList = new ArrayList<InstrumentationInfo>();
// reader
synchronized (mPackages) {
return finalList;
}
- private void createIdmapsForPackageLI(PackageParser.Package pkg) {
- ArrayMap<String, PackageParser.Package> overlays = mOverlays.get(pkg.packageName);
- if (overlays == null) {
- Slog.w(TAG, "Unable to create idmap for " + pkg.packageName + ": no overlay packages");
- return;
- }
- for (PackageParser.Package opkg : overlays.values()) {
- // Not much to do if idmap fails: we already logged the error
- // and we certainly don't want to abort installation of pkg simply
- // because an overlay didn't fit properly. For these reasons,
- // ignore the return value of createIdmapForPackagePairLI.
- createIdmapForPackagePairLI(pkg, opkg);
- }
- }
-
- private boolean createIdmapForPackagePairLI(PackageParser.Package pkg,
- PackageParser.Package opkg) {
- if (!opkg.mTrustedOverlay) {
- Slog.w(TAG, "Skipping target and overlay pair " + pkg.baseCodePath + " and " +
- opkg.baseCodePath + ": overlay not trusted");
- return false;
- }
- ArrayMap<String, PackageParser.Package> overlaySet = mOverlays.get(pkg.packageName);
- if (overlaySet == null) {
- Slog.e(TAG, "was about to create idmap for " + pkg.baseCodePath + " and " +
- opkg.baseCodePath + " but target package has no known overlays");
- return false;
- }
- final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
- // TODO: generate idmap for split APKs
- if (mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid) != 0) {
- Slog.e(TAG, "Failed to generate idmap for " + pkg.baseCodePath + " and "
- + opkg.baseCodePath);
- return false;
- }
- PackageParser.Package[] overlayArray =
- overlaySet.values().toArray(new PackageParser.Package[0]);
- Comparator<PackageParser.Package> cmp = new Comparator<PackageParser.Package>() {
- public int compare(PackageParser.Package p1, PackageParser.Package p2) {
- return p1.mOverlayPriority - p2.mOverlayPriority;
- }
- };
- Arrays.sort(overlayArray, cmp);
-
- pkg.applicationInfo.resourceDirs = new String[overlayArray.length];
- int i = 0;
- for (PackageParser.Package p : overlayArray) {
- pkg.applicationInfo.resourceDirs[i++] = p.baseCodePath;
+ private void scanDirTracedLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + dir.getAbsolutePath() + "]");
+ try {
+ scanDirLI(dir, parseFlags, scanFlags, currentTime);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- return true;
}
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags
+ " flags=0x" + Integer.toHexString(parseFlags));
}
+ 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());
// Ignore entries which are not packages
continue;
}
- try {
- scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
- scanFlags, currentTime, null);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
-
- // Delete invalid userdata apps
- if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
- e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
- logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
- if (file.isDirectory()) {
- mInstaller.rmPackageDir(file.getAbsolutePath());
- } else {
- file.delete();
- }
+ 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) {
+ // Static shared libraries have synthetic package names
+ if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
+ renameStaticSharedLibraryPackage(parseResult.pkg);
}
+ try {
+ if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
+ scanPackageLI(parseResult.pkg, parseResult.scanFile, 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 ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
+ errorCode == PackageManager.INSTALL_FAILED_INVALID_APK) {
+ logCriticalInfo(Log.WARN,
+ "Deleting invalid package at " + parseResult.scanFile);
+ removeCodePathLI(parseResult.scanFile);
}
}
+ parallelPackageParser.close();
}
private static File getSettingsProblemFile() {
logCriticalInfo(priority, msg);
}
- static void logCriticalInfo(int priority, String msg) {
+ public static void logCriticalInfo(int priority, String msg) {
Slog.println(priority, TAG, msg);
EventLogTags.writePmCriticalInfo(msg);
try {
}
}
- private void collectCertificatesLI(PackageParser pp, PackageSetting ps,
- PackageParser.Package pkg, File srcFile, int parseFlags)
- throws PackageManagerException {
+ private long getLastModifiedTime(PackageParser.Package pkg, File srcFile) {
+ if (srcFile.isDirectory()) {
+ final File baseFile = new File(pkg.baseCodePath);
+ long maxModifiedTime = baseFile.lastModified();
+ if (pkg.splitCodePaths != null) {
+ for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) {
+ final File splitFile = new File(pkg.splitCodePaths[i]);
+ maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified());
+ }
+ }
+ return maxModifiedTime;
+ }
+ return srcFile.lastModified();
+ }
+
+ private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, File srcFile,
+ final int policyFlags) 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, srcFile);
if (ps != null
&& ps.codePath.equals(srcFile)
- && ps.timeStamp == srcFile.lastModified()
+ && ps.timeStamp == lastModifiedTime
&& !isCompatSignatureUpdateNeeded(pkg)
&& !isRecoverSignatureUpdateNeeded(pkg)) {
long mSigningKeySetId = ps.keySetData.getProperSigningKeySet();
Slog.w(TAG, "PackageSetting for " + ps.name
+ " is missing signatures. Collecting certs again to recover them.");
} else {
- Log.i(TAG, srcFile.toString() + " changed; collecting certs");
+ Slog.i(TAG, srcFile.toString() + " changed; collecting certs");
}
try {
- pp.collectCertificates(pkg, parseFlags);
- pp.collectManifestDigest(pkg);
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+ PackageParser.collectCertificates(pkg, policyFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
- /*
- * Scan a package and return the newly parsed package.
- * Returns null in case of errors and the error code is stored in mLastScanError
+ /**
+ * Traces a package scan.
+ * @see #scanPackageLI(File, int, int, long, UserHandle)
+ */
+ 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
*/
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
- parseFlags |= mDefParseFlags;
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
+ pp.setCallback(mPackageParserCallback);
if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
}
+ 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 scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
+ }
+
+ /**
+ * Scans a package and returns the newly parsed package.
+ * @throws PackageManagerException on a parse error.
+ */
+ private PackageParser.Package scanPackageLI(PackageParser.Package pkg, File scanFile,
+ final int policyFlags, 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 = scanPackageInternalLI(pkg, scanFile, policyFlags,
+ 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);
+ scanPackageInternalLI(childPackage, scanFile, policyFlags, scanFlags,
+ currentTime, user);
+ }
+
+
+ if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
+ return scanPackageLI(pkg, scanFile, policyFlags, scanFlags, currentTime, user);
}
+ return scannedPkg;
+ }
+
+ /**
+ * Scans a package and returns the newly parsed package.
+ * @throws PackageManagerException on a parse error.
+ */
+ private PackageParser.Package scanPackageInternalLI(PackageParser.Package pkg, File scanFile,
+ int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user)
+ throws PackageManagerException {
PackageSetting ps = null;
PackageSetting updatedPkg;
// reader
synchronized (mPackages) {
// Look to see if we already know about this package.
- String oldName = mSettings.mRenamedPackages.get(pkg.packageName);
+ String oldName = mSettings.getRenamedPackageLPr(pkg.packageName);
if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {
// This package has been renamed to its original name. Let's
// use that.
- ps = mSettings.peekPackageLPr(oldName);
+ ps = mSettings.getPackageLPr(oldName);
}
// If there was no original package, see one for the real package name.
if (ps == null) {
- ps = mSettings.peekPackageLPr(pkg.packageName);
+ ps = mSettings.getPackageLPr(pkg.packageName);
}
// Check to see if this package could be hiding/updating a system
// package. Must look for it either under the original or real
// package name depending on our state.
updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg);
+
+ // If this is a package we don't know about on the system partition, we
+ // may need to remove disabled child packages on the system partition
+ // or may need to not add child packages if the parent apk is updated
+ // on the data partition and no longer defines this child package.
+ if ((policyFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {
+ // If this is a parent package for an updated system app and this system
+ // app got an OTA update which no longer defines some of the child packages
+ // we have to prune them from the disabled system packages.
+ PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);
+ if (disabledPs != null) {
+ final int scannedChildCount = (pkg.childPackages != null)
+ ? pkg.childPackages.size() : 0;
+ final int disabledChildCount = disabledPs.childPackageNames != null
+ ? disabledPs.childPackageNames.size() : 0;
+ for (int i = 0; i < disabledChildCount; i++) {
+ String disabledChildPackageName = disabledPs.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);
+ }
+ }
+ }
+ }
}
- boolean updatedPkgBetter = false;
+
+ final boolean isUpdatedPkg = updatedPkg != null;
+ final boolean isUpdatedSystemPkg = isUpdatedPkg
+ && (policyFlags & PackageParser.PARSE_IS_SYSTEM) != 0;
+ boolean isUpdatedPkgBetter = false;
// First check if this is a system package that may involve an update
- if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
+ if (isUpdatedSystemPkg) {
// If new package is not located in "/system/priv-app" (e.g. due to an OTA),
// it needs to drop FLAG_PRIVILEGED.
if (locationIsPrivileged(scanFile)) {
+ " ignored: updated version " + ps.versionCode
+ " better than this " + pkg.mVersionCode);
if (!updatedPkg.codePath.equals(scanFile)) {
- Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg : "
+ Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg "
+ ps.name + " changing from " + updatedPkg.codePathString
+ " to " + scanFile);
updatedPkg.codePath = scanFile;
updatedPkg.resourcePathString = scanFile.toString();
}
updatedPkg.pkg = pkg;
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Package " + ps.name + " at " + scanFile
- + " ignored: updated version " + ps.versionCode
- + " better than this " + pkg.mVersionCode);
+ updatedPkg.versionCode = pkg.mVersionCode;
+
+ // Update the disabled system child packages to point to the package too.
+ final int childCount = updatedPkg.childPackageNames != null
+ ? updatedPkg.childPackageNames.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ String childPackageName = updatedPkg.childPackageNames.get(i);
+ PackageSetting updatedChildPkg = mSettings.getDisabledSystemPkgLPr(
+ childPackageName);
+ if (updatedChildPkg != null) {
+ updatedChildPkg.pkg = pkg;
+ updatedChildPkg.versionCode = pkg.mVersionCode;
+ }
+ }
} else {
// The current app on the system partition is better than
// what we have updated to on the data partition; switch
synchronized (mPackages) {
mSettings.enableSystemPackageLPw(ps.name);
}
- updatedPkgBetter = true;
+ isUpdatedPkgBetter = true;
}
}
}
- if (updatedPkg != null) {
+ String resourcePath = null;
+ String baseResourcePath = null;
+ if ((policyFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !isUpdatedPkgBetter) {
+ if (ps != null && ps.resourcePathString != null) {
+ resourcePath = ps.resourcePathString;
+ baseResourcePath = ps.resourcePathString;
+ } else {
+ // Should not happen at all. Just log an error.
+ Slog.e(TAG, "Resource path not set for package " + pkg.packageName);
+ }
+ } else {
+ resourcePath = pkg.codePath;
+ baseResourcePath = pkg.baseCodePath;
+ }
+
+ // Set application objects path explicitly.
+ pkg.setApplicationVolumeUuid(pkg.volumeUuid);
+ pkg.setApplicationInfoCodePath(pkg.codePath);
+ pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
+ pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
+ pkg.setApplicationInfoResourcePath(resourcePath);
+ pkg.setApplicationInfoBaseResourcePath(baseResourcePath);
+ pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
+
+ // throw an exception if we have an update to a system application, but, it's not more
+ // recent than the package we've already scanned
+ if (isUpdatedSystemPkg && !isUpdatedPkgBetter) {
+ // Set CPU Abis to application info.
+ if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0) {
+ final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, updatedPkg);
+ derivePackageAbi(pkg, scanFile, cpuAbiOverride, false, mAppLib32InstallDir);
+ } else {
+ pkg.applicationInfo.primaryCpuAbi = updatedPkg.primaryCpuAbiString;
+ pkg.applicationInfo.secondaryCpuAbi = updatedPkg.secondaryCpuAbiString;
+ }
+
+ throw new PackageManagerException(Log.WARN, "Package " + ps.name + " at "
+ + scanFile + " ignored: updated version " + ps.versionCode
+ + " better than this " + pkg.mVersionCode);
+ }
+
+ if (isUpdatedPkg) {
// An updated system app will not have the PARSE_IS_SYSTEM flag set
// initially
- parseFlags |= PackageParser.PARSE_IS_SYSTEM;
+ policyFlags |= PackageParser.PARSE_IS_SYSTEM;
// An updated privileged app will not have the PARSE_IS_PRIVILEGED
// flag set initially
if ((updatedPkg.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
- parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
+ policyFlags |= PackageParser.PARSE_IS_PRIVILEGED;
}
}
// Verify certificates against what was last scanned
- collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags);
+ collectCertificatesLI(ps, pkg, scanFile, policyFlags);
/*
* A new system app appeared, but we already had a non-system one of the
* same name installed earlier.
*/
boolean shouldHideSystemApp = false;
- if (updatedPkg == null && ps != null
- && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) {
+ if (!isUpdatedPkg && ps != null
+ && (policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) {
/*
* Check to make sure the signatures match first. If they don't,
* wipe the installed application and its data.
!= PackageManager.SIGNATURE_MATCH) {
logCriticalInfo(Log.WARN, "Package " + ps.name + " appeared on system, but"
+ " signatures don't match existing userdata copy; removing");
- deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false);
+ try (PackageFreezer freezer = freezePackage(pkg.packageName,
+ "scanPackageInternalLI")) {
+ deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
+ }
ps = null;
} else {
/*
// are kept in different files. (except for app in either system or
// vendor path).
// TODO grab this value from PackageSettings
- if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+ if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
if (ps != null && !ps.codePath.equals(ps.resourcePath)) {
- parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
+ policyFlags |= PackageParser.PARSE_FORWARD_LOCK;
}
}
- // TODO: extend to support forward-locked splits
- String resourcePath = null;
- String baseResourcePath = null;
- if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) {
- if (ps != null && ps.resourcePathString != null) {
- resourcePath = ps.resourcePathString;
- baseResourcePath = ps.resourcePathString;
- } else {
- // Should not happen at all. Just log an error.
- Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
- }
- } else {
- resourcePath = pkg.codePath;
- baseResourcePath = pkg.baseCodePath;
+ final int userId = ((user == null) ? 0 : user.getIdentifier());
+ if (ps != null && ps.getInstantApp(userId)) {
+ scanFlags |= SCAN_AS_INSTANT_APP;
+ }
+ if (ps != null && ps.getVirtulalPreload(userId)) {
+ scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
}
-
- // Set application objects path explicitly.
- pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
- pkg.applicationInfo.setCodePath(pkg.codePath);
- pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
- pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
- pkg.applicationInfo.setResourcePath(resourcePath);
- pkg.applicationInfo.setBaseResourcePath(baseResourcePath);
- pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
// Note that we invoke the following method only if we are about to unpack an application
- PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
+ PackageParser.Package scannedPkg = scanPackageLI(pkg, policyFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
/*
*/
if (shouldHideSystemApp) {
synchronized (mPackages) {
- mSettings.disableSystemPackageLPw(pkg.packageName);
+ mSettings.disableSystemPackageLPw(pkg.packageName, true);
}
}
return scannedPkg;
}
+ private void renameStaticSharedLibraryPackage(PackageParser.Package pkg) {
+ // Derive the new package synthetic package name
+ pkg.setPackageName(pkg.packageName + STATIC_SHARED_LIB_DELIMITER
+ + pkg.staticSharedLibVersion);
+ }
+
private static String fixProcessName(String defProcessName,
- String processName, int uid) {
+ String processName) {
if (processName == null) {
return defProcessName;
}
*/
private static final void enforceSystemOrRoot(String message) {
final int uid = Binder.getCallingUid();
- if (uid != Process.SYSTEM_UID && uid != 0) {
+ if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) {
throw new SecurityException(message);
}
}
@Override
- public void performBootDexOpt() {
- enforceSystemOrRoot("Only the system can request dexopt be performed");
+ public void performFstrimIfNeeded() {
+ enforceSystemOrRoot("Only the system can request fstrim");
// Before everything else, see whether we need to fstrim.
try {
- IMountService ms = PackageHelper.getMountService();
- if (ms != null) {
- final boolean isUpgrade = isUpgrade();
- boolean doTrim = isUpgrade;
- if (doTrim) {
- Slog.w(TAG, "Running disk maintenance immediately due to system update");
- } else {
- 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() - ms.lastMaintenance();
- if (timeSinceLast > interval) {
- doTrim = true;
- Slog.w(TAG, "No disk maintenance in " + timeSinceLast
- + "; running immediately");
- }
+ 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) {
- if (!isFirstBoot()) {
+ final boolean dexOptDialogShown;
+ synchronized (mPackages) {
+ dexOptDialogShown = mDexOptDialogShown;
+ }
+ if (!isFirstBoot() && dexOptDialogShown) {
try {
- ActivityManagerNative.getDefault().showBootMessage(
+ ActivityManager.getService().showBootMessage(
mContext.getResources().getString(
R.string.android_upgrading_fstrim), true);
} catch (RemoteException e) {
}
}
- ms.runMaintenance();
+ sm.runMaintenance();
}
} else {
- Slog.e(TAG, "Mount service unavailable!");
+ Slog.e(TAG, "storageManager service unavailable!");
}
} catch (RemoteException e) {
- // Can't happen; MountService is local
+ // 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 = isUpgrade();
+
+ // 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;
}
- final ArraySet<PackageParser.Package> pkgs;
+ List<PackageParser.Package> pkgs;
synchronized (mPackages) {
- pkgs = mPackageDexOptimizer.clearDeferredDexOptPackages();
+ pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
}
- if (pkgs != null) {
- // Sort apps by importance for dexopt ordering. Important apps are given more priority
- // in case the device runs out of space.
- ArrayList<PackageParser.Package> sortedPkgs = new ArrayList<PackageParser.Package>();
- // Give priority to core apps.
- for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
- PackageParser.Package pkg = it.next();
- if (pkg.coreApp) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding core app " + sortedPkgs.size() + ": " + pkg.packageName);
+ final long startTime = System.nanoTime();
+ final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
+ getCompilerFilterForReason(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 String compilerFilter, 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() || isUpgrade()) && 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)) {
+ 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);
}
- sortedPkgs.add(pkg);
- it.remove();
- }
- }
- // Give priority to system apps that listen for pre boot complete.
- Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
- ArraySet<String> pkgNames = getPackageNamesForIntent(intent);
- for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
- PackageParser.Package pkg = it.next();
- if (pkgNames.contains(pkg.packageName)) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding pre boot system app " + sortedPkgs.size() + ": " + pkg.packageName);
+ } 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)) {
+ 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);
+ }
+ }
}
- sortedPkgs.add(pkg);
- it.remove();
}
}
- // Filter out packages that aren't recently used.
- filterRecentlyUsedApps(pkgs);
- // Add all remaining apps.
- for (PackageParser.Package pkg : pkgs) {
+
+ if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding app " + sortedPkgs.size() + ": " + pkg.packageName);
+ Log.i(TAG, "Skipping update of of non-optimizable app " + pkg.packageName);
}
- sortedPkgs.add(pkg);
+ numberOfPackagesSkipped++;
+ continue;
}
- // If we want to be lazy, filter everything that wasn't recently used.
- if (mLazyDexOpt) {
- filterRecentlyUsedApps(sortedPkgs);
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " +
+ numberOfPackagesToDexopt + ": " + pkg.packageName);
}
- int i = 0;
- int total = sortedPkgs.size();
- File dataDir = Environment.getDataDirectory();
- long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir);
- if (lowThreshold == 0) {
- throw new IllegalStateException("Invalid low memory threshold");
- }
- for (PackageParser.Package pkg : sortedPkgs) {
- long usableSpace = dataDir.getUsableSpace();
- if (usableSpace < lowThreshold) {
- Log.w(TAG, "Not running dexopt on remaining apps due to low memory: " + usableSpace);
- break;
+ if (showDialog) {
+ try {
+ ActivityManager.getService().showBootMessage(
+ mContext.getResources().getString(R.string.android_upgrading_apk,
+ numberOfPackagesVisited, numberOfPackagesToDexopt), true);
+ } catch (RemoteException e) {
+ }
+ synchronized (mPackages) {
+ mDexOptDialogShown = true;
}
- performBootDexOpt(pkg, ++i, total);
}
- }
- }
- private void filterRecentlyUsedApps(Collection<PackageParser.Package> pkgs) {
- // Filter out packages that aren't recently used.
- //
- // The exception is first boot of a non-eng device (aka !mLazyDexOpt), which
- // should do a full dexopt.
- if (mLazyDexOpt || (!isFirstBoot() && mPackageUsage.isHistoricalPackageUsageAvailable())) {
- int total = pkgs.size();
- int skipped = 0;
- long now = System.currentTimeMillis();
- for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
- PackageParser.Package pkg = i.next();
- long then = pkg.mLastPackageUsageTimeInMills;
- if (then + mDexOptLRUThresholdInMills < now) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " +
- ((then == 0) ? "never" : new Date(then)));
- }
- i.remove();
- skipped++;
- }
+ String pkgCompilerFilter = compilerFilter;
+ if (useProfileForDexopt) {
+ // Use background dexopt mode to try and use the profile. Note that this does not
+ // guarantee usage of the profile.
+ pkgCompilerFilter =
+ PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
+ PackageManagerService.REASON_BACKGROUND_DEXOPT);
}
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Skipped optimizing " + skipped + " of " + total);
+
+ // 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;
+ int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
+ pkg.packageName,
+ pkgCompilerFilter,
+ 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 };
}
- private ArraySet<String> getPackageNamesForIntent(Intent intent) {
- List<ResolveInfo> ris = null;
- try {
- ris = AppGlobals.getPackageManager().queryIntentReceivers(
- intent, null, 0, UserHandle.USER_OWNER);
- } catch (RemoteException e) {
- }
- ArraySet<String> pkgNames = new ArraySet<String>();
- if (ris != null) {
- for (ResolveInfo ri : ris) {
- pkgNames.add(ri.activityInfo.packageName);
+ @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);
}
- return pkgNames;
}
- private void performBootDexOpt(PackageParser.Package pkg, int curr, int total) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Optimizing app " + curr + " of " + total + ": " + pkg.packageName);
- }
- if (!isFirstBoot()) {
- try {
- ActivityManagerNative.getDefault().showBootMessage(
- mContext.getResources().getString(R.string.android_upgrading_apk,
- curr, total), true);
- } catch (RemoteException e) {
- }
- }
- PackageParser.Package p = pkg;
- synchronized (mInstallLock) {
- mPackageDexOptimizer.performDexOpt(p, null /* instruction sets */,
- false /* force dex */, false /* defer */, true /* include dependencies */,
- false /* boot complete */);
+ 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 boolean performDexOptIfNeeded(String packageName, String instructionSet) {
- return performDexOpt(packageName, instructionSet, false);
+ 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);
}
- public boolean performDexOpt(String packageName, String instructionSet, boolean backgroundDexopt) {
- boolean dexopt = mLazyDexOpt || backgroundDexopt;
- boolean updateUsage = !backgroundDexopt; // Don't update usage if this is just a backgroundDexopt
- if (!dexopt && !updateUsage) {
- // We aren't going to dexopt or update usage, so bail early.
- return false;
+ @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);
}
- PackageParser.Package p;
- final String targetInstructionSet;
- synchronized (mPackages) {
- p = mPackages.get(packageName);
- if (p == null) {
- return false;
- }
- if (updateUsage) {
- p.mLastPackageUsageTimeInMills = System.currentTimeMillis();
- }
- mPackageUsage.write(false);
- if (!dexopt) {
- // We aren't going to dexopt, so bail early.
- return false;
- }
- targetInstructionSet = instructionSet != null ? instructionSet :
- getPrimaryInstructionSet(p.applicationInfo);
- if (p.mDexOptPerformed.contains(targetInstructionSet)) {
- return false;
- }
- }
- long callingId = Binder.clearCallingIdentity();
- try {
- synchronized (mInstallLock) {
- final String[] instructionSets = new String[] { targetInstructionSet };
- int result = mPackageDexOptimizer.performDexOpt(p, instructionSets,
- false /* forceDex */, false /* defer */, true /* inclDependencies */,
- true /* boot complete */);
- return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
- }
- } finally {
- Binder.restoreCallingIdentity(callingId);
+ 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);
+ }
+ });
}
}
- public ArraySet<String> getPackagesThatNeedDexOpt() {
- ArraySet<String> pkgs = null;
- synchronized (mPackages) {
+ /**
+ * 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, 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));
+ }
+
+ /*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<String>();
+ synchronized (mPackages) {
for (PackageParser.Package p : mPackages.values()) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, p.packageName + " mDexOptPerformed=" + p.mDexOptPerformed.toArray());
- }
- if (!p.mDexOptPerformed.isEmpty()) {
- continue;
- }
- if (pkgs == null) {
- pkgs = new ArraySet<String>();
+ if (PackageDexOptimizer.canOptimizePackage(p)) {
+ pkgs.add(p.packageName);
}
- 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<PackageParser.Package> deps = findSharedNonSystemLibraries(p);
+ final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
+ if (!deps.isEmpty()) {
+ DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
+ options.getCompilerFilter(), options.getSplitName(),
+ options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
+ for (PackageParser.Package depPackage : deps) {
+ // TODO: Analyze and investigate if we (should) profile libraries.
+ pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets,
+ getOrCreateCompilerPackageStats(depPackage),
+ mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions);
+ }
+ }
+ return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets,
+ getOrCreateCompilerPackageStats(p),
+ mDexManager.getPackageUseInfoOrDefault(p.packageName), options);
+ }
+
+ /**
+ * Reconcile the information we have about the secondary dex files belonging to
+ * {@code packagName} 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() {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return false;
+ }
+ return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext);
+ }
+
+ List<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
+ if (p.usesLibraries != null || p.usesOptionalLibraries != null
+ || p.usesStaticLibraries != null) {
+ ArrayList<PackageParser.Package> retValue = new ArrayList<>();
+ Set<String> collectedNames = new HashSet<>();
+ findSharedNonSystemLibrariesRecursive(p, retValue, collectedNames);
+
+ retValue.remove(p);
+
+ return retValue;
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ private void findSharedNonSystemLibrariesRecursive(PackageParser.Package p,
+ ArrayList<PackageParser.Package> collected, Set<String> collectedNames) {
+ if (!collectedNames.contains(p.packageName)) {
+ collectedNames.add(p.packageName);
+ collected.add(p);
+
+ if (p.usesLibraries != null) {
+ findSharedNonSystemLibrariesRecursive(p.usesLibraries,
+ null, collected, collectedNames);
+ }
+ if (p.usesOptionalLibraries != null) {
+ findSharedNonSystemLibrariesRecursive(p.usesOptionalLibraries,
+ null, collected, collectedNames);
+ }
+ if (p.usesStaticLibraries != null) {
+ findSharedNonSystemLibrariesRecursive(p.usesStaticLibraries,
+ p.usesStaticLibrariesVersions, collected, collectedNames);
+ }
+ }
+ }
+
+ private void findSharedNonSystemLibrariesRecursive(ArrayList<String> libs, int[] versions,
+ ArrayList<PackageParser.Package> collected, Set<String> collectedNames) {
+ final int libNameCount = libs.size();
+ for (int i = 0; i < libNameCount; i++) {
+ String libName = libs.get(i);
+ int version = (versions != null && versions.length == libNameCount)
+ ? versions[i] : PackageManager.VERSION_CODE_HIGHEST;
+ PackageParser.Package libPkg = findSharedNonSystemLibrary(libName, version);
+ if (libPkg != null) {
+ findSharedNonSystemLibrariesRecursive(libPkg, collected, collectedNames);
+ }
+ }
+ }
+
+ private PackageParser.Package findSharedNonSystemLibrary(String name, int version) {
+ synchronized (mPackages) {
+ SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(name, version);
+ if (libEntry != null) {
+ return mPackages.get(libEntry.apk);
+ }
+ return null;
+ }
+ }
+
+ private SharedLibraryEntry getSharedLibraryEntryLPr(String name, int version) {
+ SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
+ if (versionedLib == null) {
+ return null;
+ }
+ return versionedLib.get(version);
+ }
+
+ private SharedLibraryEntry getLatestSharedLibraVersionLPr(PackageParser.Package pkg) {
+ SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(
+ pkg.staticSharedLibName);
+ if (versionedLib == null) {
+ return null;
+ }
+ int previousLibVersion = -1;
+ final int versionCount = versionedLib.size();
+ for (int i = 0; i < versionCount; i++) {
+ final int libVersion = versionedLib.keyAt(i);
+ if (libVersion < pkg.staticSharedLibVersion) {
+ previousLibVersion = Math.max(previousLibVersion, libVersion);
+ }
+ }
+ if (previousLibVersion >= 0) {
+ return versionedLib.get(previousLibVersion);
+ }
+ return null;
+ }
+
public void shutdown() {
- mPackageUsage.write(true);
+ mPackageUsage.writeNow(mPackages);
+ mCompilerStats.writeNow();
+ mDexManager.writePackageDexUsageNow();
+ }
+
+ @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");
+ final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ try {
+ List<String> allCodePaths = pkg.getAllCodePathsExcludingResourceOnly();
+ String codePaths = TextUtils.join(";", allCodePaths);
+ mInstaller.dumpProfiles(sharedGid, packageName, codePaths);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to dump profiles", e);
+ }
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
@Override
synchronized (mPackages) {
pkg = mPackages.get(packageName);
if (pkg == null) {
- throw new IllegalArgumentException("Missing package: " + packageName);
+ throw new IllegalArgumentException("Unknown package: " + packageName);
}
}
synchronized (mInstallLock) {
- final String[] instructionSets = new String[] {
- getPrimaryInstructionSet(pkg.applicationInfo) };
- final int res = mPackageDexOptimizer.performDexOpt(pkg, instructionSets,
- true /*forceDex*/, false /* defer */, true /* inclDependencies */,
- true /* boot complete */);
+ 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);
}
return true;
}
- private int createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo) {
- int[] users = sUserManager.getUserIds();
- int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo);
- if (res < 0) {
- return res;
+ 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();
}
- for (int user : users) {
- if (user != 0) {
- res = mInstaller.createUserData(volumeUuid, packageName,
- UserHandle.getUid(user, uid), user, seinfo);
- if (res < 0) {
- return res;
- }
+ }
+
+ 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);
+ }
+ }
+
+ 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));
}
}
- return res;
}
- private int removeDataDirsLI(String volumeUuid, String packageName) {
- int[] users = sUserManager.getUserIds();
- int res = 0;
- for (int user : users) {
- int resInner = mInstaller.remove(volumeUuid, packageName, user);
- if (resInner < 0) {
- res = resInner;
+ 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);
}
+ }
- return res;
+ private void destroyAppProfilesLIF(PackageParser.Package pkg, int userId) {
+ if (pkg == null) {
+ Slog.wtf(TAG, "Package was null!", new Throwable());
+ return;
+ }
+ destroyAppProfilesLeafLIF(pkg);
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ destroyAppProfilesLeafLIF(pkg.childPackages.get(i));
+ }
+ }
+
+ private void destroyAppProfilesLeafLIF(PackageParser.Package pkg) {
+ try {
+ mInstaller.destroyAppProfiles(pkg.packageName);
+ } catch (InstallerException e) {
+ Slog.w(TAG, String.valueOf(e));
+ }
+ }
+
+ private void clearAppProfilesLIF(PackageParser.Package pkg, int userId) {
+ if (pkg == null) {
+ Slog.wtf(TAG, "Package was null!", new Throwable());
+ return;
+ }
+ clearAppProfilesLeafLIF(pkg);
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ clearAppProfilesLeafLIF(pkg.childPackages.get(i));
+ }
+ }
+
+ private void clearAppProfilesLeafLIF(PackageParser.Package pkg) {
+ try {
+ mInstaller.clearAppProfiles(pkg.packageName);
+ } catch (InstallerException e) {
+ Slog.w(TAG, String.valueOf(e));
+ }
}
- private int deleteCodeCacheDirsLI(String volumeUuid, String packageName) {
- int[] users = sUserManager.getUserIds();
- int res = 0;
- for (int user : users) {
- int resInner = mInstaller.deleteCodeCacheFiles(volumeUuid, packageName, user);
- if (resInner < 0) {
- res = resInner;
+ 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;
}
}
- return res;
}
- private void addSharedLibraryLPw(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file,
+ private void addSharedLibraryLPr(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file,
PackageParser.Package changingLib) {
if (file.path != null) {
usesLibraryFiles.add(file.path);
}
if (p != null) {
usesLibraryFiles.addAll(p.getAllCodePaths());
+ if (p.usesLibraryFiles != null) {
+ Collections.addAll(usesLibraryFiles, p.usesLibraryFiles);
+ }
}
}
- private void updateSharedLibrariesLPw(PackageParser.Package pkg,
+ private void updateSharedLibrariesLPr(PackageParser.Package pkg,
PackageParser.Package changingLib) throws PackageManagerException {
- if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) {
- final ArraySet<String> usesLibraryFiles = new ArraySet<>();
- int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0;
- for (int i=0; i<N; i++) {
- final SharedLibraryEntry file = mSharedLibraries.get(pkg.usesLibraries.get(i));
- if (file == null) {
+ if (pkg == null) {
+ return;
+ }
+ ArraySet<String> usesLibraryFiles = null;
+ if (pkg.usesLibraries != null) {
+ usesLibraryFiles = addSharedLibrariesLPw(pkg.usesLibraries,
+ null, null, pkg.packageName, changingLib, true,
+ pkg.applicationInfo.targetSdkVersion, null);
+ }
+ if (pkg.usesStaticLibraries != null) {
+ usesLibraryFiles = addSharedLibrariesLPw(pkg.usesStaticLibraries,
+ pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests,
+ pkg.packageName, changingLib, true,
+ pkg.applicationInfo.targetSdkVersion, usesLibraryFiles);
+ }
+ if (pkg.usesOptionalLibraries != null) {
+ usesLibraryFiles = addSharedLibrariesLPw(pkg.usesOptionalLibraries,
+ null, null, pkg.packageName, changingLib, false,
+ pkg.applicationInfo.targetSdkVersion, usesLibraryFiles);
+ }
+ if (!ArrayUtils.isEmpty(usesLibraryFiles)) {
+ pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[usesLibraryFiles.size()]);
+ } else {
+ pkg.usesLibraryFiles = null;
+ }
+ }
+
+ private ArraySet<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
+ @Nullable int[] requiredVersions, @Nullable String[][] requiredCertDigests,
+ @NonNull String packageName, @Nullable PackageParser.Package changingLib,
+ boolean required, int targetSdk, @Nullable ArraySet<String> outUsedLibraries)
+ throws PackageManagerException {
+ final int libCount = requestedLibraries.size();
+ for (int i = 0; i < libCount; i++) {
+ final String libName = requestedLibraries.get(i);
+ final int libVersion = requiredVersions != null ? requiredVersions[i]
+ : SharedLibraryInfo.VERSION_UNDEFINED;
+ final SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(libName, libVersion);
+ if (libEntry == null) {
+ if (required) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + pkg.packageName + " requires unavailable shared library "
- + pkg.usesLibraries.get(i) + "; failing!");
- }
- addSharedLibraryLPw(usesLibraryFiles, file, changingLib);
- }
- N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0;
- for (int i=0; i<N; i++) {
- final SharedLibraryEntry file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i));
- if (file == null) {
- Slog.w(TAG, "Package " + pkg.packageName
+ "Package " + packageName + " requires unavailable shared library "
+ + libName + "; failing!");
+ } else if (DEBUG_SHARED_LIBRARIES) {
+ Slog.i(TAG, "Package " + packageName
+ " desires unavailable shared library "
- + pkg.usesOptionalLibraries.get(i) + "; ignoring!");
- } else {
- addSharedLibraryLPw(usesLibraryFiles, file, changingLib);
+ + libName + "; ignoring!");
}
- }
- N = usesLibraryFiles.size();
- if (N > 0) {
- pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[N]);
} else {
- pkg.usesLibraryFiles = null;
+ if (requiredVersions != null && requiredCertDigests != null) {
+ if (libEntry.info.getVersion() != requiredVersions[i]) {
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires unavailable static shared"
+ + " library " + libName + " version "
+ + libEntry.info.getVersion() + "; failing!");
+ }
+
+ PackageParser.Package libPkg = mPackages.get(libEntry.apk);
+ if (libPkg == null) {
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires unavailable static shared"
+ + " library; failing!");
+ }
+
+ final String[] expectedCertDigests = requiredCertDigests[i];
+ // For apps targeting O MR1 we require explicit enumeration of all certs.
+ final String[] libCertDigests = (targetSdk > Build.VERSION_CODES.O)
+ ? PackageUtils.computeSignaturesSha256Digests(libPkg.mSignatures)
+ : PackageUtils.computeSignaturesSha256Digests(
+ new Signature[]{libPkg.mSignatures[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 sDexLoadReporter.java:45.19hared 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!");
+ }
+ }
+ }
+
+ if (outUsedLibraries == null) {
+ outUsedLibraries = new ArraySet<>();
+ }
+ addSharedLibraryLPr(outUsedLibraries, libEntry, changingLib);
}
}
+ return outUsedLibraries;
}
private static boolean hasString(List<String> list, List<String> which) {
return false;
}
- private void updateAllSharedLibrariesLPw() {
- for (PackageParser.Package pkg : mPackages.values()) {
- try {
- updateSharedLibrariesLPw(pkg, null);
- } catch (PackageManagerException e) {
- Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
- }
- }
- }
-
private ArrayList<PackageParser.Package> updateAllSharedLibrariesLPw(
PackageParser.Package changingPkg) {
ArrayList<PackageParser.Package> res = null;
for (PackageParser.Package pkg : mPackages.values()) {
- if (hasString(pkg.usesLibraries, changingPkg.libraryNames)
- || hasString(pkg.usesOptionalLibraries, changingPkg.libraryNames)) {
- if (res == null) {
- res = new ArrayList<PackageParser.Package>();
- }
- res.add(pkg);
- try {
- updateSharedLibrariesLPw(pkg, changingPkg);
- } catch (PackageManagerException e) {
- Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
+ if (changingPkg != null
+ && !hasString(pkg.usesLibraries, changingPkg.libraryNames)
+ && !hasString(pkg.usesOptionalLibraries, changingPkg.libraryNames)
+ && !ArrayUtils.contains(pkg.usesStaticLibraries,
+ changingPkg.staticSharedLibName)) {
+ return null;
+ }
+ if (res == null) {
+ res = new ArrayList<>();
+ }
+ res.add(pkg);
+ try {
+ updateSharedLibrariesLPr(pkg, changingPkg);
+ } 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.isSystemApp() || 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());
}
}
return res;
return cpuAbiOverride;
}
- private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
- int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
+ private PackageParser.Package scanPackageTracedLI(PackageParser.Package pkg,
+ final int policyFlags, 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 PackageParser.Package scannedPkg;
+ try {
+ // Scan the parent
+ scannedPkg = scanPackageLI(pkg, policyFlags, 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 childPkg = pkg.childPackages.get(i);
+ scanPackageLI(childPkg, policyFlags,
+ scanFlags, currentTime, user);
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
+ return scanPackageTracedLI(pkg, policyFlags, scanFlags, currentTime, user);
+ }
+
+ return scannedPkg;
+ }
+
+ private PackageParser.Package scanPackageLI(PackageParser.Package pkg, final int policyFlags,
+ int scanFlags, long currentTime, @Nullable UserHandle user)
+ throws PackageManagerException {
boolean success = false;
try {
- final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,
+ final PackageParser.Package res = scanPackageDirtyLI(pkg, policyFlags, scanFlags,
currentTime, user);
success = true;
return res;
} finally {
if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
- removeDataDirsLI(pkg.volumeUuid, pkg.packageName);
+ // DELETE_DATA_ON_FAILURES is only used by frozen paths
+ destroyAppDataLIF(pkg, UserHandle.USER_ALL,
+ StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+ destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
}
}
}
- private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
- int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
- final File scanFile = new File(pkg.codePath);
- 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");
+ /**
+ * 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;
+ }
- if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
- pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
- } else {
- // Only allow system apps to be flagged as core apps.
- pkg.coreApp = 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 ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {
- pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
- }
-
- if (mCustomResolverComponentName != null &&
- mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
- setUpCustomResolverActivity(pkg);
- }
-
- if (pkg.packageName.equals("android")) {
- synchronized (mPackages) {
- if (mAndroidApplication != null) {
- Slog.w(TAG, "*************************************************");
- Slog.w(TAG, "Core android package being redefined. Skipping.");
- Slog.w(TAG, " file=" + scanFile);
- Slog.w(TAG, "*************************************************");
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Core android package being redefined. Skipping.");
- }
-
- // Set up information for our fall-back user intent resolution activity.
- mPlatformPackage = pkg;
- pkg.mVersionCode = mSdkVersion;
- 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_Holo_Dialog_Alert;
- mResolveActivity.exported = true;
- mResolveActivity.enabled = true;
- mResolveInfo.activityInfo = mResolveActivity;
- mResolveInfo.priority = 0;
- mResolveInfo.preferredOrder = 0;
- mResolveInfo.match = 0;
- mResolveComponentName = new ComponentName(
- mAndroidApplication.packageName, mResolveActivity.name);
- }
- }
+ 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");
+ }
+ }
}
+ }
+ private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
+ final int policyFlags, final int scanFlags, long currentTime, @Nullable UserHandle user)
+ throws PackageManagerException {
if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+ if ((policyFlags & PackageParser.PARSE_CHATTY) != 0)
Log.d(TAG, "Scanning package " + pkg.packageName);
}
- if (mPackages.containsKey(pkg.packageName)
- || mSharedLibraries.containsKey(pkg.packageName)) {
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Application package " + pkg.packageName
- + " already installed. Skipping duplicate.");
- }
+ applyPolicy(pkg, policyFlags);
- // 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.peekPackageLPr(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.");
- }
- }
- }
- }
+ assertPackageIsValid(pkg, policyFlags, scanFlags);
// Initialize package source and resource directories
- File destCodeFile = new File(pkg.applicationInfo.getCodePath());
- File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
+ final File scanFile = new File(pkg.codePath);
+ final File destCodeFile = new File(pkg.applicationInfo.getCodePath());
+ final File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
SharedUserSetting suid = null;
PackageSetting pkgSetting = null;
- if (!isSystemApp(pkg)) {
- // Only system apps can use these features.
- pkg.mOriginalPackages = null;
- pkg.mRealPackage = null;
- pkg.mAdoptPermissions = null;
- }
+ // Getting the package setting may have a side-effect, so if we
+ // are only checking if scan would succeed, stash a copy of the
+ // old setting to restore at the end.
+ PackageSetting nonMutatedPs = null;
+
+ // 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;
+
+ final PackageParser.Package oldPkg;
// writer
synchronized (mPackages) {
if (pkg.mSharedUserId != null) {
- suid = mSettings.getSharedUserLPw(pkg.mSharedUserId, 0, 0, true);
- if (suid == null) {
- throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
- "Creating application package " + pkg.packageName
- + " for shared user failed");
- }
+ // SIDE EFFECTS; may potentially allocate a new shared user
+ suid = mSettings.getSharedUserLPw(
+ pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+ if ((policyFlags & PackageParser.PARSE_CHATTY) != 0)
Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + suid.userId
+ "): packages=" + suid.packages);
}
if (pkg.mOriginalPackages != null) {
// This package may need to be renamed to a previously
// installed name. Let's check on that...
- final String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage);
+ final String renamed = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
if (pkg.mOriginalPackages.contains(renamed)) {
// This package had originally been installed as the
// original name, and we have already taken care of
// it is not already done.
pkg.setPackageName(renamed);
}
-
} else {
for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) {
- if ((origPackage = mSettings.peekPackageLPr(
+ if ((origPackage = mSettings.getPackageLPr(
pkg.mOriginalPackages.get(i))) != null) {
// We do have the package already installed under its
// original name... should we use it?
origPackage = null;
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 " + origPackage.name);
+ " was transferred to another, but its .apk remains");
}
- // Just create the setting, don't add it yet. For already existing packages
- // the PkgSetting exists already and doesn't have to be created.
- pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
- destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
- pkg.applicationInfo.primaryCpuAbi,
- pkg.applicationInfo.secondaryCpuAbi,
- pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
- user, false);
- if (pkgSetting == null) {
- throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
- "Creating application package " + pkg.packageName + " failed");
+ // See comments in nonMutatedPs declaration
+ if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
+ PackageSetting foundPs = mSettings.getPackageLPr(pkg.packageName);
+ if (foundPs != null) {
+ nonMutatedPs = new PackageSetting(foundPs);
+ }
+ }
+
+ if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) == 0) {
+ PackageSetting foundPs = mSettings.getPackageLPr(pkg.packageName);
+ if (foundPs != null) {
+ primaryCpuAbiFromSettings = foundPs.primaryCpuAbiString;
+ secondaryCpuAbiFromSettings = foundPs.secondaryCpuAbiString;
+ }
+ }
+
+ pkgSetting = mSettings.getPackageLPr(pkg.packageName);
+ if (pkgSetting != null && pkgSetting.sharedUser != suid) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Package " + pkg.packageName + " shared user changed from "
+ + (pkgSetting.sharedUser != null
+ ? pkgSetting.sharedUser.name : "<nothing>")
+ + " to "
+ + (suid != null ? suid.name : "<nothing>")
+ + "; replacing with new");
+ pkgSetting = null;
+ }
+ final PackageSetting oldPkgSetting =
+ pkgSetting == null ? null : new PackageSetting(pkgSetting);
+ final PackageSetting disabledPkgSetting =
+ mSettings.getDisabledSystemPkgLPr(pkg.packageName);
+
+ if (oldPkgSetting == null) {
+ oldPkg = null;
+ } else {
+ oldPkg = oldPkgSetting.pkg;
+ }
+
+ String[] usesStaticLibraries = null;
+ if (pkg.usesStaticLibraries != null) {
+ usesStaticLibraries = new String[pkg.usesStaticLibraries.size()];
+ pkg.usesStaticLibraries.toArray(usesStaticLibraries);
}
+ if (pkgSetting == null) {
+ 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, origPackage,
+ disabledPkgSetting, realName, suid, 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);
+ // SIDE EFFECTS; updates system state; move elsewhere
+ if (origPackage != null) {
+ mSettings.addRenamedPackageLPw(pkg.packageName, origPackage.name);
+ }
+ mSettings.addUserToSettingLPw(pkgSetting);
+ } else {
+ // 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, suid, destCodeFile,
+ pkg.applicationInfo.nativeLibraryDir, pkg.applicationInfo.primaryCpuAbi,
+ pkg.applicationInfo.secondaryCpuAbi, pkg.applicationInfo.flags,
+ pkg.applicationInfo.privateFlags, pkg.getChildPackageNames(),
+ UserManagerService.getInstance(), usesStaticLibraries,
+ pkg.usesStaticLibrariesVersions);
+ }
+ // SIDE EFFECTS; persists system state to files on disk; move elsewhere
+ mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
+
+ // SIDE EFFECTS; modifies system state; move elsewhere
if (pkgSetting.origPackage != null) {
// If we are first transitioning from an original package,
// fix up the new package's name now. We need to do this after
reportSettingsProblem(Log.WARN, msg);
// Make a note of it.
- mTransferedPackages.add(origPackage.name);
+ if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
+ mTransferedPackages.add(origPackage.name);
+ }
// No longer need to retain this.
pkgSetting.origPackage = null;
}
- if (realName != null) {
+ // SIDE EFFECTS; modifies system state; move elsewhere
+ if ((scanFlags & SCAN_CHECK_ONLY) == 0 && realName != null) {
// Make a note of it.
mTransferedPackages.add(pkg.packageName);
}
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
- if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+ if ((scanFlags & SCAN_BOOTING) == 0
+ && (policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
// 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.
- updateSharedLibrariesLPw(pkg, null);
+ // 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.
+ updateSharedLibrariesLPr(pkg, null);
}
if (mFoundPolicyFile) {
- SELinuxMMAC.assignSeinfoValue(pkg);
+ SELinuxMMAC.assignSeInfoValue(pkg);
}
-
pkg.applicationInfo.uid = pkgSetting.appId;
pkg.mExtras = pkgSetting;
- if (shouldCheckUpgradeKeySetLP(pkgSetting, scanFlags)) {
- if (checkUpgradeKeySetLP(pkgSetting, pkg)) {
+
+
+ // 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 = pkgSetting;
+ if (pkg.applicationInfo.isStaticSharedLibrary()) {
+ SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
+ if (libraryEntry != null) {
+ signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
+ }
+ }
+
+ if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) {
+ if (checkUpgradeKeySetLP(signatureCheckPs, pkg)) {
// We just determined the app is signed correctly, so bring
// over the latest parsed certs.
pkgSetting.signatures.mSignatures = pkg.mSignatures;
} else {
- if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+ if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"Package " + pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
} else {
pkgSetting.signatures.mSignatures = pkg.mSignatures;
String msg = "System package " + pkg.packageName
- + " signature changed; retaining data.";
+ + " signature changed; retaining data.";
reportSettingsProblem(Log.WARN, msg);
}
}
} else {
try {
- verifySignaturesLP(pkgSetting, pkg);
+ // SIDE EFFECTS; compareSignaturesCompat() changes KeysetManagerService
+ verifySignaturesLP(signatureCheckPs, pkg);
// We just determined the app is signed correctly, so bring
// over the latest parsed certs.
pkgSetting.signatures.mSignatures = pkg.mSignatures;
} catch (PackageManagerException e) {
- if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+ if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
throw e;
}
// The signature has changed, but this package is in the system
// What this means is that you can't change the signatures
// associated with an overall shared user, which doesn't seem all
// that unreasonable.
- if (pkgSetting.sharedUser != null) {
- if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
- pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
+ if (signatureCheckPs.sharedUser != null) {
+ if (compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures,
+ pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
throw new PackageManagerException(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
- "Signature mismatch for shared user : "
+ "Signature mismatch for shared user: "
+ pkgSetting.sharedUser);
}
}
// File a report about this.
String msg = "System package " + pkg.packageName
- + " signature changed; retaining data.";
+ + " signature changed; retaining data.";
reportSettingsProblem(Log.WARN, msg);
}
}
- // 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) {
- final int N = pkg.providers.size();
- int i;
- for (i=0; i<N; i++) {
- PackageParser.Provider p = pkg.providers.get(i);
- if (p.info.authority != null) {
- String names[] = p.info.authority.split(";");
- for (int j = 0; j < names.length; j++) {
- if (mProvidersByAuthority.containsKey(names[j])) {
- PackageParser.Provider other = mProvidersByAuthority.get(names[j]);
- final String otherPackageName =
- ((other != null && other.getComponentName() != null) ?
- other.getComponentName().getPackageName() : "?");
- throw new PackageManagerException(
- INSTALL_FAILED_CONFLICTING_PROVIDER,
- "Can't install because provider name " + names[j]
- + " (in package " + pkg.applicationInfo.packageName
- + ") is already used by " + otherPackageName);
- }
- }
- }
- }
- }
- if (pkg.mAdoptPermissions != null) {
+ 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.peekPackageLPr(origName);
+ final PackageSetting orig = mSettings.getPackageLPr(origName);
if (orig != null) {
if (verifyPackageUpdateLPr(orig, pkg)) {
Slog.i(TAG, "Adopting permissions from " + origName + " to "
+ pkg.packageName);
+ // SIDE EFFECTS; updates permissions system state; move elsewhere
mSettings.transferPermissionsLPw(origName, pkg.packageName);
}
}
}
}
- final String pkgName = pkg.packageName;
-
- final long scanFileTime = scanFile.lastModified();
- final boolean forceDex = (scanFlags & SCAN_FORCE_DEX) != 0;
pkg.applicationInfo.processName = fixProcessName(
pkg.applicationInfo.packageName,
- pkg.applicationInfo.processName,
- pkg.applicationInfo.uid);
-
- File dataPath;
- if (mPlatformPackage == pkg) {
- // The system package is special.
- dataPath = new File(Environment.getDataDirectory(), "system");
-
- pkg.applicationInfo.dataDir = dataPath.getPath();
-
- } else {
- // This is a normal package, need to make its data directory.
- dataPath = Environment.getDataUserPackageDirectory(pkg.volumeUuid,
- UserHandle.USER_OWNER, pkg.packageName);
+ pkg.applicationInfo.processName);
- boolean uidError = false;
- if (dataPath.exists()) {
- int currentUid = 0;
- try {
- StructStat stat = Os.stat(dataPath.getPath());
- currentUid = stat.st_uid;
- } catch (ErrnoException e) {
- Slog.e(TAG, "Couldn't stat path " + dataPath.getPath(), e);
- }
-
- // If we have mismatched owners for the data path, we have a problem.
- if (currentUid != pkg.applicationInfo.uid) {
- boolean recovered = false;
- if (currentUid == 0) {
- // The directory somehow became owned by root. Wow.
- // This is probably because the system was stopped while
- // installd was in the middle of messing with its libs
- // directory. Ask installd to fix that.
- int ret = mInstaller.fixUid(pkg.volumeUuid, pkgName,
- pkg.applicationInfo.uid, pkg.applicationInfo.uid);
- if (ret >= 0) {
- recovered = true;
- String msg = "Package " + pkg.packageName
- + " unexpectedly changed to uid 0; recovered to " +
- + pkg.applicationInfo.uid;
- reportSettingsProblem(Log.WARN, msg);
- }
- }
- if (!recovered && ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
- || (scanFlags&SCAN_BOOTING) != 0)) {
- // If this is a system app, we can at least delete its
- // current data so the application will still work.
- int ret = removeDataDirsLI(pkg.volumeUuid, pkgName);
- if (ret >= 0) {
- // TODO: Kill the processes first
- // Old data gone!
- String prefix = (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
- ? "System package " : "Third party package ";
- String msg = prefix + pkg.packageName
- + " has changed from uid: "
- + currentUid + " to "
- + pkg.applicationInfo.uid + "; old data erased";
- reportSettingsProblem(Log.WARN, msg);
- recovered = true;
-
- // And now re-install the app.
- ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
- pkg.applicationInfo.seinfo);
- if (ret == -1) {
- // Ack should not happen!
- msg = prefix + pkg.packageName
- + " could not have data directory re-created after delete.";
- reportSettingsProblem(Log.WARN, msg);
- throw new PackageManagerException(
- INSTALL_FAILED_INSUFFICIENT_STORAGE, msg);
- }
- }
- if (!recovered) {
- mHasSystemUidErrors = true;
- }
- } else if (!recovered) {
- // If we allow this install to proceed, we will be broken.
- // Abort, abort!
- throw new PackageManagerException(INSTALL_FAILED_UID_CHANGED,
- "scanPackageLI");
- }
- if (!recovered) {
- pkg.applicationInfo.dataDir = "/mismatched_uid/settings_"
- + pkg.applicationInfo.uid + "/fs_"
- + currentUid;
- pkg.applicationInfo.nativeLibraryDir = pkg.applicationInfo.dataDir;
- pkg.applicationInfo.nativeLibraryRootDir = pkg.applicationInfo.dataDir;
- String msg = "Package " + pkg.packageName
- + " has mismatched uid: "
- + currentUid + " on disk, "
- + pkg.applicationInfo.uid + " in settings";
- // writer
- synchronized (mPackages) {
- mSettings.mReadMessages.append(msg);
- mSettings.mReadMessages.append('\n');
- uidError = true;
- if (!pkgSetting.uidError) {
- reportSettingsProblem(Log.ERROR, msg);
- }
- }
- }
- }
- pkg.applicationInfo.dataDir = dataPath.getPath();
- if (mShouldRestoreconData) {
- Slog.i(TAG, "SELinux relabeling of " + pkg.packageName + " issued.");
- mInstaller.restoreconData(pkg.volumeUuid, pkg.packageName,
- pkg.applicationInfo.seinfo, pkg.applicationInfo.uid);
- }
- } else {
- if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
- Log.v(TAG, "Want this data dir: " + dataPath);
- }
- //invoke installer to do the actual installation
- int ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
- pkg.applicationInfo.seinfo);
- if (ret < 0) {
- // Error from installer
- throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
- "Unable to create data dirs [errorCode=" + ret + "]");
- }
-
- if (dataPath.exists()) {
- pkg.applicationInfo.dataDir = dataPath.getPath();
- } else {
- Slog.w(TAG, "Unable to create data directory: " + dataPath);
- pkg.applicationInfo.dataDir = null;
- }
- }
-
- pkgSetting.uidError = uidError;
+ if (pkg != mPlatformPackage) {
+ // Get all of our default paths setup
+ pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
}
- final String path = scanFile.getPath();
final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
- derivePackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */);
+ if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
+ final boolean extractNativeLibs = !pkg.isLibrary();
+ derivePackageAbi(pkg, scanFile, cpuAbiOverride, extractNativeLibs,
+ mAppLib32InstallDir);
+ 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, mAppLib32InstallDir);
+ }
+ } 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;
- // 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);
- }
+ setNativeLibraryPaths(pkg, mAppLib32InstallDir);
+ 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)
// 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);
- }
-
- if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
- final int[] userIds = sUserManager.getUserIds();
- synchronized (mInstallLock) {
- // Make sure all user data directories are ready to roll; we're okay
- // if they already exist
- if (!TextUtils.isEmpty(pkg.volumeUuid)) {
- for (int userId : userIds) {
- if (userId != 0) {
- mInstaller.createUserData(pkg.volumeUuid, pkg.packageName,
- UserHandle.getUid(userId, pkg.applicationInfo.uid), userId,
- pkg.applicationInfo.seinfo);
- }
- }
- }
-
- // 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 (pkg.applicationInfo.primaryCpuAbi != null &&
- !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
- final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
- for (int userId : userIds) {
- if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
- nativeLibPath, userId) < 0) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Failed linking native library dir (user=" + userId + ")");
- }
- }
- }
+ setNativeLibraryPaths(pkg, mAppLib32InstallDir);
}
// This is a special case for the "system" package, where the ABI is
if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
if (cpuAbiOverride == null && pkgSetting.cpuAbiOverrideString != null) {
Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
- " for package: " + pkg.packageName);
+ " for package " + pkg.packageName);
}
}
" secondary=" + pkg.applicationInfo.secondaryCpuAbi);
}
- if ((scanFlags&SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
+ // SIDE EFFECTS; removes DEX files from disk; move elsewhere
+ 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.
- adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages,
- pkg, forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, true /* boot complete */);
+ adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, pkg);
}
- if ((scanFlags & SCAN_NO_DEX) == 0) {
- int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instruction sets */,
- forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */,
- (scanFlags & SCAN_BOOTING) == 0);
- if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- throw new PackageManagerException(INSTALL_FAILED_DEXOPT, "scanPackageLI");
- }
- }
if (mFactoryTest && pkg.requestedPermissions.contains(
android.Manifest.permission.FACTORY_TEST)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
}
- ArrayList<PackageParser.Package> clientLibPkgs = null;
+ if (isSystemApp(pkg)) {
+ pkgSetting.isOrphaned = true;
+ }
- // writer
- synchronized (mPackages) {
- if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
- // Only system apps can add new shared libraries.
- if (pkg.libraryNames != null) {
- for (int i=0; i<pkg.libraryNames.size(); i++) {
- String name = pkg.libraryNames.get(i);
- boolean allowed = false;
- if (pkg.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.
- final PackageSetting sysPs = mSettings
- .getDisabledSystemPkgLPr(pkg.packageName);
- if (sysPs.pkg != null && sysPs.pkg.libraryNames != null) {
- for (int j=0; j<sysPs.pkg.libraryNames.size(); j++) {
- if (name.equals(sysPs.pkg.libraryNames.get(j))) {
- allowed = true;
- allowed = true;
- break;
- }
- }
- }
- } else {
- allowed = true;
- }
- if (allowed) {
- if (!mSharedLibraries.containsKey(name)) {
- mSharedLibraries.put(name, new SharedLibraryEntry(null, pkg.packageName));
- } else if (!name.equals(pkg.packageName)) {
- Slog.w(TAG, "Package " + pkg.packageName + " library "
- + name + " already exists; skipping");
- }
- } else {
- Slog.w(TAG, "Package " + pkg.packageName + " declares lib "
- + name + " that is not declared on system image; skipping");
- }
- }
- if ((scanFlags&SCAN_BOOTING) == 0) {
- // If we are not booting, we need to update any applications
- // that are clients of our shared library. If we are booting,
- // this will all be done once the scan is complete.
- clientLibPkgs = updateAllSharedLibrariesLPw(pkg);
- }
- }
+ // Take care of first install / last update times.
+ final long scanFileTime = getLastModifiedTime(pkg, scanFile);
+ 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 ((policyFlags & 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);
- // We also need to dexopt any apps that are dependent on this library. Note that
- // if these fail, we should abort the install since installing the library will
- // result in some apps being broken.
- if (clientLibPkgs != null) {
- if ((scanFlags & SCAN_NO_DEX) == 0) {
- for (int i = 0; i < clientLibPkgs.size(); i++) {
- PackageParser.Package clientPkg = clientLibPkgs.get(i);
- int result = mPackageDexOptimizer.performDexOpt(clientPkg,
- null /* instruction sets */, forceDex,
- (scanFlags & SCAN_DEFER_DEX) != 0, false,
- (scanFlags & SCAN_BOOTING) == 0);
- if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- throw new PackageManagerException(INSTALL_FAILED_DEXOPT,
- "scanPackageLI failed to dexopt clientLibPkgs");
- }
+ if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
+ if (nonMutatedPs != null) {
+ synchronized (mPackages) {
+ mSettings.mPackages.put(nonMutatedPs.name, nonMutatedPs);
}
}
+ } else {
+ final int userId = user == null ? 0 : user.getIdentifier();
+ // Modify state for the given package setting
+ commitPackageSettings(pkg, pkgSetting, user, scanFlags,
+ (policyFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
+ if (pkgSetting.getInstantApp(userId)) {
+ mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
+ }
}
- // 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.
- if ((scanFlags & SCAN_REPLACING) != 0) {
- killApplication(pkg.applicationInfo.packageName,
- pkg.applicationInfo.uid, "replace pkg");
- }
+ if (oldPkg != null) {
+ // We need to call revokeRuntimePermissionsIfGroupChanged async as permission
+ // revokation 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());
- // 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");
- }
+ AsyncTask.execute(new Runnable() {
+ public void run() {
+ revokeRuntimePermissionsIfGroupChanged(pkg, oldPkg, allPackageNames);
+ }
+ });
}
- // Make sure we're not adding any bogus keyset info
- KeySetManagerService ksms = mSettings.mKeySetManagerService;
- ksms.assertScannedPackageValid(pkg);
-
- // writer
- synchronized (mPackages) {
- // We don't expect installation to fail beyond this point
+ return pkg;
+ }
- // Add the new setting to mSettings
- mSettings.insertPackageSettingLPw(pkgSetting, pkg);
- // Add the new setting to mPackages
- mPackages.put(pkg.applicationInfo.packageName, pkg);
- // Make sure we don't accidentally delete its data.
- final Iterator<PackageCleanItem> iter = mSettings.mPackagesToBeCleaned.iterator();
- while (iter.hasNext()) {
- PackageCleanItem item = iter.next();
- if (pkgName.equals(item.packageName)) {
- iter.remove();
+ /**
+ * 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 void applyPolicy(PackageParser.Package pkg, int policyFlags) {
+ if ((policyFlags&PackageParser.PARSE_IS_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;
}
- }
-
- // Take care of first install / last update times.
- if (currentTime != 0) {
- if (pkgSetting.firstInstallTime == 0) {
- pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
- } else if ((scanFlags&SCAN_UPDATE_TIME) != 0) {
- pkgSetting.lastUpdateTime = currentTime;
+ 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;
}
- } 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;
+ for (PackageParser.Activity r : pkg.receivers) {
+ r.info.encryptionAware = r.info.directBootAware = true;
}
}
+ if (compressedFileExists(pkg.codePath)) {
+ pkg.isStub = true;
+ }
+ } else {
+ // Only allow system apps to be flagged as core apps.
+ pkg.coreApp = false;
+ // clear flags not applicable to regular apps
+ pkg.applicationInfo.privateFlags &=
+ ~ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE;
+ pkg.applicationInfo.privateFlags &=
+ ~ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
+ }
+ pkg.mTrustedOverlay = (policyFlags&PackageParser.PARSE_TRUSTED_OVERLAY) != 0;
- // Add the package's KeySets to the global KeySetManagerService
- ksms.addScannedPackageLPw(pkg);
+ if ((policyFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {
+ pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+ }
- int N = pkg.providers.size();
- StringBuilder r = null;
- int i;
- for (i=0; i<N; i++) {
- PackageParser.Provider p = pkg.providers.get(i);
- p.info.processName = fixProcessName(pkg.applicationInfo.processName,
- p.info.processName, pkg.applicationInfo.uid);
- mProviders.addProvider(p);
- p.syncable = p.info.isSyncable;
- if (p.info.authority != null) {
- String names[] = p.info.authority.split(";");
+ if (!isSystemApp(pkg)) {
+ // Only system apps can use these features.
+ pkg.mOriginalPackages = null;
+ pkg.mRealPackage = null;
+ pkg.mAdoptPermissions = null;
+ }
+ }
+
+ /**
+ * 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, int policyFlags, int scanFlags)
+ throws PackageManagerException {
+ if ((policyFlags & PackageParser.PARSE_ENFORCE_CODE) != 0) {
+ assertCodePolicy(pkg);
+ }
+
+ if (pkg.applicationInfo.getCodePath() == null ||
+ pkg.applicationInfo.getResourcePath() == null) {
+ // Bail out. The resource and code paths haven't been set.
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Code and resource paths haven't been set correctly");
+ }
+
+ // Make sure we're not adding any bogus keyset info
+ 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 (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 (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
+ int minVersionCode = Integer.MIN_VALUE;
+ int maxVersionCode = Integer.MAX_VALUE;
+
+ SparseArray<SharedLibraryEntry> 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).info;
+ final int libVersionCode = libInfo.getDeclaringPackage()
+ .getVersionCode();
+ if (libInfo.getVersion() < pkg.staticSharedLibVersion) {
+ minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
+ } else if (libInfo.getVersion() > pkg.staticSharedLibVersion) {
+ maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
+ } else {
+ minVersionCode = maxVersionCode = libVersionCode;
+ break;
+ }
+ }
+ }
+ if (pkg.mVersionCode < minVersionCode || pkg.mVersionCode > 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 ((policyFlags & PARSE_IS_PRIVILEGED) == 0) {
+ throw new PackageManagerException("Only privileged apps can add child "
+ + "packages. Ignoring package " + pkg.packageName);
+ }
+ final int childCount = pkg.childPackages.size();
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ if (mSettings.hasOtherDisabledSystemPkgWithChildLPr(pkg.packageName,
+ childPkg.packageName)) {
+ throw new PackageManagerException("Can't override child of "
+ + "another disabled app. Ignoring package " + pkg.packageName);
+ }
+ }
+ }
+
+ // If we're only installing presumed-existing packages, require that the
+ // scanned APK is both already known and at the path previously established
+ // for it. Previously unknown packages we pick up normally, but if we have an
+ // a priori expectation about this package's install presence, enforce it.
+ // With a singular exception for new system packages. When an OTA contains
+ // a new system package, we allow the codepath to change from a system location
+ // to the user-installed location. If we don't allow this change, any newer,
+ // user-installed version of the application will be ignored.
+ if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
+ if (mExpectingBetter.containsKey(pkg.packageName)) {
+ logCriticalInfo(Log.WARN,
+ "Relax SCAN_REQUIRE_KNOWN requirement for package " + pkg.packageName);
+ } else {
+ PackageSetting known = mSettings.getPackageLPr(pkg.packageName);
+ if (known != null) {
+ if (DEBUG_PACKAGE_SCANNING) {
+ Log.d(TAG, "Examining " + pkg.codePath
+ + " and requiring known paths " + known.codePathString
+ + " & " + known.resourcePathString);
+ }
+ if (!pkg.applicationInfo.getCodePath().equals(known.codePathString)
+ || !pkg.applicationInfo.getResourcePath().equals(
+ known.resourcePathString)) {
+ throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
+ "Application package " + pkg.packageName
+ + " found at " + pkg.applicationInfo.getCodePath()
+ + " but expected at " + known.codePathString
+ + "; ignoring.");
+ }
+ } else {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ "Application package " + pkg.packageName
+ + " not found; ignoring.");
+ }
+ }
+ }
+
+ // Verify that this new package doesn't have any content providers
+ // that conflict with existing packages. Only do this if the
+ // package isn't already installed, since we don't want to break
+ // things that are installed.
+ if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
+ final int N = pkg.providers.size();
+ int i;
+ for (i=0; i<N; i++) {
+ PackageParser.Provider p = pkg.providers.get(i);
+ if (p.info.authority != null) {
+ String names[] = p.info.authority.split(";");
+ for (int j = 0; j < names.length; j++) {
+ if (mProvidersByAuthority.containsKey(names[j])) {
+ PackageParser.Provider other = mProvidersByAuthority.get(names[j]);
+ final String otherPackageName =
+ ((other != null && other.getComponentName() != null) ?
+ other.getComponentName().getPackageName() : "?");
+ throw new PackageManagerException(
+ INSTALL_FAILED_CONFLICTING_PROVIDER,
+ "Can't install because provider name " + names[j]
+ + " (in package " + pkg.applicationInfo.packageName
+ + ") is already used by " + otherPackageName);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private boolean addSharedLibraryLPw(String path, String apk, String name, int version,
+ int type, String declaringPackageName, int declaringVersionCode) {
+ SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
+ if (versionedLib == null) {
+ versionedLib = new SparseArray<>();
+ mSharedLibraries.put(name, versionedLib);
+ if (type == SharedLibraryInfo.TYPE_STATIC) {
+ mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib);
+ }
+ } else if (versionedLib.indexOfKey(version) >= 0) {
+ return false;
+ }
+ SharedLibraryEntry libEntry = new SharedLibraryEntry(path, apk, name,
+ version, type, declaringPackageName, declaringVersionCode);
+ versionedLib.put(version, libEntry);
+ return true;
+ }
+
+ private boolean removeSharedLibraryLPw(String name, int version) {
+ SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
+ if (versionedLib == null) {
+ return false;
+ }
+ final int libIdx = versionedLib.indexOfKey(version);
+ if (libIdx < 0) {
+ return false;
+ }
+ SharedLibraryEntry libEntry = versionedLib.valueAt(libIdx);
+ versionedLib.remove(version);
+ if (versionedLib.size() <= 0) {
+ mSharedLibraries.remove(name);
+ if (libEntry.info.getType() == SharedLibraryInfo.TYPE_STATIC) {
+ mStaticLibsByDeclaringPackage.remove(libEntry.info.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, PackageSetting pkgSetting,
+ UserHandle user, int scanFlags, boolean chatty) throws PackageManagerException {
+ 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;
+ 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) {
+ boolean hasStaticSharedLibs = false;
+
+ // Any app can add new static shared libraries
+ if (pkg.staticSharedLibName != null) {
+ // Static shared libs don't allow renaming as they have synthetic package
+ // names to allow install of multiple versions, so use name from manifest.
+ if (addSharedLibraryLPw(null, pkg.packageName, pkg.staticSharedLibName,
+ pkg.staticSharedLibVersion, SharedLibraryInfo.TYPE_STATIC,
+ pkg.manifestPackageName, pkg.mVersionCode)) {
+ hasStaticSharedLibs = true;
+ } else {
+ Slog.w(TAG, "Package " + pkg.packageName + " library "
+ + pkg.staticSharedLibName + " already exists; skipping");
+ }
+ // Static shared libs cannot be updated once installed since they
+ // use synthetic package name which includes the version code, so
+ // not need to update other packages's shared lib dependencies.
+ }
+
+ if (!hasStaticSharedLibs
+ && (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ // Only system apps can add new dynamic shared libraries.
+ if (pkg.libraryNames != null) {
+ for (int i = 0; i < pkg.libraryNames.size(); i++) {
+ String name = pkg.libraryNames.get(i);
+ boolean allowed = false;
+ if (pkg.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.
+ final PackageSetting sysPs = mSettings
+ .getDisabledSystemPkgLPr(pkg.packageName);
+ if (sysPs.pkg != null && sysPs.pkg.libraryNames != null) {
+ for (int j = 0; j < sysPs.pkg.libraryNames.size(); j++) {
+ if (name.equals(sysPs.pkg.libraryNames.get(j))) {
+ allowed = true;
+ break;
+ }
+ }
+ }
+ } else {
+ allowed = true;
+ }
+ if (allowed) {
+ if (!addSharedLibraryLPw(null, pkg.packageName, name,
+ SharedLibraryInfo.VERSION_UNDEFINED,
+ SharedLibraryInfo.TYPE_DYNAMIC,
+ pkg.packageName, pkg.mVersionCode)) {
+ Slog.w(TAG, "Package " + pkg.packageName + " library "
+ + name + " already exists; skipping");
+ }
+ } else {
+ Slog.w(TAG, "Package " + pkg.packageName + " declares lib "
+ + name + " that is not declared on system image; skipping");
+ }
+ }
+
+ if ((scanFlags & SCAN_BOOTING) == 0) {
+ // If we are not booting, we need to update any applications
+ // that are clients of our shared library. If we are booting,
+ // this will all be done once the scan is complete.
+ clientLibPkgs = updateAllSharedLibrariesLPw(pkg);
+ }
+ }
+ }
+ }
+
+ 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);
+ // Make sure we don't accidentally delete its data.
+ final Iterator<PackageCleanItem> iter = mSettings.mPackagesToBeCleaned.iterator();
+ while (iter.hasNext()) {
+ PackageCleanItem item = iter.next();
+ if (pkgName.equals(item.packageName)) {
+ iter.remove();
+ }
+ }
+
+ // Add the package's KeySets to the global KeySetManagerService
+ KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ ksms.addScannedPackageLPw(pkg);
+
+ int N = pkg.providers.size();
+ StringBuilder r = null;
+ int i;
+ for (i=0; i<N; i++) {
+ PackageParser.Provider p = pkg.providers.get(i);
+ p.info.processName = fixProcessName(pkg.applicationInfo.processName,
+ p.info.processName);
+ mProviders.addProvider(p);
+ p.syncable = p.info.isSyncable;
+ if (p.info.authority != null) {
+ String names[] = p.info.authority.split(";");
p.info.authority = null;
for (int j = 0; j < names.length; j++) {
if (j == 1 && p.syncable) {
p.info.authority = p.info.authority + ";" + names[j];
}
if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+ if (chatty)
Log.d(TAG, "Registered content provider: " + names[j]
+ ", className = " + p.info.name + ", isSyncable = "
+ p.info.isSyncable);
}
}
}
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+ if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
for (i=0; i<N; i++) {
PackageParser.Service s = pkg.services.get(i);
s.info.processName = fixProcessName(pkg.applicationInfo.processName,
- s.info.processName, pkg.applicationInfo.uid);
+ s.info.processName);
mServices.addService(s);
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+ if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.receivers.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
- a.info.processName, pkg.applicationInfo.uid);
+ a.info.processName);
mReceivers.addActivity(a, "receiver");
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+ if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.activities.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
- a.info.processName, pkg.applicationInfo.uid);
+ a.info.processName);
mActivities.addActivity(a, "activity");
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+ if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
for (i=0; i<N; i++) {
PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i);
PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name);
- if (cur == null) {
+ final String curPackageName = cur == null ? null : cur.info.packageName;
+ // Dont allow ephemeral apps to define new permission groups.
+ if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
+ Slog.w(TAG, "Permission group " + pg.info.name + " from package "
+ + pg.info.packageName
+ + " ignored: instant apps cannot define new permission groups.");
+ continue;
+ }
+ final boolean isPackageUpdate = pg.info.packageName.equals(curPackageName);
+ if (cur == null || isPackageUpdate) {
mPermissionGroups.put(pg.info.name, pg);
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+ if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
+ if (isPackageUpdate) {
+ r.append("UPD:");
+ }
r.append(pg.info.name);
}
} else {
Slog.w(TAG, "Permission group " + pg.info.name + " from package "
+ pg.info.packageName + " ignored: original from "
+ cur.info.packageName);
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+ if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
for (i=0; i<N; i++) {
PackageParser.Permission p = pkg.permissions.get(i);
+ // Dont allow ephemeral apps to define new permissions.
+ if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
+ Slog.w(TAG, "Permission " + p.info.name + " from package "
+ + p.info.packageName
+ + " ignored: instant apps cannot define new permissions.");
+ continue;
+ }
+
// Assume by default that we did not install this permission into the system.
p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;
// Now that permission groups have a special meaning, we ignore permission
// groups for legacy apps to prevent unexpected behavior. In particular,
- // permissions for one app being granted to someone just becuase they happen
+ // permissions for one app being granted to someone just because they happen
// to be in a group defined by another app (before this had no implications).
if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
p.group = mPermissionGroups.get(p.info.group);
// Warn for a permission in an unknown group.
- if (p.info.group != null && p.group == null) {
- Slog.w(TAG, "Permission " + p.info.name + " from package "
+ if (DEBUG_PERMISSIONS && p.info.group != null && p.group == null) {
+ Slog.i(TAG, "Permission " + p.info.name + " from package "
+ p.info.packageName + " in an unknown group " + p.info.group);
}
}
bp.uid = pkg.applicationInfo.uid;
bp.sourcePackage = p.info.packageName;
p.info.flags |= PermissionInfo.FLAG_INSTALLED;
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+ if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
+ p.info.packageName + " ignored: original from "
+ bp.sourcePackage);
}
- } else if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+ } else if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
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;
-
- // TODO: Update instrumentation.nativeLibraryDir as well ? Does it
- // need other information about the application, like the ABI and what not ?
+ a.info.deviceProtectedDataDir = pkg.applicationInfo.deviceProtectedDataDir;
+ a.info.credentialProtectedDataDir = pkg.applicationInfo.credentialProtectedDataDir;
a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
+ a.info.secondaryNativeLibraryDir = pkg.applicationInfo.secondaryNativeLibraryDir;
mInstrumentation.put(a.getComponentName(), a);
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+ if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
if (pkg.protectedBroadcasts != null) {
N = pkg.protectedBroadcasts.size();
- for (i=0; i<N; i++) {
- mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
+ synchronized (mProtectedBroadcasts) {
+ for (i = 0; i < N; i++) {
+ mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
+ }
}
}
-
- pkgSetting.setTimeStamp(scanFileTime);
-
- // Create idmap files for pairs of (packages, overlay packages).
- // Note: "android", ie framework-res.apk, is handled by native layers.
- if (pkg.mOverlayTarget != null) {
- // This is an overlay package.
- if (pkg.mOverlayTarget != null && !pkg.mOverlayTarget.equals("android")) {
- if (!mOverlays.containsKey(pkg.mOverlayTarget)) {
- mOverlays.put(pkg.mOverlayTarget,
- new ArrayMap<String, PackageParser.Package>());
- }
- ArrayMap<String, PackageParser.Package> map = mOverlays.get(pkg.mOverlayTarget);
- map.put(pkg.packageName, pkg);
- PackageParser.Package orig = mPackages.get(pkg.mOverlayTarget);
- if (orig != null && !createIdmapForPackagePairLI(orig, pkg)) {
- throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "scanPackageLI failed to createIdmap");
- }
- }
- } else if (mOverlays.containsKey(pkg.packageName) &&
- !pkg.packageName.equals("android")) {
- // This is a regular package, with one or more known overlay packages.
- createIdmapsForPackageLI(pkg);
- }
}
- return pkg;
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
/**
*
* If {@code extractLibs} is true, native libraries are extracted from the app if required.
*/
- public void derivePackageAbi(PackageParser.Package pkg, File scanFile,
- String cpuAbiOverride, boolean extractLibs)
+ private static void derivePackageAbi(PackageParser.Package pkg, File scanFile,
+ String cpuAbiOverride, boolean extractLibs,
+ File appLib32InstallDir)
throws PackageManagerException {
- // TODO: We can probably be smarter about this stuff. For installed apps,
- // we can calculate this information at install time once and for all. For
- // system apps, we can probably assume that this information doesn't change
- // after the first boot scan. As things stand, we do lots of unnecessary work.
-
// Give ourselves some initial paths; we'll come back for another
// pass once we've determined ABI below.
- setNativeLibraryPaths(pkg);
+ setNativeLibraryPaths(pkg, appLib32InstallDir);
// We would never need to extract libs for forward-locked and external packages,
// since the container service will do it for us. We shouldn't attempt to
NativeLibraryHelper.Handle handle = null;
try {
- handle = NativeLibraryHelper.Handle.create(scanFile);
+ 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.
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(
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) {
- pkg.applicationInfo.secondaryCpuAbi = abi;
+ if (pkg.use32bitAbi) {
+ pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
+ pkg.applicationInfo.primaryCpuAbi = abi;
+ } else {
+ pkg.applicationInfo.secondaryCpuAbi = abi;
+ }
} else {
pkg.applicationInfo.primaryCpuAbi = abi;
}
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,
}
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;
// 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);
+ setNativeLibraryPaths(pkg, appLib32InstallDir);
}
/**
* adds unnecessary complexity.
*/
private void adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser,
- PackageParser.Package scannedPackage, boolean forceDexOpt, boolean deferDexOpt,
- boolean bootComplete) {
+ PackageParser.Package scannedPackage) {
String requiredInstructionSet = null;
if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) {
requiredInstructionSet = VMRuntime.getInstructionSet(
}
ps.primaryCpuAbiString = adjustedAbi;
- if (ps.pkg != null && ps.pkg.applicationInfo != null) {
+ if (ps.pkg != null && ps.pkg.applicationInfo != null &&
+ !TextUtils.equals(adjustedAbi, ps.pkg.applicationInfo.primaryCpuAbi)) {
ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
- Slog.i(TAG, "Adjusting ABI for : " + ps.name + " to " + adjustedAbi);
-
- int result = mPackageDexOptimizer.performDexOpt(ps.pkg,
- null /* instruction sets */, forceDexOpt, deferDexOpt, true,
- bootComplete);
- if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- ps.primaryCpuAbiString = null;
- ps.pkg.applicationInfo.primaryCpuAbi = null;
- return;
- } else {
+ 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")
+ + ")");
+ }
+ try {
mInstaller.rmdex(ps.codePathString,
getDexCodeInstructionSet(getPreferredInstructionSet()));
+ } catch (InstallerException ignored) {
}
}
}
}
}
+ private void setUpInstantAppInstallerActivityLP(ActivityInfo installerActivity) {
+ if (installerActivity == null) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Clear ephemeral installer activity");
+ }
+ mInstantAppInstallerActivity = null;
+ return;
+ }
+
+ if (DEBUG_EPHEMERAL) {
+ 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 = 0;
+ 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;
* Derive and set the location of native libraries for the given package,
* which varies depending on where and how the package was installed.
*/
- private void setNativeLibraryPaths(PackageParser.Package pkg) {
+ 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);
.getAbsolutePath();
} else {
final String apkName = deriveCodePathName(codePath);
- info.nativeLibraryRootDir = new File(mAppLib32InstallDir, apkName)
+ info.nativeLibraryRootDir = new File(appLib32InstallDir, apkName)
.getAbsolutePath();
}
* of this information, and instead assume that the system was built
* sensibly.
*/
- private void setBundledAppAbisAndRoots(PackageParser.Package pkg,
+ private static void setBundledAppAbisAndRoots(PackageParser.Package pkg,
PackageSetting pkgSetting) {
final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
// 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.");
+ Slog.e(TAG, "Package " + pkg + " has multiple bundled libs, but is not multiarch.");
}
if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) {
}
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.
- IActivityManager am = ActivityManagerNative.getDefault();
- if (am != null) {
- try {
- am.killApplicationWithAppId(pkgName, appId, reason);
- } catch (RemoteException e) {
+ 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, chatty);
+ }
+ // 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, chatty);
}
}
}
// 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);
+ }
}
}
}
}
if ((p.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
- ArraySet<String> appOpPerms = mAppOpPermissionPackages.get(p.info.name);
- if (appOpPerms != null) {
- appOpPerms.remove(pkg.packageName);
+ ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(p.info.name);
+ if (appOpPkgs != null) {
+ appOpPkgs.remove(pkg.packageName);
}
}
}
String perm = pkg.requestedPermissions.get(i);
BasePermission bp = mSettings.mPermissions.get(perm);
if (bp != null && (bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
- ArraySet<String> appOpPerms = mAppOpPermissionPackages.get(perm);
- if (appOpPerms != null) {
- appOpPerms.remove(pkg.packageName);
- if (appOpPerms.isEmpty()) {
+ ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(perm);
+ if (appOpPkgs != null) {
+ appOpPkgs.remove(pkg.packageName);
+ if (appOpPkgs.isEmpty()) {
mAppOpPermissionPackages.remove(perm);
}
}
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
// Only system apps can hold shared libraries.
if (pkg.libraryNames != null) {
- for (i=0; i<pkg.libraryNames.size(); i++) {
+ for (i = 0; i < pkg.libraryNames.size(); i++) {
String name = pkg.libraryNames.get(i);
- SharedLibraryEntry cur = mSharedLibraries.get(name);
- if (cur != null && cur.apk != null && cur.apk.equals(pkg.packageName)) {
- mSharedLibraries.remove(name);
+ if (removeSharedLibraryLPw(name, 0)) {
if (DEBUG_REMOVE && chatty) {
if (r == null) {
r = new StringBuilder(256);
}
}
}
+
+ 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);
}
static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
+ private void updatePermissionsLPw(PackageParser.Package pkg, int flags) {
+ // Update the parent permissions
+ updatePermissionsLPw(pkg.packageName, pkg, flags);
+ // Update the child permissions
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ updatePermissionsLPw(childPkg.packageName, childPkg, flags);
+ }
+ }
+
private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo,
int flags) {
final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null;
}
}
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "grantPermissions");
// Now update the permissions for all packages, in particular
// replace the granted permissions of the system packages.
if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
&& Objects.equals(replaceVolumeUuid, volumeUuid);
grantPermissionsLPw(pkgInfo, replace, changingPkg);
}
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace,
for (int i=0; i<N; i++) {
final String name = pkg.requestedPermissions.get(i);
final BasePermission bp = mSettings.mPermissions.get(name);
+ final boolean appSupportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
+ >= Build.VERSION_CODES.M;
if (DEBUG_INSTALL) {
Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
if (bp == null || bp.packageSetting == null) {
if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
- Slog.w(TAG, "Unknown permission " + name
- + " in package " + pkg.packageName);
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Unknown permission " + name
+ + " in package " + pkg.packageName);
+ }
+ }
+ continue;
+ }
+
+
+ // Limit ephemeral apps to ephemeral allowed permissions.
+ if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
+ if (DEBUG_PERMISSIONS) {
+ Log.i(TAG, "Denying non-ephemeral permission " + bp.name + " for package "
+ + pkg.packageName);
+ }
+ continue;
+ }
+
+ if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
+ if (DEBUG_PERMISSIONS) {
+ Log.i(TAG, "Denying runtime-only permission " + bp.name + " for package "
+ + pkg.packageName);
}
continue;
}
} break;
case PermissionInfo.PROTECTION_DANGEROUS: {
- if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ // If a permission review is required for legacy apps we represent
+ // their permissions as always granted runtime ones since we need
+ // to keep the review required permission flag per user while an
+ // install permission's state is shared across all users.
+ if (!appSupportsRuntimePermissions && !mPermissionReviewRequired) {
// For legacy apps dangerous permissions are install time ones.
- grant = GRANT_INSTALL_LEGACY;
+ grant = GRANT_INSTALL;
} else if (origPermissions.hasInstallPermission(bp.name)) {
// For legacy apps that became modern, install becomes runtime.
grant = GRANT_UPGRADE;
} break;
}
- if (DEBUG_INSTALL) {
- Log.i(TAG, "Package " + pkg.packageName + " granting " + perm);
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Granting permission " + perm + " to package " + pkg.packageName);
}
if (grant != GRANT_DENIED) {
case GRANT_INSTALL: {
// Revoke this as runtime permission to handle the case of
// a runtime permission being downgraded to an install one.
+ // Also in permission review mode we keep dangerous permissions
+ // for legacy apps
for (int userId : UserManagerService.getInstance().getUserIds()) {
if (origPermissions.getRuntimePermissionState(
bp.name, userId) != null) {
}
} break;
- case GRANT_INSTALL_LEGACY: {
- // Grant an install permission.
- if (permissionsState.grantInstallPermission(bp) !=
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- changedInstallPermission = true;
- }
- } break;
-
case GRANT_RUNTIME: {
// Grant previously granted runtime permissions.
for (int userId : UserManagerService.getInstance().getUserIds()) {
PermissionState permissionState = origPermissions
.getRuntimePermissionState(bp.name, userId);
- final int flags = permissionState != null
+ int flags = permissionState != null
? permissionState.getFlags() : 0;
if (origPermissions.hasRuntimePermission(bp.name, userId)) {
- if (permissionsState.grantRuntimePermission(bp, userId) ==
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- // If we cannot put the permission as it was, we have to write.
+ // Don't propagate the permission in a permission review mode if
+ // the former was revoked, i.e. marked to not propagate on upgrade.
+ // Note that in a permission review mode install permissions are
+ // represented as constantly granted runtime ones since we need to
+ // keep a per user state associated with the permission. Also the
+ // revoke on upgrade flag is no longer applicable and is reset.
+ final boolean revokeOnUpgrade = (flags & PackageManager
+ .FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0;
+ if (revokeOnUpgrade) {
+ flags &= ~PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ // Since we changed the flags, we have to write.
+ changedRuntimePermissionUserIds = ArrayUtils.appendInt(
+ changedRuntimePermissionUserIds, userId);
+ }
+ if (!mPermissionReviewRequired || !revokeOnUpgrade) {
+ if (permissionsState.grantRuntimePermission(bp, userId) ==
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ // If we cannot put the permission as it was,
+ // we have to write.
+ changedRuntimePermissionUserIds = ArrayUtils.appendInt(
+ changedRuntimePermissionUserIds, userId);
+ }
+ }
+
+ // If the app supports runtime permissions no need for a review.
+ if (mPermissionReviewRequired
+ && appSupportsRuntimePermissions
+ && (flags & PackageManager
+ .FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ flags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+ // Since we changed the flags, we have to write.
+ changedRuntimePermissionUserIds = ArrayUtils.appendInt(
+ changedRuntimePermissionUserIds, userId);
+ }
+ } else if (mPermissionReviewRequired
+ && !appSupportsRuntimePermissions) {
+ // For legacy apps that need a permission review, every new
+ // runtime permission is granted but it is pending a review.
+ // We also need to review only platform defined runtime
+ // permissions as these are the only ones the platform knows
+ // how to disable the API to simulate revocation as legacy
+ // apps don't expect to run with revoked permissions.
+ if (PLATFORM_PACKAGE_NAME.equals(bp.sourcePackage)) {
+ if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
+ flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+ // We changed the flags, hence have to write.
+ changedRuntimePermissionUserIds = ArrayUtils.appendInt(
+ changedRuntimePermissionUserIds, userId);
+ }
+ }
+ if (permissionsState.grantRuntimePermission(bp, userId)
+ != PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ // We changed the permission, hence have to write.
changedRuntimePermissionUserIds = ArrayUtils.appendInt(
changedRuntimePermissionUserIds, userId);
}
default: {
if (packageOfInterest == null
|| packageOfInterest.equals(pkg.packageName)) {
- Slog.w(TAG, "Not granting permission " + perm
- + " to package " + pkg.packageName
- + " because it was previously installed without");
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Not granting permission " + perm
+ + " to package " + pkg.packageName
+ + " because it was previously installed without");
+ }
}
} break;
}
} else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) {
// Don't print warning for app op permissions, since it is fine for them
// not to be granted, there is a UI for the user to decide.
- if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
- Slog.w(TAG, "Not granting permission " + perm
+ if (DEBUG_PERMISSIONS
+ && (packageOfInterest == null
+ || packageOfInterest.equals(pkg.packageName))) {
+ Slog.i(TAG, "Not granting permission " + perm
+ " to package " + pkg.packageName
+ " (protectionLevel=" + bp.protectionLevel
+ " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,
BasePermission bp, PermissionsState origPermissions) {
- boolean allowed;
- allowed = (compareSignatures(
+ boolean privilegedPermission = (bp.protectionLevel
+ & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0;
+ boolean privappPermissionsDisable =
+ RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE;
+ boolean platformPermission = PLATFORM_PACKAGE_NAME.equals(bp.sourcePackage);
+ boolean platformPackage = PLATFORM_PACKAGE_NAME.equals(pkg.packageName);
+ if (!privappPermissionsDisable && privilegedPermission && pkg.isPrivilegedApp()
+ && !platformPackage && platformPermission) {
+ final ArraySet<String> allowedPermissions = SystemConfig.getInstance()
+ .getPrivAppPermissions(pkg.packageName);
+ final boolean whitelisted =
+ allowedPermissions != null && allowedPermissions.contains(perm);
+ if (!whitelisted) {
+ Slog.w(TAG, "Privileged permission " + perm + " for package "
+ + pkg.packageName + " - not in privapp-permissions whitelist");
+ // Only report violations for apps on system image
+ if (!mSystemReady && !pkg.isUpdatedSystemApp()) {
+ // it's only a reportable violation if the permission isn't explicitly denied
+ final ArraySet<String> deniedPermissions = SystemConfig.getInstance()
+ .getPrivAppDenyPermissions(pkg.packageName);
+ final boolean permissionViolation =
+ deniedPermissions == null || !deniedPermissions.contains(perm);
+ if (permissionViolation) {
+ if (mPrivappPermissionsViolations == null) {
+ mPrivappPermissionsViolations = new ArraySet<>();
+ }
+ mPrivappPermissionsViolations.add(pkg.packageName + ": " + perm);
+ } else {
+ return false;
+ }
+ }
+ if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
+ return false;
+ }
+ }
+ }
+ boolean allowed = (compareSignatures(
bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH)
|| (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH);
- if (!allowed && (bp.protectionLevel
- & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0) {
+ if (!allowed && privilegedPermission) {
if (isSystemApp(pkg)) {
// For updated system applications, a system permission
// is granted only if it had been defined by the original application.
if (pkg.isUpdatedSystemApp()) {
final PackageSetting sysPs = mSettings
.getDisabledSystemPkgLPr(pkg.packageName);
- if (sysPs.getPermissionsState().hasInstallPermission(perm)) {
+ if (sysPs != null && sysPs.getPermissionsState().hasInstallPermission(perm)) {
// If the original was granted this permission, we take
// that grant decision as read and propagate it to the
// update.
// before. In this case we do want to allow the app to
// now get the new permission if the ancestral apk is
// privileged to get it.
- if (sysPs.pkg != null && sysPs.isPrivileged()) {
- for (int j=0;
- j<sysPs.pkg.requestedPermissions.size(); j++) {
- if (perm.equals(
- sysPs.pkg.requestedPermissions.get(j))) {
+ if (sysPs != null && sysPs.pkg != null && sysPs.isPrivileged()) {
+ for (int j = 0; j < sysPs.pkg.requestedPermissions.size(); j++) {
+ if (perm.equals(sysPs.pkg.requestedPermissions.get(j))) {
allowed = true;
break;
}
}
}
+ // Also if a privileged parent package on the system image or any of
+ // its children requested a privileged permission, the updated child
+ // packages can also get the permission.
+ if (pkg.parentPackage != null) {
+ final PackageSetting disabledSysParentPs = mSettings
+ .getDisabledSystemPkgLPr(pkg.parentPackage.packageName);
+ if (disabledSysParentPs != null && disabledSysParentPs.pkg != null
+ && disabledSysParentPs.isPrivileged()) {
+ if (isPackageRequestingPermission(disabledSysParentPs.pkg, perm)) {
+ allowed = true;
+ } else if (disabledSysParentPs.pkg.childPackages != null) {
+ final int count = disabledSysParentPs.pkg.childPackages.size();
+ for (int i = 0; i < count; i++) {
+ PackageParser.Package disabledSysChildPkg =
+ disabledSysParentPs.pkg.childPackages.get(i);
+ if (isPackageRequestingPermission(disabledSysChildPkg,
+ perm)) {
+ allowed = true;
+ break;
+ }
+ }
+ }
+ }
+ }
}
} else {
allowed = isPrivilegedApp(pkg);
// is granted only if it was already granted.
allowed = origPermissions.hasInstallPermission(perm);
}
+ if (!allowed && (bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0
+ && pkg.packageName.equals(mSetupWizardPackage)) {
+ // If this permission is to be granted to the system setup wizard and
+ // this app is a setup wizard, then it gets the permission.
+ allowed = true;
+ }
}
return allowed;
}
+ private boolean isPackageRequestingPermission(PackageParser.Package pkg, String permission) {
+ final int permCount = pkg.requestedPermissions.size();
+ for (int j = 0; j < permCount; j++) {
+ String requestedPermission = pkg.requestedPermissions.get(j);
+ if (permission.equals(requestedPermission)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
final class ActivityIntentResolver
extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
boolean defaultOnly, int userId) {
if (!sUserManager.exists(userId)) return null;
- mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
+ mFlags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0);
return super.queryIntent(intent, resolvedType, defaultOnly, userId);
}
if (!sUserManager.exists(userId)) return null;
mFlags = flags;
return super.queryIntent(intent, resolvedType,
- (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+ userId);
}
public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
return null;
}
mFlags = flags;
- final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
+ final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
final int N = packageActivities.size();
ArrayList<PackageParser.ActivityIntentInfo[]> listCut =
new ArrayList<PackageParser.ActivityIntentInfo[]>(N);
}
}
+ private boolean isProtectedAction(ActivityIntentInfo filter) {
+ final Iterator<String> actionsIter = filter.actionsIterator();
+ while (actionsIter != null && actionsIter.hasNext()) {
+ final String filterAction = actionsIter.next();
+ if (PROTECTED_ACTIONS.contains(filterAction)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Adjusts the priority of the given intent filter according to policy.
* <p>
* <ul>
- * <li>The priority for unbundled updates to system applications is capped to the
+ * <li>The priority for non privileged applications is capped to '0'</li>
+ * <li>The priority for protected actions on privileged applications is capped to '0'</li>
+ * <li>The priority for unbundled updates to privileged applications is capped to the
* priority defined on the system partition</li>
* </ul>
+ * <p>
+ * <em>NOTE:</em> There is one exception. For security reasons, the setup wizard is
+ * allowed to obtain any priority on any action.
*/
private void adjustPriority(
List<PackageParser.Activity> systemActivities, ActivityIntentInfo intent) {
final ActivityInfo activityInfo = intent.activity.info;
final ApplicationInfo applicationInfo = activityInfo.applicationInfo;
- final boolean systemApp = applicationInfo.isSystemApp();
- if (!systemApp) {
- // non-system applications can never define a priority >0
- Slog.w(TAG, "Non-system app; cap priority to 0;"
- + " package: " + applicationInfo.packageName
- + " activity: " + intent.activity.className
- + " origPrio: " + intent.getPriority());
+ final boolean privilegedApp =
+ ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0);
+ if (!privilegedApp) {
+ // non-privileged applications can never define a priority >0
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Non-privileged app; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
intent.setPriority(0);
return;
}
if (systemActivities == null) {
// the system package is not disabled; we're parsing the system partition
- // apps on the system image get whatever priority they request
+ if (isProtectedAction(intent)) {
+ if (mDeferProtectedFilters) {
+ // We can't deal with these just yet. No component should ever obtain a
+ // >0 priority for a protected actions, with ONE exception -- the setup
+ // wizard. The setup wizard, however, cannot be known until we're able to
+ // query it for the category CATEGORY_SETUP_WIZARD. Which we can't do
+ // until all intent filters have been processed. Chicken, meet egg.
+ // Let the filter temporarily have a high priority and rectify the
+ // priorities after all system packages have been scanned.
+ mProtectedFilters.add(intent);
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Protected action; save for later;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ return;
+ } else {
+ if (DEBUG_FILTERS && mSetupWizardPackage == null) {
+ Slog.i(TAG, "No setup wizard;"
+ + " All protected intents capped to priority 0");
+ }
+ if (intent.activity.info.packageName.equals(mSetupWizardPackage)) {
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Found setup wizard;"
+ + " allow priority " + intent.getPriority() + ";"
+ + " package: " + intent.activity.info.packageName
+ + " activity: " + intent.activity.className
+ + " priority: " + intent.getPriority());
+ }
+ // setup wizard gets whatever it wants
+ return;
+ }
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Protected action; cap priority to 0;"
+ + " package: " + intent.activity.info.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+ }
+ // privileged apps on the system image get whatever priority they request
return;
}
- // system app unbundled update ... try to find the same activity
+ // privileged app unbundled update ... try to find the same activity
final PackageParser.Activity foundActivity =
findMatchingActivity(systemActivities, activityInfo);
if (foundActivity == null) {
}
public final void addActivity(PackageParser.Activity a, String type) {
- final boolean systemApp = a.info.applicationInfo.isSystemApp();
mActivities.put(a.getComponentName(), a);
if (DEBUG_SHOW_INFO)
Log.v(
protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info,
int match, int userId) {
if (!sUserManager.exists(userId)) return null;
- if (!mSettings.isEnabledLPr(info.activity.info, mFlags, userId)) {
+ if (!mSettings.isEnabledAndMatchLPr(info.activity.info, mFlags, userId)) {
return null;
}
final PackageParser.Activity activity = info.activity;
- if (mSafeMode && (activity.info.applicationInfo.flags
- &ApplicationInfo.FLAG_SYSTEM) == 0) {
- return null;
- }
PackageSetting ps = (PackageSetting) activity.owner.mExtras;
if (ps == null) {
return null;
}
- ActivityInfo ai = PackageParser.generateActivityInfo(activity, mFlags,
- ps.readUserState(userId), userId);
+ final PackageUserState userState = ps.readUserState(userId);
+ ActivityInfo ai =
+ PackageParser.generateActivityInfo(activity, mFlags, userState, userId);
if (ai == null) {
return null;
}
+ final boolean matchExplicitlyVisibleOnly =
+ (mFlags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
+ final boolean matchVisibleToInstantApp =
+ (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
+ final boolean componentVisible =
+ matchVisibleToInstantApp
+ && info.isVisibleToInstantApp()
+ && (!matchExplicitlyVisibleOnly || info.isExplicitlyVisibleToInstantApp());
+ final boolean matchInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
+ // throw out filters that aren't visible to ephemeral apps
+ if (matchVisibleToInstantApp && !(componentVisible || userState.instantApp)) {
+ return null;
+ }
+ // throw out instant app filters if we're not explicitly requesting them
+ if (!matchInstantApp && userState.instantApp) {
+ return null;
+ }
+ // throw out instant app filters if updates are available; will trigger
+ // instant app resolution
+ if (userState.instantApp && ps.isUpdateAvailable()) {
+ return null;
+ }
final ResolveInfo res = new ResolveInfo();
res.activityInfo = ai;
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
}
res.iconResourceId = info.icon;
res.system = res.activityInfo.applicationInfo.isSystemApp();
+ res.isInstantAppAvailable = userState.instantApp;
return res;
}
if (!sUserManager.exists(userId)) return null;
mFlags = flags;
return super.queryIntent(intent, resolvedType,
- (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+ userId);
}
public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
int match, int userId) {
if (!sUserManager.exists(userId)) return null;
final PackageParser.ServiceIntentInfo info = (PackageParser.ServiceIntentInfo)filter;
- if (!mSettings.isEnabledLPr(info.service.info, mFlags, userId)) {
+ if (!mSettings.isEnabledAndMatchLPr(info.service.info, mFlags, userId)) {
return null;
}
final PackageParser.Service service = info.service;
- if (mSafeMode && (service.info.applicationInfo.flags
- &ApplicationInfo.FLAG_SYSTEM) == 0) {
- return null;
- }
PackageSetting ps = (PackageSetting) service.owner.mExtras;
if (ps == null) {
return null;
}
+ final PackageUserState userState = ps.readUserState(userId);
ServiceInfo si = PackageParser.generateServiceInfo(service, mFlags,
- ps.readUserState(userId), userId);
+ userState, userId);
if (si == null) {
return null;
}
+ final boolean matchVisibleToInstantApp =
+ (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
+ final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
+ // throw out filters that aren't visible to ephemeral apps
+ if (matchVisibleToInstantApp
+ && !(info.isVisibleToInstantApp() || userState.instantApp)) {
+ return null;
+ }
+ // throw out ephemeral filters if we're not explicitly requesting them
+ if (!isInstantApp && userState.instantApp) {
+ return null;
+ }
+ // throw out instant app filters if updates are available; will trigger
+ // instant app resolution
+ if (userState.instantApp && ps.isUpdateAvailable()) {
+ return null;
+ }
final ResolveInfo res = new ResolveInfo();
res.serviceInfo = si;
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
private final ArrayMap<ComponentName, PackageParser.Service> mServices
= new ArrayMap<ComponentName, PackageParser.Service>();
private int mFlags;
- };
+ }
private final class ProviderIntentResolver
extends IntentResolver<PackageParser.ProviderIntentInfo, ResolveInfo> {
return null;
mFlags = flags;
return super.queryIntent(intent, resolvedType,
- (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+ userId);
}
public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
if (!sUserManager.exists(userId))
return null;
final PackageParser.ProviderIntentInfo info = filter;
- if (!mSettings.isEnabledLPr(info.provider.info, mFlags, userId)) {
+ if (!mSettings.isEnabledAndMatchLPr(info.provider.info, mFlags, userId)) {
return null;
}
final PackageParser.Provider provider = info.provider;
- if (mSafeMode && (provider.info.applicationInfo.flags
- & ApplicationInfo.FLAG_SYSTEM) == 0) {
- return null;
- }
PackageSetting ps = (PackageSetting) provider.owner.mExtras;
if (ps == null) {
return null;
}
+ final PackageUserState userState = ps.readUserState(userId);
+ final boolean matchVisibleToInstantApp =
+ (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
+ final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
+ // throw out filters that aren't visible to instant applications
+ if (matchVisibleToInstantApp
+ && !(info.isVisibleToInstantApp() || userState.instantApp)) {
+ return null;
+ }
+ // throw out instant application filters if we're not explicitly requesting them
+ if (!isInstantApp && userState.instantApp) {
+ return null;
+ }
+ // throw out instant application filters if updates are available; will trigger
+ // instant application resolution
+ if (userState.instantApp && ps.isUpdateAvailable()) {
+ return null;
+ }
ProviderInfo pi = PackageParser.generateProviderInfo(provider, mFlags,
- ps.readUserState(userId), userId);
+ userState, userId);
if (pi == null) {
return null;
}
private final ArrayMap<ComponentName, PackageParser.Provider> mProviders
= new ArrayMap<ComponentName, PackageParser.Provider>();
private int mFlags;
- };
+ }
+
+ static final class EphemeralIntentResolver
+ extends IntentResolver<AuxiliaryResolveInfo, AuxiliaryResolveInfo> {
+ /**
+ * The result that has the highest defined order. Ordering applies on a
+ * per-package basis. Mapping is from package name to Pair of order and
+ * EphemeralResolveInfo.
+ * <p>
+ * NOTE: This is implemented as a field variable for convenience and efficiency.
+ * By having a field variable, we're able to track filter ordering as soon as
+ * a non-zero order is defined. Otherwise, multiple loops across the result set
+ * would be needed to apply ordering. If the intent resolver becomes re-entrant,
+ * this needs to be contained entirely within {@link #filterResults}.
+ */
+ final ArrayMap<String, Pair<Integer, InstantAppResolveInfo>> mOrderResult = new ArrayMap<>();
+
+ @Override
+ protected AuxiliaryResolveInfo[] newArray(int size) {
+ return new AuxiliaryResolveInfo[size];
+ }
+
+ @Override
+ protected boolean isPackageForFilter(String packageName, AuxiliaryResolveInfo responseObj) {
+ return true;
+ }
+
+ @Override
+ protected AuxiliaryResolveInfo newResult(AuxiliaryResolveInfo responseObj, int match,
+ int userId) {
+ if (!sUserManager.exists(userId)) {
+ return null;
+ }
+ final String packageName = responseObj.resolveInfo.getPackageName();
+ final Integer order = responseObj.getOrder();
+ final Pair<Integer, InstantAppResolveInfo> lastOrderResult =
+ mOrderResult.get(packageName);
+ // ordering is enabled and this item's order isn't high enough
+ if (lastOrderResult != null && lastOrderResult.first >= order) {
+ return null;
+ }
+ final InstantAppResolveInfo res = responseObj.resolveInfo;
+ if (order > 0) {
+ // non-zero order, enable ordering
+ mOrderResult.put(packageName, new Pair<>(order, res));
+ }
+ return responseObj;
+ }
+
+ @Override
+ protected void filterResults(List<AuxiliaryResolveInfo> results) {
+ // only do work if ordering is enabled [most of the time it won't be]
+ if (mOrderResult.size() == 0) {
+ return;
+ }
+ int resultSize = results.size();
+ for (int i = 0; i < resultSize; i++) {
+ final InstantAppResolveInfo info = results.get(i).resolveInfo;
+ final String packageName = info.getPackageName();
+ final Pair<Integer, InstantAppResolveInfo> savedInfo = mOrderResult.get(packageName);
+ if (savedInfo == null) {
+ // package doesn't having ordering
+ continue;
+ }
+ if (savedInfo.second == info) {
+ // circled back to the highest ordered item; remove from order list
+ mOrderResult.remove(packageName);
+ if (mOrderResult.size() == 0) {
+ // no more ordered items
+ break;
+ }
+ continue;
+ }
+ // item has a worse order, remove it from the result list
+ results.remove(i);
+ resultSize--;
+ i--;
+ }
+ }
+ }
private static final Comparator<ResolveInfo> mResolvePrioritySorter =
new Comparator<ResolveInfo>() {
if (r1.system != r2.system) {
return r1.system ? -1 : 1;
}
+ if (r1.activityInfo != null) {
+ return r1.activityInfo.packageName.compareTo(r2.activityInfo.packageName);
+ }
+ if (r1.serviceInfo != null) {
+ return r1.serviceInfo.packageName.compareTo(r2.serviceInfo.packageName);
+ }
+ if (r1.providerInfo != null) {
+ return r1.providerInfo.packageName.compareTo(r2.providerInfo.packageName);
+ }
return 0;
}
};
}
};
- final void sendPackageBroadcast(final String action, final String pkg,
- final Bundle extras, final String targetPkg, final IIntentReceiver finishedReceiver,
+ public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
+ final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
final int[] userIds) {
mHandler.post(new Runnable() {
@Override
public void run() {
try {
- final IActivityManager am = ActivityManagerNative.getDefault();
+ final IActivityManager am = ActivityManager.getService();
if (am == null) return;
final int[] resolvedUserIds;
if (userIds == null) {
}
for (int id : resolvedUserIds) {
final Intent intent = new Intent(action,
- pkg != null ? Uri.fromParts("package", pkg, null) : null);
+ pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
if (extras != null) {
intent.putExtras(extras);
}
intent.putExtra(Intent.EXTRA_UID, uid);
}
intent.putExtra(Intent.EXTRA_USER_HANDLE, id);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
if (DEBUG_BROADCASTS) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
@Override
public PackageCleanItem nextPackageToClean(PackageCleanItem lastPackage) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return null;
+ }
// writer
synchronized (mPackages) {
if (!isExternalMediaAvailable()) {
void startCleaningPackages() {
// reader
+ if (!isExternalMediaAvailable()) {
+ return;
+ }
synchronized (mPackages) {
- if (!isExternalMediaAvailable()) {
- return;
- }
if (mSettings.mPackagesToBeCleaned.isEmpty()) {
return;
}
}
Intent intent = new Intent(PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE);
intent.setComponent(DEFAULT_CONTAINER_COMPONENT);
- IActivityManager am = ActivityManagerNative.getDefault();
+ IActivityManager am = ActivityManager.getService();
if (am != null) {
+ int dcsUid = -1;
+ synchronized (mPackages) {
+ if (!mDefaultContainerWhitelisted) {
+ mDefaultContainerWhitelisted = true;
+ PackageSetting ps = mSettings.mPackages.get(DEFAULT_CONTAINER_PACKAGE);
+ dcsUid = UserHandle.getUid(UserHandle.USER_SYSTEM, ps.appId);
+ }
+ }
try {
- am.startService(null, intent, null, mContext.getOpPackageName(),
- UserHandle.USER_OWNER);
+ if (dcsUid > 0) {
+ am.backgroundWhitelistUid(dcsUid);
+ }
+ am.startService(null, intent, null, false, mContext.getOpPackageName(),
+ UserHandle.USER_SYSTEM);
} catch (RemoteException e) {
}
}
}
@Override
- public void installPackage(String originPath, IPackageInstallObserver2 observer,
- int installFlags, String installerPackageName, VerificationParams verificationParams,
- String packageAbiOverride) {
- installPackageAsUser(originPath, observer, installFlags, installerPackageName,
- verificationParams, packageAbiOverride, UserHandle.getCallingUserId());
- }
-
- @Override
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
- int installFlags, String installerPackageName, VerificationParams verificationParams,
- String packageAbiOverride, int userId) {
+ int installFlags, String installerPackageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, true /* checkShell */, "installPackageAsUser");
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
try {
+ "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
}
- verificationParams.setInstallerUid(callingUid);
+ if ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
+ || (installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
+ throw new IllegalArgumentException(
+ "New installs into ASEC containers no longer supported");
+ }
final File originFile = new File(originPath);
final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
final Message msg = mHandler.obtainMessage(INIT_COPY);
- msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
- null, verificationParams, user, packageAbiOverride, null);
+ final VerificationInfo verificationInfo = new VerificationInfo(
+ null /*originatingUri*/, null /*referrer*/, -1 /*originatingUid*/, callingUid);
+ final InstallParams params = new InstallParams(origin, null /*moveInfo*/, observer,
+ installFlags, installerPackageName, null /*volumeUuid*/, verificationInfo, user,
+ null /*packageAbiOverride*/, null /*grantedPermissions*/,
+ null /*certificates*/, PackageManager.INSTALL_REASON_UNKNOWN);
+ params.setTraceMethod("installAsUser").setTraceCookie(System.identityHashCode(params));
+ msg.obj = params;
+
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installAsUser",
+ System.identityHashCode(msg.obj));
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+ System.identityHashCode(msg.obj));
+
mHandler.sendMessage(msg);
}
+
+ /**
+ * 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 IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+ if (dpm != null) {
+ ComponentName owner = null;
+ try {
+ owner = dpm.getDeviceOwnerComponent(true /* callingUserOnly */);
+ if (owner == null) {
+ owner = dpm.getProfileOwner(UserHandle.getUserId(installerUid));
+ }
+ } catch (RemoteException e) {
+ }
+ if (owner != null && owner.getPackageName().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(String packageName, File stagedDir, String stagedCid,
- IPackageInstallObserver2 observer, PackageInstaller.SessionParams params,
- String installerPackageName, int installerUid, UserHandle user) {
- final VerificationParams verifParams = new VerificationParams(null, params.originatingUri,
- params.referrerUri, installerUid, null);
- verifParams.setInstallerUid(installerUid);
+ IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
+ String installerPackageName, int installerUid, UserHandle user,
+ Certificate[][] certificates) {
+ if (DEBUG_EPHEMERAL) {
+ if ((sessionParams.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
+ Slog.d(TAG, "Ephemeral install of " + packageName);
+ }
+ }
+ final VerificationInfo verificationInfo = new VerificationInfo(
+ sessionParams.originatingUri, sessionParams.referrerUri,
+ sessionParams.originatingUid, installerUid);
final OriginInfo origin;
if (stagedDir != null) {
}
final Message msg = mHandler.obtainMessage(INIT_COPY);
- msg.obj = new InstallParams(origin, null, observer, params.installFlags,
- installerPackageName, params.volumeUuid, verifParams, user, params.abiOverride,
- params.grantedRuntimePermissions);
+ final int installReason = fixUpInstallReason(installerPackageName, installerUid,
+ sessionParams.installReason);
+ final InstallParams params = new InstallParams(origin, null, observer,
+ sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
+ verificationInfo, user, sessionParams.abiOverride,
+ sessionParams.grantedRuntimePermissions, certificates, installReason);
+ 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);
}
- private void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting, int userId) {
- Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userId, pkgSetting.appId));
+ private void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting,
+ int userId) {
+ final boolean isSystem = isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting);
+ sendPackageAddedForNewUsers(packageName, isSystem /*sendBootCompleted*/,
+ false /*startReceiver*/, pkgSetting.appId, userId);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- packageName, extras, null, null, new int[] {userId});
- try {
- IActivityManager am = ActivityManagerNative.getDefault();
- final boolean isSystem =
- isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting);
- if (isSystem && am.isUserRunning(userId, false)) {
- // 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 BOOT_COMPLETED if it would ordinarily have gotten one.
- Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED)
- .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
- .setPackage(packageName);
- am.broadcastIntent(null, bcIntent, null, null, 0, null, null, null,
- android.app.AppOpsManager.OP_NONE, null, false, false, userId);
+ // Send a session commit broadcast
+ final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo();
+ info.installReason = pkgSetting.getInstallReason(userId);
+ info.appPackageName = packageName;
+ sendSessionCommitBroadcast(info, userId);
+ }
+
+ public void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted,
+ boolean includeStopped, int appId, int... userIds) {
+ if (ArrayUtils.isEmpty(userIds)) {
+ return;
+ }
+ Bundle extras = new Bundle(1);
+ // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
+ extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userIds[0], appId));
+
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
+ packageName, extras, 0, null, null, userIds);
+ if (sendBootCompleted) {
+ 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) {
- // shouldn't happen
- Slog.w(TAG, "Unable to bootstrap installed package", e);
+ throw e.rethrowFromSystemServer();
}
}
int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
PackageSetting pkgSetting;
- final int uid = Binder.getCallingUid();
- enforceCrossUserPermission(uid, userId, true, true,
+ final int callingUid = Binder.getCallingUid();
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, true /* checkShell */,
"setApplicationHiddenSetting for user " + userId);
if (hidden && isPackageDeviceAdmin(packageName, userId)) {
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);
private void sendApplicationHiddenForUser(String packageName, PackageSetting pkgSetting,
int userId) {
- final PackageRemovedInfo info = new PackageRemovedInfo();
+ 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.sendBroadcast(false, false, false);
+ info.sendPackageRemovedBroadcasts(true /*killApp*/);
+ }
+
+ private void sendPackagesSuspendedForUser(String[] pkgList, int userId, boolean suspended) {
+ if (pkgList.length > 0) {
+ Bundle extras = new Bundle(1);
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+
+ sendPackageBroadcast(
+ suspended ? Intent.ACTION_PACKAGES_SUSPENDED
+ : Intent.ACTION_PACKAGES_UNSUSPENDED,
+ null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
+ new int[] {userId});
+ }
}
/**
@Override
public boolean getApplicationHiddenSettingAsUser(String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
- enforceCrossUserPermission(Binder.getCallingUid(), userId, true,
- false, "getApplicationHidden for user " + userId);
- PackageSetting pkgSetting;
+ final int callingUid = Binder.getCallingUid();
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, false /* checkShell */,
+ "getApplicationHidden for user " + userId);
+ PackageSetting ps;
long callingId = Binder.clearCallingIdentity();
try {
// writer
synchronized (mPackages) {
- pkgSetting = mSettings.mPackages.get(packageName);
- if (pkgSetting == null) {
+ ps = mSettings.mPackages.get(packageName);
+ if (ps == null) {
+ return true;
+ }
+ if (filterAppAccessLPr(ps, callingUid, userId)) {
return true;
}
- return pkgSetting.getHidden(userId);
+ return ps.getHidden(userId);
}
} finally {
Binder.restoreCallingIdentity(callingId);
* @hide
*/
@Override
- public int installExistingPackageAsUser(String packageName, int userId) {
+ public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
+ int installReason) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
null);
PackageSetting pkgSetting;
- final int uid = Binder.getCallingUid();
- enforceCrossUserPermission(uid, userId, true, true, "installExistingPackage for user "
- + userId);
+ final int callingUid = Binder.getCallingUid();
+ 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 sendAdded = false;
+ boolean installed = false;
+ final boolean instantApp =
+ (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
+ final boolean fullApp =
+ (installFlags & PackageManager.INSTALL_FULL_APP) != 0;
// writer
synchronized (mPackages) {
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);
- sendAdded = true;
+ 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 (sendAdded) {
+ if (installed) {
+ if (pkgSetting.pkg != null) {
+ synchronized (mInstallLock) {
+ // We don't need to freeze for a brand new install
+ prepareAppDataAfterInstallLIF(pkgSetting.pkg);
+ }
+ }
sendPackageAddedForUser(packageName, pkgSetting, userId);
+ synchronized (mPackages) {
+ updateSequenceNumberLP(pkgSetting, new int[]{ userId });
+ }
}
} finally {
Binder.restoreCallingIdentity(callingId);
return PackageManager.INSTALL_SUCCEEDED;
}
+ 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)) {
}
@Override
+ public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
+ int userId) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ final int callingUid = Binder.getCallingUid();
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, true /* checkShell */,
+ "setPackagesSuspended for user " + userId);
+
+ if (ArrayUtils.isEmpty(packageNames)) {
+ return packageNames;
+ }
+
+ // List of package names for whom the suspended state has changed.
+ List<String> changedPackages = new ArrayList<>(packageNames.length);
+ // List of package names for whom the suspended state is not set as requested in this
+ // method.
+ List<String> unactionedPackages = new ArrayList<>(packageNames.length);
+ long callingId = Binder.clearCallingIdentity();
+ try {
+ for (int i = 0; i < packageNames.length; i++) {
+ String packageName = packageNames[i];
+ boolean changed = false;
+ final int appId;
+ synchronized (mPackages) {
+ final PackageSetting 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;
+ }
+ appId = pkgSetting.appId;
+ if (pkgSetting.getSuspended(userId) != suspended) {
+ if (!canSuspendPackageForUserLocked(packageName, userId)) {
+ unactionedPackages.add(packageName);
+ continue;
+ }
+ pkgSetting.setSuspended(suspended, userId);
+ mSettings.writePackageRestrictionsLPr(userId);
+ changed = true;
+ changedPackages.add(packageName);
+ }
+ }
+
+ if (changed && suspended) {
+ killApplication(packageName, UserHandle.getUid(userId, appId),
+ "suspending package");
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+
+ if (!changedPackages.isEmpty()) {
+ sendPackagesSuspendedForUser(changedPackages.toArray(
+ new String[changedPackages.size()]), userId, suspended);
+ }
+
+ return unactionedPackages.toArray(new String[unactionedPackages.size()]);
+ }
+
+ @Override
+ public boolean isPackageSuspendedForUser(String packageName, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ 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);
+ }
+ }
+
+ private boolean canSuspendPackageForUserLocked(String packageName, int userId) {
+ if (isPackageDeviceAdmin(packageName, userId)) {
+ Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+ + "\": has an active device admin");
+ return false;
+ }
+
+ String activeLauncherPackageName = getActiveLauncherPackageName(userId);
+ if (packageName.equals(activeLauncherPackageName)) {
+ Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+ + "\": contains the active launcher");
+ return false;
+ }
+
+ if (packageName.equals(mRequiredInstallerPackage)) {
+ Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+ + "\": required for package installation");
+ return false;
+ }
+
+ if (packageName.equals(mRequiredUninstallerPackage)) {
+ Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+ + "\": required for package uninstallation");
+ return false;
+ }
+
+ if (packageName.equals(mRequiredVerifierPackage)) {
+ Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+ + "\": required for package verification");
+ return false;
+ }
+
+ if (packageName.equals(getDefaultDialerPackageName(userId))) {
+ Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+ + "\": is the default dialer");
+ return false;
+ }
+
+ if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+ + "\": protected package");
+ return false;
+ }
+
+ // 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);
+ return false;
+ }
+
+ return true;
+ }
+
+ private String getActiveLauncherPackageName(int userId) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_HOME);
+ ResolveInfo resolveInfo = resolveIntent(
+ intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ PackageManager.MATCH_DEFAULT_ONLY,
+ userId);
+
+ return resolveInfo == null ? null : resolveInfo.activityInfo.packageName;
+ }
+
+ private String getDefaultDialerPackageName(int userId) {
+ synchronized (mPackages) {
+ return mSettings.getDefaultDialerPackageNameLPw(userId);
+ }
+ }
+
+ @Override
public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
}
@Override
- public void finishPackageInstall(int token) {
+ 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, 0);
+ final Message msg = mHandler.obtainMessage(POST_INSTALL, token, didLaunch ? 1 : 0);
mHandler.sendMessage(msg);
}
/**
- * Get the verification agent timeout.
+ * Get the verification agent timeout. Used for both the APK verifier and the
+ * intent filter verifier.
*
* @return verification timeout in milliseconds
*/
*
* @return default verification response code
*/
- private int getDefaultVerificationResponse() {
+ 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);
*
* @return true if verification should be performed
*/
- private boolean isVerificationEnabled(int userId, int installFlags) {
+ private boolean isVerificationEnabled(int userId, int installFlags, int installerUid) {
if (!DEFAULT_VERIFY_ENABLE) {
return false;
}
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) {
@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);
}
}
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) {
}
@Override
- public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
+ public @NonNull ParceledListSlice<IntentFilterVerificationInfo> getIntentFilterVerifications(
+ String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ if (getInstantAppPackageName(callingUid) != null) {
+ return ParceledListSlice.emptyList();
+ }
synchronized (mPackages) {
- return mSettings.getIntentFilterVerificationsLPr(packageName);
+ 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 List<IntentFilter> getAllIntentFilters(String packageName) {
+ public @NonNull ParceledListSlice<IntentFilter> getAllIntentFilters(String packageName) {
if (TextUtils.isEmpty(packageName)) {
- return Collections.<IntentFilter>emptyList();
+ 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 Collections.<IntentFilter>emptyList();
+ 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) {
+ if (activity.intents != null && activity.intents.size() > 0) {
result.addAll(activity.intents);
}
}
- return result;
+ return new ParceledListSlice<>(result);
}
}
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);
+ }
synchronized (mPackages) {
boolean result = mSettings.setDefaultBrowserPackageNameLPw(packageName, userId);
if (packageName != null) {
- result |= updateIntentVerificationStatus(packageName,
- PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
- userId);
mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowserLPr(
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;
+ }
synchronized (mPackages) {
return mSettings.getDefaultBrowserPackageNameLPw(userId);
}
* @return the current "allow unknown sources" setting
*/
private int getUnknownSourcesSettings() {
- return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
- android.provider.Settings.Global.INSTALL_NON_MARKET_APPS,
+ 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 uid = Binder.getCallingUid();
+ final int callingUid = Binder.getCallingUid();
+ if (getInstantAppPackageName(callingUid) != null) {
+ return;
+ }
// writer
synchronized (mPackages) {
PackageSetting targetPackageSetting = mSettings.mPackages.get(targetPackage);
- if (targetPackageSetting == null) {
+ if (targetPackageSetting == null
+ || filterAppAccessLPr(
+ targetPackageSetting, callingUid, UserHandle.getUserId(callingUid))) {
throw new IllegalArgumentException("Unknown target package: " + targetPackage);
}
}
Signature[] callerSignature;
- Object obj = mSettings.getUserIdLPr(uid);
+ Object obj = mSettings.getUserIdLPr(callingUid);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
callerSignature = ((SharedUserSetting)obj).signatures.mSignatures;
} else if (obj instanceof PackageSetting) {
callerSignature = ((PackageSetting)obj).signatures.mSignatures;
} else {
- throw new SecurityException("Bad object " + obj + " for uid " + uid);
+ throw new SecurityException("Bad object " + obj + " for uid " + callingUid);
}
} else {
- throw new SecurityException("Unknown calling uid " + uid);
+ throw new SecurityException("Unknown calling UID: " + callingUid);
}
// Verify: can't set installerPackageName to a package that is
// 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) {
// Queue up an async operation since the package installation may take a little while.
mHandler.post(new Runnable() {
mHandler.removeCallbacks(this);
// Result object to be returned
PackageInstalledInfo res = new PackageInstalledInfo();
- res.returnCode = currentStatus;
+ res.setReturnCode(currentStatus);
res.uid = -1;
res.pkg = null;
- res.removedInfo = new PackageRemovedInfo();
+ res.removedInfo = null;
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
- installPackageLI(args, res);
+ installPackageTracedLI(args, res);
}
args.doPostInstall(res.returnCode, res.uid);
}
// 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.removedPackage != null;
+ 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);
if (bm != null) {
if (DEBUG_INSTALL) Log.v(TAG, "token " + token
+ " to BM for possible restore");
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
try {
- if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) {
+ // TODO: http://b/22388012
+ if (bm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
} else {
doRestore = false;
// 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 pkgName, 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(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < mRunningInstalls.size(); i++) {
+ final PostInstallData data = mRunningInstalls.valueAt(i);
+ if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+ continue;
+ }
+ if (pkgName.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 " + pkgName
+ + " being restored so deferring FIRST_LAUNCH");
+ }
+ return;
+ }
+ }
+ }
+ }
+ // didn't find it, so not being restored
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "Package " + pkgName + " sending normal FIRST_LAUNCH");
+ }
+ sendFirstLaunchBroadcast(pkgName, installerPackage, new int[] {userId});
+ }
+ });
+ }
+
+ private void sendFirstLaunchBroadcast(String pkgName, String installerPkg, int[] userIds) {
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
+ installerPkg, null, userIds);
+ }
+
private abstract class HandlerParams {
private static final int MAX_RETRIES = 4;
/** User handle for the user requesting the information or installation. */
private final UserHandle mUser;
+ String traceMethod;
+ int traceCookie;
HandlerParams(UserHandle user) {
mUser = user;
return mUser;
}
+ HandlerParams setTraceMethod(String traceMethod) {
+ this.traceMethod = traceMethod;
+ return this;
+ }
+
+ HandlerParams setTraceCookie(int traceCookie) {
+ this.traceCookie = traceCookie;
+ return this;
+ }
+
final boolean startCopy() {
boolean res;
try {
abstract void handleReturnCode();
}
- class MeasureParams extends HandlerParams {
- private final PackageStats mStats;
- private boolean mSuccess;
-
- private final IPackageStatsObserver mObserver;
-
- public MeasureParams(PackageStats stats, IPackageStatsObserver observer) {
- super(new UserHandle(stats.userHandle));
- mObserver = observer;
- mStats = stats;
- }
-
- @Override
- public String toString() {
- return "MeasureParams{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + mStats.packageName + "}";
+ private static void clearDirectory(IMediaContainerService mcs, File[] paths) {
+ for (File path : paths) {
+ try {
+ mcs.clearDirectory(path.getAbsolutePath());
+ } catch (RemoteException e) {
+ }
}
+ }
- @Override
- void handleStartCopy() throws RemoteException {
- synchronized (mInstallLock) {
- mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats.userHandle, mStats);
- }
+ 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;
+ final String cid;
- if (mSuccess) {
- final boolean mounted;
- if (Environment.isExternalStorageEmulated()) {
- mounted = true;
- } else {
- final String status = Environment.getExternalStorageState();
- mounted = (Environment.MEDIA_MOUNTED.equals(status)
- || Environment.MEDIA_MOUNTED_READ_ONLY.equals(status));
- }
+ /**
+ * Flag indicating that {@link #file} or {@link #cid} has already been
+ * staged, meaning downstream users don't need to defensively copy the
+ * contents.
+ */
+ final boolean staged;
- if (mounted) {
- final UserEnvironment userEnv = new UserEnvironment(mStats.userHandle);
-
- mStats.externalCacheSize = calculateDirectorySize(mContainerService,
- userEnv.buildExternalStorageAppCacheDirs(mStats.packageName));
-
- mStats.externalDataSize = calculateDirectorySize(mContainerService,
- userEnv.buildExternalStorageAppDataDirs(mStats.packageName));
-
- // Always subtract cache size, since it's a subdirectory
- mStats.externalDataSize -= mStats.externalCacheSize;
-
- mStats.externalMediaSize = calculateDirectorySize(mContainerService,
- userEnv.buildExternalStorageAppMediaDirs(mStats.packageName));
-
- mStats.externalObbSize = calculateDirectorySize(mContainerService,
- userEnv.buildExternalStorageAppObbDirs(mStats.packageName));
- }
- }
- }
-
- @Override
- void handleReturnCode() {
- if (mObserver != null) {
- try {
- mObserver.onGetStatsCompleted(mStats, mSuccess);
- } catch (RemoteException e) {
- Slog.i(TAG, "Observer no longer exists.");
- }
- }
- }
-
- @Override
- void handleServiceError() {
- Slog.e(TAG, "Could not measure application " + mStats.packageName
- + " external storage");
- }
- }
-
- private static long calculateDirectorySize(IMediaContainerService mcs, File[] paths)
- throws RemoteException {
- long result = 0;
- for (File path : paths) {
- result += mcs.calculateDirectorySize(path.getAbsolutePath());
- }
- return result;
- }
-
- private static void clearDirectory(IMediaContainerService mcs, File[] paths) {
- for (File path : paths) {
- try {
- mcs.clearDirectory(path.getAbsolutePath());
- } catch (RemoteException e) {
- }
- }
- }
-
- 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;
- final String cid;
-
- /**
- * Flag indicating that {@link #file} or {@link #cid} has already been
- * staged, meaning downstream users don't need to defensively copy the
- * contents.
- */
- final boolean staged;
-
- /**
- * Flag indicating that {@link #file} or {@link #cid} is an already
- * installed app that is being moved.
- */
- final boolean existing;
+ /**
+ * Flag indicating that {@link #file} or {@link #cid} is an already
+ * installed app that is being moved.
+ */
+ final boolean existing;
final String resolvedPath;
final File resolvedFile;
}
}
- class MoveInfo {
+ static class MoveInfo {
final int moveId;
final String fromUuid;
final String toUuid;
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) {
+ String dataAppName, int appId, String seinfo, int targetSdkVersion) {
this.moveId = moveId;
this.fromUuid = fromUuid;
this.toUuid = toUuid;
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;
}
}
int installFlags;
final String installerPackageName;
final String volumeUuid;
- final VerificationParams verificationParams;
private InstallArgs mArgs;
private int mRet;
final String packageAbiOverride;
final String[] grantedRuntimePermissions;
-
+ final VerificationInfo verificationInfo;
+ final Certificate[][] certificates;
+ final int installReason;
InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, String volumeUuid,
- VerificationParams verificationParams, UserHandle user, String packageAbiOverride,
- String[] grantedPermissions) {
+ VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
+ String[] grantedPermissions, Certificate[][] certificates, int installReason) {
super(user);
this.origin = origin;
this.move = move;
this.installFlags = installFlags;
this.installerPackageName = installerPackageName;
this.volumeUuid = volumeUuid;
- this.verificationParams = verificationParams;
+ this.verificationInfo = verificationInfo;
this.packageAbiOverride = packageAbiOverride;
this.grantedRuntimePermissions = grantedPermissions;
+ this.certificates = certificates;
+ this.installReason = installReason;
}
@Override
+ " file=" + origin.file + " cid=" + origin.cid + "}";
}
- public ManifestDigest getManifestDigest() {
- if (verificationParams == null) {
- return null;
- }
- return verificationParams.getManifestDigest();
- }
-
private int installLocationPolicy(PackageInfoLite pkgLite) {
String packageName = pkgLite.packageName;
int installLocation = pkgLite.installLocation;
boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
// reader
synchronized (mPackages) {
- PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg != null) {
- if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
- // Check for downgrading.
- if ((installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) == 0) {
- try {
- checkDowngrade(pkg, pkgLite);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Downgrade detected: " + e.getMessage());
- return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE;
- }
+ // Currently installed package which the new package is attempting to replace or
+ // null if no such package is installed.
+ PackageParser.Package installedPkg = mPackages.get(packageName);
+ // Package which currently owns the data which the new package will own if installed.
+ // If an app is unstalled while keeping data (e.g., adb uninstall -k), installedPkg
+ // will be null whereas dataOwnerPkg will contain information about the package
+ // which was uninstalled while keeping its data.
+ PackageParser.Package dataOwnerPkg = installedPkg;
+ if (dataOwnerPkg == null) {
+ PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps != null) {
+ dataOwnerPkg = ps.pkg;
+ }
+ }
+
+ if (dataOwnerPkg != null) {
+ // If installed, the package will get access to data left on the device by its
+ // predecessor. As a security measure, this is permited only if this is not a
+ // version downgrade or if the predecessor package is marked as debuggable and
+ // a downgrade is explicitly requested.
+ //
+ // On debuggable platform builds, downgrades are permitted even for
+ // non-debuggable packages to make testing easier. Debuggable platform builds do
+ // not offer security guarantees and thus it's OK to disable some security
+ // mechanisms to make debugging/testing easier on those builds. However, even on
+ // debuggable builds downgrades of packages are permitted only if requested via
+ // installFlags. This is because we aim to keep the behavior of debuggable
+ // platform builds as close as possible to the behavior of non-debuggable
+ // platform builds.
+ final boolean downgradeRequested =
+ (installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0;
+ final boolean packageDebuggable =
+ (dataOwnerPkg.applicationInfo.flags
+ & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ final boolean downgradePermitted =
+ (downgradeRequested) && ((Build.IS_DEBUGGABLE) || (packageDebuggable));
+ if (!downgradePermitted) {
+ 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 ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if ((installedPkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
if (onSd) {
Slog.w(TAG, "Cannot install update to system app on sdcard");
return PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION;
// App explictly prefers external. Let policy decide
} else {
// Prefer previous location
- if (isExternal(pkg)) {
+ if (isExternal(installedPkg)) {
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
}
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
-
+ final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
PackageInfoLite pkgLite = null;
if (onInt && onSd) {
// Check if both bits are set.
Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+ } else if (onSd && ephemeral) {
+ Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external");
+ ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
packageAbiOverride);
+ if (DEBUG_EPHEMERAL && ephemeral) {
+ Slog.v(TAG, "pkgLite for install: " + pkgLite);
+ }
+
/*
* If we have too little free space, try to free cache
* before giving up.
final long sizeBytes = mContainerService.calculateInstalledSize(
origin.resolvedPath, isForwardLocked(), packageAbiOverride);
- if (mInstaller.freeCache(null, sizeBytes + lowThreshold) >= 0) {
+ try {
+ mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
installFlags, packageAbiOverride);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to free cache", e);
}
/*
// Set the flag to install on external media.
installFlags |= PackageManager.INSTALL_EXTERNAL;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
+ } else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
+ }
+ installFlags |= PackageManager.INSTALL_INSTANT_APP;
+ installFlags &= ~(PackageManager.INSTALL_EXTERNAL
+ |PackageManager.INSTALL_INTERNAL);
} else {
// Make sure the flag for installing on external
// media is unset
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
- /*
- * ADB installs appear as UserHandle.USER_ALL, and can only be performed by
- * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER.
- */
- int userIdentifier = getUser().getIdentifier();
- if (userIdentifier == UserHandle.USER_ALL
- && ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)) {
- userIdentifier = UserHandle.USER_OWNER;
+ // 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;
}
/*
* do, then we'll defer to them to verify the packages.
*/
final int requiredUid = mRequiredVerifierPackage == null ? -1
- : getPackageUid(mRequiredVerifierPackage, userIdentifier);
+ : getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+ verifierUser.getIdentifier());
+ final int installerUid =
+ verificationInfo == null ? -1 : verificationInfo.installerUid;
if (!origin.existing && requiredUid != -1
- && isVerificationEnabled(userIdentifier, installFlags)) {
+ && isVerificationEnabled(
+ verifierUser.getIdentifier(), installFlags, installerUid)) {
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
PACKAGE_MIME_TYPE);
verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- final List<ResolveInfo> receivers = queryIntentReceivers(verification,
- PACKAGE_MIME_TYPE, PackageManager.GET_DISABLED_COMPONENTS,
- 0 /* TODO: Which userId? */);
+ // 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.putExtra(PackageManager.EXTRA_VERIFICATION_VERSION_CODE,
pkgLite.versionCode);
- if (verificationParams != null) {
- if (verificationParams.getVerificationURI() != null) {
- verification.putExtra(PackageManager.EXTRA_VERIFICATION_URI,
- verificationParams.getVerificationURI());
- }
- if (verificationParams.getOriginatingURI() != null) {
+ if (verificationInfo != null) {
+ if (verificationInfo.originatingUri != null) {
verification.putExtra(Intent.EXTRA_ORIGINATING_URI,
- verificationParams.getOriginatingURI());
+ verificationInfo.originatingUri);
}
- if (verificationParams.getReferrer() != null) {
+ if (verificationInfo.referrer != null) {
verification.putExtra(Intent.EXTRA_REFERRER,
- verificationParams.getReferrer());
+ verificationInfo.referrer);
}
- if (verificationParams.getOriginatingUid() >= 0) {
+ if (verificationInfo.originatingUid >= 0) {
verification.putExtra(Intent.EXTRA_ORIGINATING_UID,
- verificationParams.getOriginatingUid());
+ verificationInfo.originatingUid);
}
- if (verificationParams.getInstallerUid() >= 0) {
+ if (verificationInfo.installerUid >= 0) {
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID,
- verificationParams.getInstallerUid());
+ verificationInfo.installerUid);
}
}
final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
receivers, verificationState);
- // Apps installed for "all" users use the device owner to verify the app
- UserHandle verifierUser = getUser();
- if (verifierUser == UserHandle.ALL) {
- verifierUser = UserHandle.OWNER;
- }
+ DeviceIdleController.LocalService idleController = getDeviceIdleController();
+ final long idleDuration = getVerificationTimeout();
/*
* If any sufficient verifiers were listed in the package
} 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);
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() {
final int installFlags;
final String installerPackageName;
final String volumeUuid;
- final ManifestDigest manifestDigest;
final UserHandle user;
final String abiOverride;
final String[] installGrantPermissions;
+ /** If non-null, drop an async trace when the install completes */
+ final String traceMethod;
+ final int traceCookie;
+ final Certificate[][] certificates;
+ final int installReason;
// 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
InstallArgs(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, String volumeUuid,
- ManifestDigest manifestDigest, UserHandle user, String[] instructionSets,
- String abiOverride, String[] installGrantPermissions) {
+ UserHandle user, String[] instructionSets,
+ String abiOverride, String[] installGrantPermissions,
+ String traceMethod, int traceCookie, Certificate[][] certificates,
+ int installReason) {
this.origin = origin;
this.move = move;
this.installFlags = installFlags;
this.observer = observer;
this.installerPackageName = installerPackageName;
this.volumeUuid = volumeUuid;
- this.manifestDigest = manifestDigest;
this.user = user;
this.instructionSets = instructionSets;
this.abiOverride = abiOverride;
this.installGrantPermissions = installGrantPermissions;
+ this.traceMethod = traceMethod;
+ this.traceCookie = traceCookie;
+ this.certificates = certificates;
+ this.installReason = installReason;
}
abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
return (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
}
+ protected boolean isEphemeral() {
+ return (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
+ }
+
UserHandle getUser() {
return user;
}
}
- private void removeDexFiles(List<String> allCodePaths, String[] instructionSets) {
+ 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) {
- int retCode = mInstaller.rmdex(codePath, dexCodeInstructionSet);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't remove dex file for package: "
- + " at location " + codePath + ", retcode=" + retCode);
- // we don't consider this to be a failure of the core package deletion
+ try {
+ mInstaller.rmdex(codePath, dexCodeInstructionSet);
+ } catch (InstallerException ignored) {
}
}
}
/** New install */
FileInstallArgs(InstallParams params) {
super(params.origin, params.move, params.observer, params.installFlags,
- params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
- params.getUser(), null /* instruction sets */, params.packageAbiOverride,
- params.grantedRuntimePermissions);
+ params.installerPackageName, params.volumeUuid,
+ params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
+ params.grantedRuntimePermissions,
+ params.traceMethod, params.traceCookie, params.certificates,
+ params.installReason);
if (isFwdLocked()) {
throw new IllegalArgumentException("Forward locking only supported in ASEC");
}
/** Existing install */
FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
- super(OriginInfo.fromNothing(), null, null, 0, null, null, null, null, instructionSets,
- null, null);
+ super(OriginInfo.fromNothing(), null, null, 0, null, null, null, instructionSets,
+ null, null, null, 0, null /*certificates*/,
+ PackageManager.INSTALL_REASON_UNKNOWN);
this.codeFile = (codePath != null) ? new File(codePath) : null;
this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
}
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
+ try {
+ return doCopyApk(imcs, temp);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
if (origin.staged) {
if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
codeFile = origin.file;
}
try {
- final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid);
+ final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
+ final File tempDir =
+ mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
resourceFile = afterCodeFile;
// Reflect the rename in scanned details
- pkg.codePath = afterCodeFile.getAbsolutePath();
- pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
- pkg.baseCodePath);
- pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
- pkg.splitCodePaths);
+ pkg.setCodePath(afterCodeFile.getAbsolutePath());
+ pkg.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
+ afterCodeFile, pkg.baseCodePath));
+ pkg.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
+ afterCodeFile, pkg.splitCodePaths));
// Reflect the rename in app info
- pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
- pkg.applicationInfo.setCodePath(pkg.codePath);
- pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
- pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
- pkg.applicationInfo.setResourcePath(pkg.codePath);
- pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
- pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
+ 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;
}
return false;
}
- if (codeFile.isDirectory()) {
- mInstaller.rmPackageDir(codeFile.getAbsolutePath());
- } else {
- codeFile.delete();
- }
+ removeCodePathLI(codeFile);
if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
resourceFile.delete();
}
/**
- * Extract the MountService "container ID" from the full code path of an
+ * Extract the StorageManagerService "container ID" from the full code path of an
* .apk.
*/
static String cidFromCodePath(String fullCodePath) {
/** New install */
AsecInstallArgs(InstallParams params) {
super(params.origin, params.move, params.observer, params.installFlags,
- params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
+ params.installerPackageName, params.volumeUuid,
params.getUser(), null /* instruction sets */, params.packageAbiOverride,
- params.grantedRuntimePermissions);
+ params.grantedRuntimePermissions,
+ params.traceMethod, params.traceCookie, params.certificates,
+ params.installReason);
}
/** Existing install */
AsecInstallArgs(String fullCodePath, String[] instructionSets,
boolean isExternal, boolean isForwardLocked) {
super(OriginInfo.fromNothing(), null, null, (isExternal ? INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null,
- instructionSets, null, null);
+ | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
+ instructionSets, null, null, null, 0, null /*certificates*/,
+ PackageManager.INSTALL_REASON_UNKNOWN);
// Hackily pretend we're still looking at a full code path
if (!fullCodePath.endsWith(RES_FILE_NAME)) {
fullCodePath = new File(fullCodePath, RES_FILE_NAME).getAbsolutePath();
AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) {
super(OriginInfo.fromNothing(), null, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null,
- instructionSets, null, null);
+ | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
+ instructionSets, null, null, null, 0, null /*certificates*/,
+ PackageManager.INSTALL_REASON_UNKNOWN);
this.cid = cid;
setMountPath(PackageHelper.getSdDir(cid));
}
}
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
- if (origin.staged) {
+ if (origin.staged && origin.cid != null) {
if (DEBUG_INSTALL) Slog.d(TAG, origin.cid + " already staged; skipping copy");
cid = origin.cid;
setMountPath(PackageHelper.getSdDir(cid));
final File afterCodeFile = new File(packagePath);
// Reflect the rename in scanned details
- pkg.codePath = afterCodeFile.getAbsolutePath();
- pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
- pkg.baseCodePath);
- pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
- pkg.splitCodePaths);
+ pkg.setCodePath(afterCodeFile.getAbsolutePath());
+ pkg.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
+ afterCodeFile, pkg.baseCodePath));
+ pkg.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
+ afterCodeFile, pkg.splitCodePaths));
// Reflect the rename in app info
- pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
- pkg.applicationInfo.setCodePath(pkg.codePath);
- pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
- pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
- pkg.applicationInfo.setResourcePath(pkg.codePath);
- pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
- pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
+ 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;
}
@Override
int doPreCopy() {
if (isFwdLocked()) {
- if (!PackageHelper.fixSdPermissions(cid,
- getPackageUid(DEFAULT_CONTAINER_PACKAGE, 0), RES_FILE_NAME)) {
+ if (!PackageHelper.fixSdPermissions(cid, getPackageUid(DEFAULT_CONTAINER_PACKAGE,
+ MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM), RES_FILE_NAME)) {
return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
}
}
/** New install */
MoveInstallArgs(InstallParams params) {
super(params.origin, params.move, params.observer, params.installFlags,
- params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
+ params.installerPackageName, params.volumeUuid,
params.getUser(), null /* instruction sets */, params.packageAbiOverride,
- params.grantedRuntimePermissions);
+ params.grantedRuntimePermissions,
+ params.traceMethod, params.traceCookie, params.certificates,
+ params.installReason);
}
int copyApk(IMediaContainerService imcs, boolean temp) {
if (DEBUG_INSTALL) Slog.d(TAG, "Moving " + move.packageName + " from "
+ move.fromUuid + " to " + move.toUuid);
synchronized (mInstaller) {
- if (mInstaller.copyCompleteApp(move.fromUuid, move.toUuid, move.packageName,
- move.dataAppName, move.appId, move.seinfo) != 0) {
+ 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;
}
}
}
// Reflect the move in app info
- pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
- pkg.applicationInfo.setCodePath(pkg.codePath);
- pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
- pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
- pkg.applicationInfo.setResourcePath(pkg.codePath);
- pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
- pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
+ 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;
}
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
- removeDataDirsLI(volumeUuid, move.packageName);
- if (codeFile.isDirectory()) {
- mInstaller.rmPackageDir(codeFile.getAbsolutePath());
- } else {
- codeFile.delete();
+ // All package moves are frozen until finished
+ for (int userId : userIds) {
+ try {
+ mInstaller.destroyAppData(volumeUuid, move.packageName, userId,
+ StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE, 0);
+ } catch (InstallerException e) {
+ Slog.w(TAG, String.valueOf(e));
+ }
}
+ removeCodePathLI(codeFile);
}
return true;
}
}
private File getNextCodePath(File targetDir, String packageName) {
- int suffix = 1;
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);
- suffix++;
} while (result.exists());
return result;
}
}
}
- class PackageInstalledInfo {
+ static class PackageInstalledInfo {
String name;
int uid;
// The set of users that originally had this package installed.
PackageParser.Package pkg;
int returnCode;
String returnMsg;
+ String installerPackageName;
PackageRemovedInfo removedInfo;
+ ArrayMap<String, PackageInstalledInfo> addedChildPackages;
public void setError(int code, String msg) {
- returnCode = code;
- returnMsg = msg;
+ setReturnCode(code);
+ setReturnMessage(msg);
Slog.w(TAG, msg);
}
public void setError(String msg, PackageParserException e) {
- returnCode = e.error;
- returnMsg = ExceptionUtils.getCompleteMessage(msg, 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;
- returnMsg = ExceptionUtils.getCompleteMessage(msg, e);
+ 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;
/*
* Install a non-existing package.
*/
- private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags,
- UserHandle user, String installerPackageName, String volumeUuid,
- PackageInstalledInfo res) {
+ private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags,
+ int scanFlags, UserHandle user, String installerPackageName, String volumeUuid,
+ PackageInstalledInfo res, int installReason) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage");
+
// Remember this for later, in case we need to rollback this install
String pkgName = pkg.packageName;
if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
- final boolean dataDirExists = Environment
- .getDataUserPackageDirectory(volumeUuid, UserHandle.USER_OWNER, pkgName).exists();
+
synchronized(mPackages) {
- if (mSettings.mRenamedPackages.containsKey(pkgName)) {
+ final String renamedPackage = mSettings.getRenamedPackageLPr(pkgName);
+ 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.
res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
+ " without first uninstalling package running as "
- + mSettings.mRenamedPackages.get(pkgName));
+ + renamedPackage);
return;
}
if (mPackages.containsKey(pkgName)) {
}
try {
- PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,
+ PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags,
System.currentTimeMillis(), user);
- updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user);
- // delete the partially installed application. the data directory will have to be
- // restored if it was already existing
- if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
- // remove package from internal structures. Note that we want deletePackageX to
- // delete the package data and cache directories that it created in
- // scanPackageLocked, unless those directories existed before we even tried to
- // install.
- deletePackageLI(pkgName, UserHandle.ALL, false, null, null,
- dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
- res.removedInfo, true);
- }
+ updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);
+
+ if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ prepareAppDataAfterInstallLIF(newPackage);
+ } else {
+ // Remove package from internal structures, but keep around any
+ // data that might have already existed
+ deletePackageLIF(pkgName, UserHandle.ALL, false, null,
+ PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
+ }
} catch (PackageManagerException e) {
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
+
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
private boolean shouldCheckUpgradeKeySetLP(PackageSetting oldPs, int scanFlags) {
return false;
}
- private void replacePackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags,
- UserHandle user, String installerPackageName, String volumeUuid,
- PackageInstalledInfo res) {
+ private static void updateDigest(MessageDigest digest, File file) throws IOException {
+ try (DigestInputStream digestStream =
+ new DigestInputStream(new FileInputStream(file), digest)) {
+ while (digestStream.read() != -1) {} // nothing to do; just plow through the file
+ }
+ }
+
+ private void replacePackageLIF(PackageParser.Package pkg, final int policyFlags, int scanFlags,
+ UserHandle user, String installerPackageName, PackageInstalledInfo res,
+ int installReason) {
+ final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+
final PackageParser.Package oldPackage;
+ final PackageSetting ps;
final String pkgName = pkg.packageName;
final int[] allUsers;
- final boolean[] perUserInstalled;
+ final int[] installedUsers;
- // First find the old package info and check signatures
synchronized(mPackages) {
oldPackage = mPackages.get(pkgName);
if (DEBUG_INSTALL) Slog.d(TAG, "replacePackageLI: new=" + pkg + ", old=" + oldPackage);
- final PackageSetting ps = mSettings.mPackages.get(pkgName);
+
+ // don't allow upgrade to target a release SDK from a pre-release SDK
+ final boolean oldTargetsPreRelease = oldPackage.applicationInfo.targetSdkVersion
+ == android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
+ final boolean newTargetsPreRelease = pkg.applicationInfo.targetSdkVersion
+ == android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
+ if (oldTargetsPreRelease
+ && !newTargetsPreRelease
+ && ((policyFlags & PackageParser.PARSE_FORCE_SDK) == 0)) {
+ Slog.w(TAG, "Can't install package targeting released sdk");
+ res.setReturnCode(PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE);
+ return;
+ }
+
+ ps = mSettings.mPackages.get(pkgName);
+
+ // verify signatures are valid
if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {
- if(!checkUpgradeKeySetLP(ps, pkg)) {
+ if (!checkUpgradeKeySetLP(ps, pkg)) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"New package not signed by keys specified by upgrade-keysets: "
- + pkgName);
+ + pkgName);
return;
}
} else {
// default to original signature matching
if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures)
- != PackageManager.SIGNATURE_MATCH) {
+ != PackageManager.SIGNATURE_MATCH) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"New package has a different signature: " + pkgName);
return;
}
}
+ // don't allow a system upgrade unless the upgrade hash matches
+ if (oldPackage.restrictUpdateHash != null && oldPackage.isSystemApp()) {
+ byte[] digestBytes = null;
+ 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) {
+ res.setError(INSTALL_FAILED_INVALID_APK,
+ "Could not compute hash: " + pkgName);
+ return;
+ }
+ if (!Arrays.equals(oldPackage.restrictUpdateHash, digestBytes)) {
+ res.setError(INSTALL_FAILED_INVALID_APK,
+ "New package fails restrict-update check: " + pkgName);
+ return;
+ }
+ // retain upgrade restriction
+ pkg.restrictUpdateHash = oldPackage.restrictUpdateHash;
+ }
+
+ // Check for shared user id changes
+ String invalidPackageName =
+ getParentOrChildPackageChangedSharedUser(oldPackage, pkg);
+ if (invalidPackageName != null) {
+ res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
+ "Package " + invalidPackageName + " tried to change user "
+ + oldPackage.mSharedUserId);
+ return;
+ }
+
// In case of rollback, remember per-user/profile install state
allUsers = sUserManager.getUserIds();
- perUserInstalled = new boolean[allUsers.length];
- for (int i = 0; i < allUsers.length; i++) {
- perUserInstalled[i] = ps != null ? ps.getInstalled(allUsers[i]) : false;
+ installedUsers = ps.queryInstalledUsers(allUsers, true);
+
+ // don't allow an upgrade from full to ephemeral
+ if (isInstantApp) {
+ if (user == null || 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: " + pkgName
+ + " for user: " + currentUser);
+ res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+ return;
+ }
+ }
+ } else if (!ps.getInstantApp(user.getIdentifier())) {
+ // can't downgrade from full to instant
+ Slog.w(TAG, "Can't replace full app with instant app: " + pkgName
+ + " for user: " + user.getIdentifier());
+ res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+ return;
+ }
+ }
+ }
+
+ // 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));
+ }
+
+ final int childCount = (oldPackage.childPackages != null)
+ ? oldPackage.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ boolean childPackageUpdated = false;
+ PackageParser.Package childPkg = oldPackage.childPackages.get(i);
+ final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
+ 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);
}
}
boolean sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
- replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
- user, allUsers, perUserInstalled, installerPackageName, volumeUuid, res);
+ // Set the system/privileged flags as needed
+ final boolean privileged =
+ (oldPackage.applicationInfo.privateFlags
+ & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+ final int systemPolicyFlags = policyFlags
+ | PackageParser.PARSE_IS_SYSTEM
+ | (privileged ? PackageParser.PARSE_IS_PRIVILEGED : 0);
+
+ replaceSystemPackageLIF(oldPackage, pkg, systemPolicyFlags, scanFlags,
+ user, allUsers, installerPackageName, res, installReason);
} else {
- replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
- user, allUsers, perUserInstalled, installerPackageName, volumeUuid, res);
+ replaceNonSystemPackageLIF(oldPackage, pkg, policyFlags, scanFlags,
+ user, allUsers, installerPackageName, res, installReason);
+ }
+ }
+
+ @Override
+ public List<String> getPreviousCodePaths(String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ final List<String> result = new ArrayList<>();
+ if (getInstantAppPackageName(callingUid) != null) {
+ return result;
}
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps != null
+ && ps.oldCodePaths != null
+ && !filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
+ result.addAll(ps.oldCodePaths);
+ }
+ return result;
}
- private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
- PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,
- int[] allUsers, boolean[] perUserInstalled, String installerPackageName,
- String volumeUuid, PackageInstalledInfo res) {
+ private void replaceNonSystemPackageLIF(PackageParser.Package deletedPackage,
+ PackageParser.Package pkg, final int policyFlags, int scanFlags, UserHandle user,
+ int[] allUsers, String installerPackageName, PackageInstalledInfo res,
+ int installReason) {
+ if (DEBUG_INSTALL) Slog.d(TAG, "replaceNonSystemPackageLI: new=" + pkg + ", old="
+ + deletedPackage);
+
String pkgName = deletedPackage.packageName;
boolean deletedPkg = true;
+ boolean addedPkg = false;
boolean updatedSettings = false;
+ final boolean killApp = (scanFlags & SCAN_DONT_KILL_APP) == 0;
+ final int deleteFlags = PackageManager.DELETE_KEEP_DATA
+ | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
- if (DEBUG_INSTALL) Slog.d(TAG, "replaceNonSystemPackageLI: new=" + pkg + ", old="
- + deletedPackage);
- long origUpdateTime;
- if (pkg.mExtras != null) {
- origUpdateTime = ((PackageSetting)pkg.mExtras).lastUpdateTime;
- } else {
- origUpdateTime = 0;
- }
+ final long origUpdateTime = (pkg.mExtras != null)
+ ? ((PackageSetting)pkg.mExtras).lastUpdateTime : 0;
// First delete the existing package while retaining the data directory
- if (!deletePackageLI(pkgName, null, true, null, null, PackageManager.DELETE_KEEP_DATA,
- res.removedInfo, true)) {
+ if (!deletePackageLIF(pkgName, null, true, allUsers, deleteFlags,
+ res.removedInfo, true, pkg)) {
// If the existing package wasn't successfully deleted
res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE, "replaceNonSystemPackageLI");
deletedPkg = false;
sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
}
- deleteCodeCacheDirsLI(pkg.volumeUuid, pkgName);
+ clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
+ | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ clearAppProfilesLIF(deletedPackage, UserHandle.USER_ALL);
+
try {
- final PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags,
+ final PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags,
scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);
- updateSettingsLI(newPackage, installerPackageName, volumeUuid, allUsers,
- perUserInstalled, res, user);
- updatedSettings = true;
+ updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,
+ installReason);
+
+ // Update the in-memory copy of the previous code paths.
+ PackageSetting ps = mSettings.mPackages.get(pkgName);
+ if (!killApp) {
+ if (ps.oldCodePaths == null) {
+ ps.oldCodePaths = new ArraySet<>();
+ }
+ Collections.addAll(ps.oldCodePaths, deletedPackage.baseCodePath);
+ if (deletedPackage.splitCodePaths != null) {
+ Collections.addAll(ps.oldCodePaths, deletedPackage.splitCodePaths);
+ }
+ } else {
+ ps.oldCodePaths = null;
+ }
+ if (ps.childPackageNames != null) {
+ for (int i = ps.childPackageNames.size() - 1; i >= 0; --i) {
+ final String childPkgName = ps.childPackageNames.get(i);
+ final PackageSetting childPs = mSettings.mPackages.get(childPkgName);
+ childPs.oldCodePaths = ps.oldCodePaths;
+ }
+ }
+ // set instant app status, but, only if it's explicitly specified
+ final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+ final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
+ setInstantAppForUser(ps, user.getIdentifier(), instantApp, fullApp);
+ prepareAppDataAfterInstallLIF(newPackage);
+ addedPkg = true;
+ mDexManager.notifyPackageUpdated(newPackage.packageName,
+ newPackage.baseCodePath, newPackage.splitCodePaths);
} catch (PackageManagerException e) {
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
}
if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
- // remove package from internal structures. Note that we want deletePackageX to
- // delete the package data and cache directories that it created in
- // scanPackageLocked, unless those directories existed before we even tried to
- // install.
- if(updatedSettings) {
- if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName);
- deletePackageLI(
- pkgName, null, true, allUsers, perUserInstalled,
- PackageManager.DELETE_KEEP_DATA,
- res.removedInfo, true);
- }
- // Since we failed to install the new package we need to restore the old
- // package that we deleted.
+ if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName);
+
+ // Revert all internal state mutations and added folders for the failed install
+ if (addedPkg) {
+ deletePackageLIF(pkgName, null, true, allUsers, deleteFlags,
+ res.removedInfo, true, null);
+ }
+
+ // Restore the old package
if (deletedPkg) {
if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);
File restoreFile = new File(deletedPackage.codePath);
(oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
try {
- scanPackageLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime, null);
+ scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime,
+ null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade: "
+ e.getMessage());
return;
}
- // Restore of old package succeeded. Update permissions.
- // writer
+
synchronized (mPackages) {
- updatePermissionsLPw(deletedPackage.packageName, deletedPackage,
- UPDATE_PERMISSIONS_ALL);
- // can downgrade to reader
+ // Ensure the installer package name up to date
+ setInstallerPackageNameLPw(deletedPackage, installerPackageName);
+
+ // Update permissions for restored package
+ updatePermissionsLPw(deletedPackage, UPDATE_PERMISSIONS_ALL);
+
mSettings.writeLPr();
}
+
Slog.i(TAG, "Successfully restored package : " + pkgName + " after failed upgrade");
}
+ } else {
+ synchronized (mPackages) {
+ PackageSetting ps = mSettings.getPackageLPr(pkg.packageName);
+ if (ps != null) {
+ res.removedInfo.removedForAllUsers = mPackages.get(ps.name) == null;
+ if (res.removedInfo.removedChildPackages != null) {
+ final int childCount = res.removedInfo.removedChildPackages.size();
+ // Iterate in reverse as we may modify the collection
+ for (int i = childCount - 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;
+ }
+ }
+ }
+ }
+ }
}
}
- private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
- PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,
- int[] allUsers, boolean[] perUserInstalled, String installerPackageName,
- String volumeUuid, PackageInstalledInfo res) {
+ private void replaceSystemPackageLIF(PackageParser.Package deletedPackage,
+ PackageParser.Package pkg, final int policyFlags, int scanFlags, UserHandle user,
+ int[] allUsers, String installerPackageName, PackageInstalledInfo res,
+ int installReason) {
if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
+ ", old=" + deletedPackage);
- boolean disabledSystem = false;
- boolean updatedSettings = false;
- parseFlags |= PackageParser.PARSE_IS_SYSTEM;
- if ((deletedPackage.applicationInfo.privateFlags&ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
- != 0) {
- parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
- }
- String packageName = deletedPackage.packageName;
- if (packageName == null) {
- res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE,
- "Attempt to delete null packageName.");
- return;
- }
- PackageParser.Package oldPkg;
- PackageSetting oldPkgSetting;
- // reader
- synchronized (mPackages) {
- oldPkg = mPackages.get(packageName);
- oldPkgSetting = mSettings.mPackages.get(packageName);
- if((oldPkg == null) || (oldPkg.applicationInfo == null) ||
- (oldPkgSetting == null)) {
- res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE,
- "Couldn't find package:" + packageName + " information");
- return;
- }
- }
- killApplication(packageName, oldPkg.applicationInfo.uid, "replace sys pkg");
+ final boolean disabledSystem;
- res.removedInfo.uid = oldPkg.applicationInfo.uid;
- res.removedInfo.removedPackage = packageName;
// Remove existing system package
- removePackageLI(oldPkgSetting, true);
- // writer
+ removePackageLI(deletedPackage, true);
+
synchronized (mPackages) {
- disabledSystem = mSettings.disableSystemPackageLPw(packageName);
- if (!disabledSystem && deletedPackage != null) {
- // 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(0,
- deletedPackage.applicationInfo.getCodePath(),
- deletedPackage.applicationInfo.getResourcePath(),
- getAppDexInstructionSets(deletedPackage.applicationInfo));
- } else {
- res.removedInfo.args = null;
- }
+ disabledSystem = disableSystemPackageLPw(deletedPackage, pkg);
+ }
+ if (!disabledSystem) {
+ // 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(0,
+ deletedPackage.applicationInfo.getCodePath(),
+ deletedPackage.applicationInfo.getResourcePath(),
+ getAppDexInstructionSets(deletedPackage.applicationInfo));
+ } else {
+ res.removedInfo.args = null;
}
// Successfully disabled the old package. Now proceed with re-installation
- deleteCodeCacheDirsLI(pkg.volumeUuid, packageName);
+ clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
+ | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ clearAppProfilesLIF(deletedPackage, UserHandle.USER_ALL);
- res.returnCode = PackageManager.INSTALL_SUCCEEDED;
- pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+ res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
+ ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
PackageParser.Package newPackage = null;
try {
- newPackage = scanPackageLI(pkg, parseFlags, scanFlags, 0, user);
- if (newPackage.mExtras != null) {
- final PackageSetting newPkgSetting = (PackageSetting) newPackage.mExtras;
- newPkgSetting.firstInstallTime = oldPkgSetting.firstInstallTime;
- newPkgSetting.lastUpdateTime = System.currentTimeMillis();
+ // Add the package to the internal data structures
+ newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags, 0, user);
- // is the update attempting to change shared user? that isn't going to work...
- if (oldPkgSetting.sharedUser != newPkgSetting.sharedUser) {
- res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
- "Forbidding shared user change from " + oldPkgSetting.sharedUser
- + " to " + newPkgSetting.sharedUser);
- updatedSettings = true;
- }
- }
+ // Set the update and install times
+ PackageSetting deletedPkgSetting = (PackageSetting) deletedPackage.mExtras;
+ setInstallAndUpdateTime(newPackage, deletedPkgSetting.firstInstallTime,
+ System.currentTimeMillis());
+ // Update the package dynamic state if succeeded
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- updateSettingsLI(newPackage, installerPackageName, volumeUuid, allUsers,
- perUserInstalled, res, user);
- updatedSettings = true;
- }
+ // Now that the install succeeded make sure we remove data
+ // directories for any child package the update removed.
+ final int deletedChildCount = (deletedPackage.childPackages != null)
+ ? deletedPackage.childPackages.size() : 0;
+ final int newChildCount = (newPackage.childPackages != null)
+ ? newPackage.childPackages.size() : 0;
+ for (int i = 0; i < deletedChildCount; i++) {
+ PackageParser.Package deletedChildPkg = deletedPackage.childPackages.get(i);
+ boolean childPackageDeleted = true;
+ for (int j = 0; j < newChildCount; j++) {
+ PackageParser.Package newChildPkg = newPackage.childPackages.get(j);
+ if (deletedChildPkg.packageName.equals(newChildPkg.packageName)) {
+ childPackageDeleted = false;
+ break;
+ }
+ }
+ if (childPackageDeleted) {
+ PackageSetting ps = mSettings.getDisabledSystemPkgLPr(
+ deletedChildPkg.packageName);
+ if (ps != null && res.removedInfo.removedChildPackages != null) {
+ PackageRemovedInfo removedChildRes = res.removedInfo
+ .removedChildPackages.get(deletedChildPkg.packageName);
+ removePackageDataLIF(ps, allUsers, removedChildRes, 0, false);
+ removedChildRes.removedForAllUsers = mPackages.get(ps.name) == null;
+ }
+ }
+ }
+ updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,
+ installReason);
+ prepareAppDataAfterInstallLIF(newPackage);
+
+ mDexManager.notifyPackageUpdated(newPackage.packageName,
+ newPackage.baseCodePath, newPackage.splitCodePaths);
+ }
} catch (PackageManagerException e) {
+ res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
}
// Add back the old system package
try {
- scanPackageLI(oldPkg, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);
+ scanPackageTracedLI(deletedPackage, policyFlags, SCAN_UPDATE_SIGNATURE, 0, user);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to restore original package: " + e.getMessage());
}
- // Restore the old system information in Settings
+
synchronized (mPackages) {
if (disabledSystem) {
- mSettings.enableSystemPackageLPw(packageName);
- }
- if (updatedSettings) {
- mSettings.setInstallerPackageName(packageName,
- oldPkgSetting.installerPackageName);
+ enableSystemPackageLPw(deletedPackage);
}
+
+ // Ensure the installer package name up to date
+ setInstallerPackageNameLPw(deletedPackage, installerPackageName);
+
+ // Update permissions for restored package
+ updatePermissionsLPw(deletedPackage, UPDATE_PERMISSIONS_ALL);
+
mSettings.writeLPr();
}
+
+ Slog.i(TAG, "Successfully restored package : " + deletedPackage.packageName
+ + " after failed upgrade");
}
}
- private int[] revokeUnusedSharedUserPermissionsLPw(SharedUserSetting su, int[] allUserIds) {
- // Collect all used permissions in the UID
- ArraySet<String> usedPermissions = new ArraySet<>();
- final int packageCount = su.packages.size();
- for (int i = 0; i < packageCount; i++) {
- PackageSetting ps = su.packages.valueAt(i);
- if (ps.pkg == null) {
- continue;
+ /**
+ * 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;
+ }
}
- final int requestedPermCount = ps.pkg.requestedPermissions.size();
- for (int j = 0; j < requestedPermCount; j++) {
- String permission = ps.pkg.requestedPermissions.get(j);
+ }
+ 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);
+ }
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+
+ 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;
+ }
+
+ 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 int[] revokeUnusedSharedUserPermissionsLPw(SharedUserSetting su, int[] allUserIds) {
+ // Collect all used permissions in the UID
+ ArraySet<String> usedPermissions = new ArraySet<>();
+ final int packageCount = su.packages.size();
+ for (int i = 0; i < packageCount; i++) {
+ PackageSetting ps = su.packages.valueAt(i);
+ if (ps.pkg == null) {
+ continue;
+ }
+ final int requestedPermCount = ps.pkg.requestedPermissions.size();
+ for (int j = 0; j < requestedPermCount; j++) {
+ String permission = ps.pkg.requestedPermissions.get(j);
BasePermission bp = mSettings.mPermissions.get(permission);
if (bp != null) {
usedPermissions.add(permission);
}
private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
- String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res,
- UserHandle user) {
+ 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 newPackage,
+ String installerPackageName, int[] allUsers, int[] installedForUsers,
+ PackageInstalledInfo res, UserHandle user, int installReason) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
+
String pkgName = newPackage.packageName;
synchronized (mPackages) {
//write settings. the installStatus will be incomplete at this stage.
//note that the new package setting would have already been
//added to mPackages. It hasn't been persisted yet.
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);
+ // TODO: Remove this write? It's also written at the end of this method
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
mSettings.writeLPr();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.codePath);
-
synchronized (mPackages) {
updatePermissionsLPw(newPackage.packageName, newPackage,
UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
// 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(newPackage)) {
- // NB: implicit assumption that system package upgrades apply to all users
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 userHandle : res.origUsers) {
- ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
- userHandle, installerPackageName);
+ 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 && perUserInstalled != null) {
- for (int i = 0; i < allUsers.length; i++) {
+ if (allUsers != null && installedForUsers != null) {
+ for (int currentUserId : allUsers) {
+ final boolean installed = ArrayUtils.contains(
+ installedForUsers, currentUserId);
if (DEBUG_INSTALL) {
- Slog.d(TAG, " user " + allUsers[i]
- + " => " + perUserInstalled[i]);
+ Slog.d(TAG, " user " + currentUserId + " => " + installed);
}
- ps.setInstalled(perUserInstalled[i], allUsers[i]);
+ 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.
- int userId = user.getIdentifier();
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 = newPackage.applicationInfo.uid;
res.pkg = newPackage;
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
mSettings.setInstallerPackageName(pkgName, installerPackageName);
- res.returnCode = PackageManager.INSTALL_SUCCEEDED;
+ 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 void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage");
+ installPackageLI(args, res);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
|| (args.volumeUuid != null));
+ final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
+ final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
+ final boolean forceSdk = ((installFlags & PackageManager.INSTALL_FORCE_SDK) != 0);
+ final boolean virtualPreload =
+ ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
boolean replace = false;
int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
if (args.move != null) {
- // moving a complete application; perfom an initial scan on the new install location
+ // 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;
+ }
+
// Result object to be returned
- res.returnCode = PackageManager.INSTALL_SUCCEEDED;
+ res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ res.installerPackageName = installerPackageName;
if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
+
+ // Sanity check
+ if (instantApp && (forwardLocked || onExternal)) {
+ Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked
+ + " external=" + onExternal);
+ res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+ return;
+ }
+
// Retrieve PackageSettings and parse package
final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
+ | PackageParser.PARSE_ENFORCE_CODE
| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
- | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
+ | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
+ | (instantApp ? PackageParser.PARSE_IS_EPHEMERAL : 0)
+ | (forceSdk ? PackageParser.PARSE_FORCE_SDK : 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);
} catch (PackageParserException e) {
res.setError("Failed parse during installPackageLI", e);
return;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ // Instant apps must have target SDK >= O and have targetSanboxVersion >= 2
+ if (instantApp && pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
+ Slog.w(TAG, "Instant app package " + pkg.packageName + " does not target O");
+ res.setError(INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE,
+ "Instant app package must target O");
+ return;
+ }
+ if (instantApp && pkg.applicationInfo.targetSandboxVersion != 2) {
+ Slog.w(TAG, "Instant app package " + pkg.packageName
+ + " does not target targetSandboxVersion 2");
+ res.setError(INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE,
+ "Instant app package must use targetSanboxVersion 2");
+ return;
+ }
+
+ 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.");
+ res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ "Packages declaring static-shared libs cannot be updated");
+ return;
+ }
+ }
+
+ // 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);
+ }
+ }
}
- // Mark that we have an install time CPU ABI override.
- pkg.cpuAbiOverride = args.abiOverride;
+ // 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) {
}
try {
- pp.collectCertificates(pkg, parseFlags);
- pp.collectManifestDigest(pkg);
+ // either use what we've been given or parse directly from the APK
+ if (args.certificates != null) {
+ try {
+ PackageParser.populateCertificates(pkg, args.certificates);
+ } catch (PackageParserException e) {
+ // there was something wrong with the certificates we were given;
+ // try to pull them from the APK
+ PackageParser.collectCertificates(pkg, parseFlags);
+ }
+ } else {
+ PackageParser.collectCertificates(pkg, parseFlags);
+ }
} catch (PackageParserException e) {
res.setError("Failed collect during installPackageLI", e);
return;
}
- /* If the installer passed in a manifest digest, compare it now. */
- if (args.manifestDigest != null) {
- if (DEBUG_INSTALL) {
- final String parsedManifest = pkg.manifestDigest == null ? "null"
- : pkg.manifestDigest.toString();
- Slog.d(TAG, "Comparing manifests: " + args.manifestDigest.toString() + " vs. "
- + parsedManifest);
- }
-
- if (!args.manifestDigest.equals(pkg.manifestDigest)) {
- res.setError(INSTALL_FAILED_PACKAGE_CHANGED, "Manifest digest changed");
- return;
- }
- } else if (DEBUG_INSTALL) {
- final String parsedManifest = pkg.manifestDigest == null
- ? "null" : pkg.manifestDigest.toString();
- Slog.d(TAG, "manifestDigest was not present, but parser got: " + parsedManifest);
- }
-
// Get rid of all references to package scan path via parser.
pp = null;
String oldCodePath = null;
synchronized (mPackages) {
// Check if installing already existing package
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
- String oldName = mSettings.mRenamedPackages.get(pkgName);
+ String oldName = mSettings.getRenamedPackageLPr(pkgName);
if (pkg.mOriginalPackages != null
&& pkg.mOriginalPackages.contains(oldName)
&& mPackages.containsKey(oldName)) {
if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);
}
- // Prevent apps opting out from runtime permissions
+ // Child packages are installed through the parent package
+ if (pkg.parentPackage != null) {
+ res.setError(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.");
+ return;
+ }
+
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;
+ " target SDK " + oldTargetSdk + " does.");
return;
}
+ // Prevent apps from downgrading their targetSandbox.
+ final int oldTargetSandbox = oldPackage.applicationInfo.targetSandboxVersion;
+ final int newTargetSandbox = pkg.applicationInfo.targetSandboxVersion;
+ if (oldTargetSandbox == 2 && newTargetSandbox != 2) {
+ res.setError(PackageManager.INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE,
+ "Package " + pkg.packageName + " new target sandbox "
+ + newTargetSandbox + " is incompatible with the previous value of"
+ + oldTargetSandbox + ".");
+ return;
+ }
+
+ // Prevent installing of child packages
+ if (oldPackage.parentPackage != null) {
+ res.setError(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.");
+ return;
+ }
}
}
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()) {
+ SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
+ if (libraryEntry != null) {
+ signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
+ }
+ }
+
// 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.
- if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {
- if (!checkUpgradeKeySetLP(ps, pkg)) {
+ if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) {
+ if (!checkUpgradeKeySetLP(signatureCheckPs, pkg)) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
}
} else {
try {
- verifySignaturesLP(ps, pkg);
+ verifySignaturesLP(signatureCheckPs, pkg);
} catch (PackageManagerException e) {
res.setError(e.error, e.getMessage());
return;
res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
- // Check whether the newly-scanned package wants to define an already-defined perm
int N = pkg.permissions.size();
for (int i = N-1; i >= 0; i--) {
PackageParser.Permission perm = pkg.permissions.get(i);
BasePermission bp = mSettings.mPermissions.get(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.
+ perm.info.name + "; ignoring new declaration");
pkg.permissions.remove(i);
}
- } else if (!"android".equals(pkg.packageName)) {
+ } 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
}
}
}
-
}
- if (systemApp && onExternal) {
- // Disable updates to system apps on sdcard
- res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
- "Cannot install updates to system apps on sdcard");
- return;
+ if (systemApp) {
+ if (onExternal) {
+ // Abort update; system app can't be replaced with app on sdcard
+ res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ "Cannot install updates to system apps on sdcard");
+ return;
+ } else if (instantApp) {
+ // Abort update; system app can't be replaced with an instant app
+ res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+ "Cannot update a system app with an instant app");
+ return;
+ }
}
if (args.move != null) {
scanFlags |= SCAN_NO_DEX;
try {
- derivePackageAbi(pkg, new File(pkg.codePath), args.abiOverride,
- true /* extract libs */);
+ String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?
+ args.abiOverride : pkg.cpuAbiOverride);
+ final boolean extractNativeLibs = !pkg.isLibrary();
+ derivePackageAbi(pkg, new File(pkg.codePath), abiOverride,
+ extractNativeLibs, mAppLib32InstallDir);
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
return;
}
- // Run dexopt before old package gets removed, to minimize time when app is unavailable
- int result = mPackageDexOptimizer
- .performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
- false /* defer */, false /* inclDependencies */,
- true /* boot complete */);
- if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
- return;
+ // Shared libraries for the package need to be updated.
+ synchronized (mPackages) {
+ try {
+ updateSharedLibrariesLPr(pkg, null);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
+ }
}
}
return;
}
+ // Verify if 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.
+ //
+ // We only need to dexopt if the package meets ALL of the following conditions:
+ // 1) it is not forward locked.
+ // 2) it is not on on an external ASEC container.
+ // 3) it is not an instant app or if it is then dexopt is enabled via gservices.
+ //
+ // 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 = !forwardLocked
+ && !pkg.applicationInfo.isExternalAsec()
+ && (!instantApp || Global.getInt(mContext.getContentResolver(),
+ Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0);
+
+ if (performDexopt) {
+ 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(pkg.packageName,
+ REASON_INSTALL,
+ DexoptOptions.DEXOPT_BOOT_COMPLETE);
+ mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
+ null /* instructionSets */,
+ getOrCreateCompilerPackageStats(pkg),
+ mDexManager.getPackageUseInfoOrDefault(pkg.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(pkg.packageName);
+
startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
- if (replace) {
- replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
- installerPackageName, volumeUuid, res);
- } else {
- installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
- args.user, installerPackageName, volumeUuid, res);
+ try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
+ "installPackageLI")) {
+ if (replace) {
+ 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.mVersionCode != pkg.mVersionCode) {
+ res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
+ + "static-shared libs cannot be updated");
+ return;
+ }
+ }
+ replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
+ installerPackageName, res, args.installReason);
+ } else {
+ installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
+ args.user, installerPackageName, volumeUuid, res, args.installReason);
+ }
}
+
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
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(pkgName);
}
}
}
final int verifierUid = getPackageUid(
mIntentFilterVerifierComponent.getPackageName(),
- (userId == UserHandle.USER_ALL) ? UserHandle.USER_OWNER : userId);
+ MATCH_DEBUG_TRIAGED_MISSING,
+ (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
- mHandler.removeMessages(START_INTENT_FILTER_VERIFICATIONS);
- final Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
+ 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,
}
}
- private static boolean isMultiArch(PackageSetting ps) {
- return (ps.pkgFlags & ApplicationInfo.FLAG_MULTIARCH) != 0;
- }
-
private static boolean isMultiArch(ApplicationInfo info) {
return (info.flags & ApplicationInfo.FLAG_MULTIARCH) != 0;
}
return (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
- private static boolean isExternal(ApplicationInfo info) {
- return (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
- }
-
private static boolean isSystemApp(PackageParser.Package pkg) {
return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
}
@Override
- public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, int userId,
- int flags) {
- deletePackage(packageName, new LegacyPackageDeleteObserver(observer).getBinder(), userId,
- flags);
+ 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 deletePackage(final String packageName,
- final IPackageDeleteObserver2 observer, final int userId, final int flags) {
+ 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);
- Preconditions.checkNotNull(packageName);
+ final boolean canViewInstantApps = canViewInstantApps(callingUid, userId);
+ Preconditions.checkNotNull(versionedPackage);
Preconditions.checkNotNull(observer);
+ Preconditions.checkArgumentInRange(versionedPackage.getVersionCode(),
+ PackageManager.VERSION_CODE_HIGHEST,
+ Integer.MAX_VALUE, "versionCode must be >= -1");
+
+ final String packageName = versionedPackage.getPackageName();
+ final int versionCode = versionedPackage.getVersionCode();
+ final String internalPackageName;
+ synchronized (mPackages) {
+ // Normalize package name to handle renamed packages and static libs
+ internalPackageName = resolveInternalPackageNameLPr(versionedPackage.getPackageName(),
+ versionedPackage.getVersionCode());
+ }
+
final int uid = Binder.getCallingUid();
- if (UserHandle.getUserId(uid) != userId) {
- mContext.enforceCallingPermission(
+ if (!isOrphaned(internalPackageName)
+ && !isCallerAllowedToSilentlyUninstall(uid, internalPackageName)) {
+ 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)) {
try {
observer.onPackageDeleted(packageName,
return;
}
- boolean uninstallBlocked = false;
- if ((flags & PackageManager.DELETE_ALL_USERS) != 0) {
- int[] users = sUserManager.getUserIds();
- for (int i = 0; i < users.length; ++i) {
- if (getBlockUninstallForUser(packageName, users[i])) {
- uninstallBlocked = true;
- break;
- }
- }
- } else {
- uninstallBlocked = getBlockUninstallForUser(packageName, userId);
- }
- if (uninstallBlocked) {
+ if (!deleteAllUsers && getBlockUninstallForUser(internalPackageName, userId)) {
try {
- observer.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_OWNER_BLOCKED,
- null);
+ observer.onPackageDeleted(packageName,
+ PackageManager.DELETE_FAILED_OWNER_BLOCKED, null);
} catch (RemoteException re) {
}
return;
}
if (DEBUG_REMOVE) {
- Slog.d(TAG, "deletePackageAsUser: pkg=" + packageName + " user=" + userId);
+ 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(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
- final int returnCode = deletePackageX(packageName, userId, flags);
- if (observer != null) {
- try {
- observer.onPackageDeleted(packageName, returnCode, null);
- } catch (RemoteException e) {
- Log.i(TAG, "Observer no longer exists.");
- } //end catch
- } //end if
+ 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 userId : users) {
+ if (!ArrayUtils.contains(blockUninstallUserIds, userId)) {
+ returnCode = deletePackageX(internalPackageName, versionCode,
+ userId, userFlags);
+ if (returnCode != PackageManager.DELETE_SUCCEEDED) {
+ Slog.w(TAG, "Package delete failed for user " + userId
+ + ", 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
} //end run
});
}
+ private String resolveExternalPackageNameLPr(PackageParser.Package pkg) {
+ if (pkg.staticSharedLibName != null) {
+ return pkg.manifestPackageName;
+ }
+ return pkg.packageName;
+ }
+
+ private String resolveInternalPackageNameLPr(String packageName, int versionCode) {
+ // Handle renamed packages
+ String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
+ packageName = normalizedPackageName != null ? normalizedPackageName : packageName;
+
+ // Is this a static library?
+ SparseArray<SharedLibraryEntry> versionedLib =
+ mStaticLibsByDeclaringPackage.get(packageName);
+ if (versionedLib == null || versionedLib.size() <= 0) {
+ return packageName;
+ }
+
+ // Figure out which lib versions the caller can see
+ SparseIntArray versionsCallerCanSee = null;
+ final int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
+ if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.SHELL_UID
+ && callingAppId != Process.ROOT_UID) {
+ versionsCallerCanSee = new SparseIntArray();
+ String libName = versionedLib.valueAt(0).info.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 int 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
+ SharedLibraryEntry highestVersion = null;
+ final int versionCount = versionedLib.size();
+ for (int i = 0; i < versionCount; i++) {
+ SharedLibraryEntry libEntry = versionedLib.valueAt(i);
+ if (versionsCallerCanSee != null && versionsCallerCanSee.indexOfKey(
+ libEntry.info.getVersion()) < 0) {
+ continue;
+ }
+ final int libVersionCode = libEntry.info.getDeclaringPackage().getVersionCode();
+ if (versionCode != PackageManager.VERSION_CODE_HIGHEST) {
+ if (libVersionCode == versionCode) {
+ return libEntry.apk;
+ }
+ } else if (highestVersion == null) {
+ highestVersion = libEntry;
+ } else if (libVersionCode > highestVersion.info
+ .getDeclaringPackage().getVersionCode()) {
+ highestVersion = libEntry;
+ }
+ }
+
+ if (highestVersion != null) {
+ return highestVersion.apk;
+ }
+
+ 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) {
- if (dpm.isDeviceOwner(packageName)) {
+ 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();
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 installed. For cleaning up a failed
+ * 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
* persisting settings for later use
* sending a broadcast if necessary
*/
- private int deletePackageX(String packageName, int userId, int flags) {
- final PackageRemovedInfo info = new PackageRemovedInfo();
+ int deletePackageX(String packageName, int versionCode, int userId, int deleteFlags) {
+ final PackageRemovedInfo info = new PackageRemovedInfo(this);
final boolean res;
- final UserHandle removeForUser = (flags & PackageManager.DELETE_ALL_USERS) != 0
- ? UserHandle.ALL : new UserHandle(userId);
+ final int removeUser = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0
+ ? UserHandle.USER_ALL : userId;
- if (isPackageDeviceAdmin(packageName, removeForUser.getIdentifier())) {
+ if (isPackageDeviceAdmin(packageName, removeUser)) {
Slog.w(TAG, "Not removing package " + packageName + ": has active device admin");
return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
}
- boolean removedForAllUsers = false;
- boolean systemUpdate = false;
+ PackageSetting uninstalledPs = null;
+ PackageParser.Package pkg = null;
// for the uninstall-updates case and restricted profiles, remember the per-
- // userhandle installed state
+ // user handle installed state
int[] allUsers;
- boolean[] perUserInstalled;
synchronized (mPackages) {
- PackageSetting ps = mSettings.mPackages.get(packageName);
+ uninstalledPs = mSettings.mPackages.get(packageName);
+ if (uninstalledPs == null) {
+ Slog.w(TAG, "Not removing non-existent package " + packageName);
+ return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
+ }
+
+ if (versionCode != PackageManager.VERSION_CODE_HIGHEST
+ && uninstalledPs.versionCode != versionCode) {
+ Slog.w(TAG, "Not removing package " + packageName + " with versionCode "
+ + uninstalledPs.versionCode + " != " + versionCode);
+ return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
+ }
+
+ // Static shared libs can be declared by any package, so let us not
+ // allow removing a package if it provides a lib others depend on.
+ pkg = mPackages.get(packageName);
+
allUsers = sUserManager.getUserIds();
- perUserInstalled = new boolean[allUsers.length];
- for (int i = 0; i < allUsers.length; i++) {
- perUserInstalled[i] = ps != null ? ps.getInstalled(allUsers[i]) : false;
+
+ if (pkg != null && pkg.staticSharedLibName != null) {
+ SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(pkg.staticSharedLibName,
+ pkg.staticSharedLibVersion);
+ if (libEntry != null) {
+ for (int currUserId : allUsers) {
+ if (removeUser != UserHandle.USER_ALL && removeUser != currUserId) {
+ continue;
+ }
+ List<VersionedPackage> libClientPackages = getPackagesUsingSharedLibraryLPr(
+ libEntry.info, 0, currUserId);
+ if (!ArrayUtils.isEmpty(libClientPackages)) {
+ Slog.w(TAG, "Not removing package " + pkg.manifestPackageName
+ + " hosting lib " + libEntry.info.getName() + " version "
+ + libEntry.info.getVersion() + " 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);
- res = deletePackageLI(packageName, removeForUser,
- true, allUsers, perUserInstalled,
- flags | REMOVE_CHATTY, info, true);
- systemUpdate = info.isRemovedPackageSystemUpdate;
- if (res && !systemUpdate && mPackages.get(packageName) == null) {
- removedForAllUsers = true;
+ try (PackageFreezer freezer = freezePackageForDelete(packageName, freezeUser,
+ deleteFlags, "deletePackageX")) {
+ res = deletePackageLIF(packageName, UserHandle.of(removeUser), true, allUsers,
+ deleteFlags | FLAGS_REMOVE_CHATTY, info, true, null);
+ }
+ synchronized (mPackages) {
+ if (res) {
+ if (pkg != null) {
+ mInstantAppRegistry.onPackageUninstalledLPw(pkg, info.removedUsers);
+ }
+ updateSequenceNumberLP(uninstalledPs, info.removedUsers);
+ updateInstantAppInstallerLocked(packageName);
+ }
}
- if (DEBUG_REMOVE) Slog.d(TAG, "delete res: systemUpdate=" + systemUpdate
- + " removedForAllUsers=" + removedForAllUsers);
}
if (res) {
- info.sendBroadcast(true, systemUpdate, removedForAllUsers);
-
- // If the removed package was a system update, the old system package
- // was re-enabled; we need to broadcast this information
- if (systemUpdate) {
- Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, info.removedAppId >= 0
- ? info.removedAppId : info.uid);
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
-
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, null, null, null);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, null, null, null);
- sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
- null, packageName, null, null);
- }
+ final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0;
+ info.sendPackageRemovedBroadcasts(killApp);
+ info.sendSystemPackageUpdatedBroadcasts();
+ info.sendSystemPackageAppearedBroadcasts();
}
// Force a gc here.
Runtime.getRuntime().gc();
return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;
}
- class PackageRemovedInfo {
+ static class PackageRemovedInfo {
+ final PackageSender packageSender;
String removedPackage;
+ String installerPackageName;
int uid = -1;
int removedAppId = -1;
+ int[] origUsers;
int[] removedUsers = null;
+ int[] broadcastUsers = 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;
- void sendBroadcast(boolean fullRemove, boolean replacing, boolean removedForAllUsers) {
- Bundle extras = new Bundle(1);
+ 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);
+ }
+ }
+
+ private void sendSystemPackageUpdatedBroadcastsInternal() {
+ Bundle extras = new Bundle(2);
extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
- extras.putBoolean(Intent.EXTRA_DATA_REMOVED, fullRemove);
- if (replacing) {
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
+ removedPackage, extras, 0, null /*targetPackage*/, null, null);
+ packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ removedPackage, extras, 0, null /*targetPackage*/, null, null);
+ packageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
+ null, null, 0, removedPackage, null, null);
+ if (installerPackageName != null) {
+ packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
+ removedPackage, extras, 0 /*flags*/,
+ installerPackageName, null, null);
+ packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ removedPackage, extras, 0 /*flags*/,
+ installerPackageName, 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);
+ extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
+ 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) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, null, null, removedUsers);
- if (fullRemove && !replacing) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage,
- extras, null, null, removedUsers);
+ packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
+ removedPackage, extras, 0, null /*targetPackage*/, null, broadcastUsers);
+ if (installerPackageName != null) {
+ packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
+ removedPackage, extras, 0 /*flags*/,
+ installerPackageName, null, broadcastUsers);
+ }
+ if (dataRemoved && !isRemovedPackageSystemUpdate) {
+ packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
+ removedPackage, extras,
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
+ null, null, broadcastUsers);
}
}
if (removedAppId >= 0) {
- sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null,
- removedUsers);
+ packageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED,
+ null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
+ null, null, broadcastUsers);
+ }
+ }
+
+ void populateUsers(int[] userIds, PackageSetting deletedPackageSetting) {
+ removedUsers = userIds;
+ if (removedUsers == null) {
+ broadcastUsers = null;
+ return;
+ }
+
+ broadcastUsers = EMPTY_INT_ARRAY;
+ for (int i = userIds.length - 1; i >= 0; --i) {
+ final int userId = userIds[i];
+ if (deletedPackageSetting.getInstantApp(userId)) {
+ continue;
+ }
+ broadcastUsers = ArrayUtils.appendInt(broadcastUsers, userId);
}
}
}
* make sure this flag is set for partially installed apps. If not its meaningless to
* delete a partially installed application.
*/
- private void removePackageDataLI(PackageSetting ps,
- int[] allUserHandles, boolean[] perUserInstalled,
+ private void removePackageDataLIF(PackageSetting ps, int[] allUserHandles,
PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
String packageName = ps.name;
if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + ps);
- removePackageLI(ps, (flags&REMOVE_CHATTY) != 0);
// Retrieve object to delete permissions for shared user later on
+ final PackageParser.Package deletedPkg;
final PackageSetting deletedPs;
// reader
synchronized (mPackages) {
+ deletedPkg = mPackages.get(packageName);
deletedPs = mSettings.mPackages.get(packageName);
if (outInfo != null) {
outInfo.removedPackage = packageName;
- outInfo.removedUsers = deletedPs != null
- ? deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true)
- : null;
+ outInfo.installerPackageName = ps.installerPackageName;
+ outInfo.isStaticSharedLib = deletedPkg != null
+ && deletedPkg.staticSharedLibName != null;
+ outInfo.populateUsers(deletedPs == null ? null
+ : deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true), deletedPs);
}
}
- if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
- removeDataDirsLI(ps.volumeUuid, packageName);
+
+ removePackageLI(ps, (flags & FLAGS_REMOVE_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(ps.name);
+ resolvedPkg.setVolumeUuid(ps.volumeUuid);
+ }
+ destroyAppDataLIF(resolvedPkg, UserHandle.USER_ALL,
+ StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+ destroyAppProfilesLIF(resolvedPkg, UserHandle.USER_ALL);
+ if (outInfo != null) {
+ outInfo.dataRemoved = true;
+ }
schedulePackageCleaning(packageName, UserHandle.USER_ALL, true);
}
+
+ int removedAppId = -1;
+
// writer
synchronized (mPackages) {
+ boolean installedStateChanged = false;
if (deletedPs != null) {
if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
clearDefaultBrowserIfNeeded(packageName);
+ mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
+ removedAppId = mSettings.removePackageLPw(packageName);
if (outInfo != null) {
- mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
- outInfo.removedAppId = mSettings.removePackageLPw(packageName);
+ outInfo.removedAppId = removedAppId;
}
updatePermissionsLPw(deletedPs.name, null, 0);
if (deletedPs.sharedUser != null) {
final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs,
userId);
if (userIdToKill == UserHandle.USER_ALL
- || userIdToKill >= UserHandle.USER_OWNER) {
+ || userIdToKill >= UserHandle.USER_SYSTEM) {
// If gids changed for this user, kill all affected packages.
mHandler.post(new Runnable() {
@Override
}
// 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 && perUserInstalled != null) {
+ if (allUserHandles != null && outInfo != null && outInfo.origUsers != null) {
if (DEBUG_REMOVE) {
Slog.d(TAG, "Propagating install state across downgrade");
}
- for (int i = 0; i < allUserHandles.length; i++) {
+ for (int userId : allUserHandles) {
+ final boolean installed = ArrayUtils.contains(outInfo.origUsers, userId);
if (DEBUG_REMOVE) {
- Slog.d(TAG, " user " + allUserHandles[i]
- + " => " + perUserInstalled[i]);
+ Slog.d(TAG, " user " + userId + " => " + installed);
+ }
+ if (installed != ps.getInstalled(userId)) {
+ installedStateChanged = true;
}
- ps.setInstalled(perUserInstalled[i], allUserHandles[i]);
+ ps.setInstalled(installed, userId);
}
}
}
// Save settings now
mSettings.writeLPr();
}
+ if (installedStateChanged) {
+ mSettings.writeKernelMappingLPr(ps);
+ }
}
- if (outInfo != null) {
+ if (removedAppId != -1) {
// A user ID was deleted here. Go through all users and remove it
// from KeyStore.
- removeKeystoreDataIfNeeded(UserHandle.USER_ALL, outInfo.removedAppId);
+ removeKeystoreDataIfNeeded(UserHandle.USER_ALL, removedAppId);
}
}
/*
* Tries to delete system package.
*/
- private boolean deleteSystemPackageLI(PackageSetting newPs,
- int[] allUserHandles, boolean[] perUserInstalled,
- int flags, PackageRemovedInfo outInfo, boolean writeSettings) {
+ private boolean deleteSystemPackageLIF(PackageParser.Package deletedPkg,
+ PackageSetting deletedPs, int[] allUserHandles, int flags, PackageRemovedInfo outInfo,
+ boolean writeSettings) {
+ if (deletedPs.parentPackageName != null) {
+ Slog.w(TAG, "Attempt to delete child system package " + deletedPkg.packageName);
+ return false;
+ }
+
final boolean applyUserRestrictions
- = (allUserHandles != null) && (perUserInstalled != null);
- PackageSetting disabledPs = null;
+ = (allUserHandles != null) && (outInfo.origUsers != null);
+ final PackageSetting disabledPs;
// 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
synchronized (mPackages) {
- disabledPs = mSettings.getDisabledSystemPkgLPr(newPs.name);
+ disabledPs = mSettings.getDisabledSystemPkgLPr(deletedPs.name);
}
- if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + newPs
+
+ if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.packageName
+ " disabledPs=" + disabledPs);
+
if (disabledPs == null) {
- Slog.w(TAG, "Attempt to delete unknown system package "+ newPs.name);
+ Slog.w(TAG, "Attempt to delete unknown system package "+ deletedPkg.packageName);
return false;
} else if (DEBUG_REMOVE) {
Slog.d(TAG, "Deleting system pkg from data partition");
}
+
if (DEBUG_REMOVE) {
if (applyUserRestrictions) {
Slog.d(TAG, "Remembering install states:");
- for (int i = 0; i < allUserHandles.length; i++) {
- Slog.d(TAG, " u=" + allUserHandles[i] + " inst=" + perUserInstalled[i]);
+ for (int userId : allUserHandles) {
+ final boolean finstalled = ArrayUtils.contains(outInfo.origUsers, userId);
+ Slog.d(TAG, " u=" + userId + " inst=" + finstalled);
}
}
}
+
// Delete the updated package
outInfo.isRemovedPackageSystemUpdate = true;
- if (disabledPs.versionCode < newPs.versionCode) {
+ 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;
}
- boolean ret = deleteInstalledPackageLI(newPs, true, flags,
- allUserHandles, perUserInstalled, outInfo, writeSettings);
+
+ boolean ret = deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
+ outInfo, writeSettings, disabledPs.pkg);
if (!ret) {
return false;
}
+
// 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
- mSettings.enableSystemPackageLPw(newPs.name);
+ enableSystemPackageLPw(disabledPs.pkg);
// Remove any native libraries from the upgraded package.
- NativeLibraryHelper.removeNativeBinariesLI(newPs.legacyNativeLibraryPathString);
+ removeNativeBinariesLI(deletedPs);
}
+
// Install the system package
if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
- int parseFlags = PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM;
- if (locationIsPrivileged(disabledPs.codePath)) {
+ try {
+ installPackageFromSystemLIF(disabledPs.codePath, false /*isPrivileged*/, allUserHandles,
+ outInfo.origUsers, deletedPs.getPermissionsState(), writeSettings);
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "Failed to restore system package:" + deletedPkg.packageName + ": "
+ + e.getMessage());
+ return false;
+ } finally {
+ if (disabledPs.pkg.isStub) {
+ mSettings.disableSystemPackageLPw(disabledPs.name, true /*replaced*/);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Installs a package that's already on the system partition.
+ */
+ private PackageParser.Package installPackageFromSystemLIF(@NonNull File codePath,
+ boolean isPrivileged, @Nullable int[] allUserHandles, @Nullable int[] origUserHandles,
+ @Nullable PermissionsState origPermissionState, boolean writeSettings)
+ throws PackageManagerException {
+ int parseFlags = mDefParseFlags
+ | PackageParser.PARSE_MUST_BE_APK
+ | PackageParser.PARSE_IS_SYSTEM
+ | PackageParser.PARSE_IS_SYSTEM_DIR;
+ if (isPrivileged || locationIsPrivileged(codePath)) {
parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
}
- final PackageParser.Package newPkg;
+ final PackageParser.Package newPkg =
+ scanPackageTracedLI(codePath, parseFlags, 0 /*scanFlags*/, 0 /*currentTime*/, null);
+
try {
- newPkg = scanPackageLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0, null);
+ // update shared libraries for the newly re-installed system package
+ updateSharedLibrariesLPr(newPkg, null);
} catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to restore system package:" + newPs.name + ": " + e.getMessage());
- return false;
+ Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
}
+ prepareAppDataAfterInstallLIF(newPkg);
+
// writer
synchronized (mPackages) {
PackageSetting ps = mSettings.mPackages.get(newPkg.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.
- ps.getPermissionsState().copyFrom(newPs.getPermissionsState());
+ if (origPermissionState != null) {
+ ps.getPermissionsState().copyFrom(origPermissionState);
+ }
updatePermissionsLPw(newPkg.packageName, newPkg,
UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
+ final boolean applyUserRestrictions
+ = (allUserHandles != null) && (origUserHandles != null);
if (applyUserRestrictions) {
+ boolean installedStateChanged = false;
if (DEBUG_REMOVE) {
Slog.d(TAG, "Propagating install state across reinstall");
}
- for (int i = 0; i < allUserHandles.length; i++) {
+ for (int userId : allUserHandles) {
+ final boolean installed = ArrayUtils.contains(origUserHandles, userId);
if (DEBUG_REMOVE) {
- Slog.d(TAG, " user " + allUserHandles[i]
- + " => " + perUserInstalled[i]);
+ Slog.d(TAG, " user " + userId + " => " + installed);
+ }
+ if (installed != ps.getInstalled(userId)) {
+ installedStateChanged = true;
}
- ps.setInstalled(perUserInstalled[i], allUserHandles[i]);
+ ps.setInstalled(installed, userId);
- mSettings.writeRuntimePermissionsForUserLPr(allUserHandles[i], false);
+ 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 true;
+ return newPkg;
}
- private boolean deleteInstalledPackageLI(PackageSetting ps,
- boolean deleteCodeAndResources, int flags,
- int[] allUserHandles, boolean[] perUserInstalled,
- PackageRemovedInfo outInfo, boolean writeSettings) {
- if (outInfo != null) {
- outInfo.uid = ps.appId;
+ private boolean 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);
+ if (childPs == null) {
+ return false;
+ }
+ 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
- removePackageDataLI(ps, allUserHandles, perUserInstalled, outInfo, flags, writeSettings);
+ removePackageDataLIF(ps, allUserHandles, outInfo, flags, writeSettings);
- // Delete application code and resources
- if (deleteCodeAndResources && (outInfo != null)) {
- outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
- ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
- if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
+ // 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(packageFlagsToInstallFlags(ps),
+ ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
+ if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
+ }
}
+
return true;
}
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_PACKAGES, null);
synchronized (mPackages) {
- PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
- Log.i(TAG, "Package doesn't exist in set block uninstall " + packageName);
- return false;
- }
- if (!ps.getInstalled(userId)) {
- // Can't block uninstall for an app that is not installed or enabled.
- Log.i(TAG, "Package not installed in set block uninstall " + packageName);
+ // 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;
}
- ps.setBlockUninstall(blockUninstall, userId);
+ 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.i(TAG, "Package doesn't exist in get block uninstall " + packageName);
+ Log.w(TAG, "Package doesn't exist: " + packageName);
return false;
}
- return ps.getBlockUninstall(userId);
+ 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;
}
/*
* This method handles package deletion in general
*/
- private boolean deletePackageLI(String packageName, UserHandle user,
- boolean deleteCodeAndResources, int[] allUserHandles, boolean[] perUserInstalled,
- int flags, PackageRemovedInfo outInfo,
- boolean writeSettings) {
+ private boolean deletePackageLIF(String packageName, UserHandle user,
+ boolean deleteCodeAndResources, int[] allUserHandles, int flags,
+ PackageRemovedInfo outInfo, boolean writeSettings,
+ PackageParser.Package replacingPackage) {
if (packageName == null) {
Slog.w(TAG, "Attempt to delete null packageName.");
return false;
}
+
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
+
PackageSetting ps;
- boolean dataOnly = false;
- int removeUser = -1;
- int appId = -1;
synchronized (mPackages) {
ps = mSettings.mPackages.get(packageName);
if (ps == null) {
Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
return false;
}
- if ((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
- && user.getIdentifier() != 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.
- if (DEBUG_REMOVE) Slog.d(TAG, "Only deleting for single user");
- final int userId = user.getIdentifier();
- ps.setUserState(userId,
- COMPONENT_ENABLED_STATE_DEFAULT,
- false, //installed
- true, //stopped
- true, //notLaunched
- false, //hidden
- null, null, null,
- false, // blockUninstall
- ps.readUserState(userId).domainVerificationStatus, 0);
- if (!isSystemApp(ps)) {
- if (ps.isAnyInstalled(sUserManager.getUserIds())) {
- // Other user 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");
- removeUser = user.getIdentifier();
- appId = ps.appId;
- scheduleWritePackageRestrictionsLocked(removeUser);
- } 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, user.getIdentifier());
- }
- } else {
- // This is a system app, so we assume that the
- // other users still have this package installed, so all
+
+ if (ps.parentPackageName != null && (!isSystemApp(ps)
+ || (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;
+ if (!clearPackageStateForUserLIF(ps, removedUserId, outInfo)) {
+ return false;
+ }
+ markPackageUninstalledForUserLPw(ps, user);
+ scheduleWritePackageRestrictionsLocked(user);
+ return true;
+ }
+ }
+
+ if (((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
+ && user.getIdentifier() != 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.
+ markPackageUninstalledForUserLPw(ps, user);
+
+ if (!isSystemApp(ps)) {
+ // Do not uninstall the APK if an app should be cached
+ boolean keepUninstalledPackage = shouldKeepUninstalledPackageLPr(packageName);
+ if (ps.isAnyInstalled(sUserManager.getUserIds()) || keepUninstalledPackage) {
+ // Other user 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");
- removeUser = user.getIdentifier();
- appId = ps.appId;
- scheduleWritePackageRestrictionsLocked(removeUser);
+ if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
+ if (!clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo)) {
+ return false;
+ }
+ scheduleWritePackageRestrictionsLocked(user);
+ return 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, user.getIdentifier());
+ mSettings.writeKernelMappingLPr(ps);
+ }
+ } else {
+ // This is a system app, so we assume that the
+ // other users still have this package installed, so all
+ // we need to do is clear this user's data and save that
+ // it is uninstalled.
+ if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
+ if (!clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo)) {
+ return false;
}
+ scheduleWritePackageRestrictionsLocked(user);
+ return true;
}
}
- if (removeUser >= 0) {
- // From above, we determined that we are deleting this only
- // for a single user. Continue the work here.
- if (DEBUG_REMOVE) Slog.d(TAG, "Updating install state for user: " + removeUser);
- if (outInfo != null) {
- outInfo.removedPackage = packageName;
- outInfo.removedAppId = appId;
- outInfo.removedUsers = new int[] {removeUser};
- }
- mInstaller.clearUserData(ps.volumeUuid, packageName, removeUser);
- removeKeystoreDataIfNeeded(removeUser, appId);
- schedulePackageCleaning(packageName, removeUser, false);
+ // 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) {
- if (clearPackagePreferredActivitiesLPw(packageName, removeUser)) {
- scheduleWritePackageRestrictionsLocked(removeUser);
+ 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);
+ }
}
- resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, removeUser);
}
- return true;
- }
-
- if (dataOnly) {
- // Delete application data first
- if (DEBUG_REMOVE) Slog.d(TAG, "Removing package data only");
- removePackageDataLI(ps, null, null, outInfo, flags, writeSettings);
- return true;
}
boolean ret = false;
if (isSystemApp(ps)) {
- 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
- ret = deleteSystemPackageLI(ps, allUserHandles, perUserInstalled,
- flags, outInfo, writeSettings);
+ 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
+ ret = deleteSystemPackageLIF(ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings);
} else {
- if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package:" + ps.name);
- // Kill application pre-emptively especially for apps on sd.
- killApplication(packageName, ps.appId, "uninstall pkg");
- ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags,
- allUserHandles, perUserInstalled,
- outInfo, writeSettings);
+ if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
+ ret = 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 (isSystemApp(ps)) {
+ 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);
+ }
+ }
+ }
+ }
}
return ret;
}
+ 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*/,
+ false /*suspended*/,
+ false /*instantApp*/,
+ false /*virtualPreload*/,
+ null /*lastDisableAppCaller*/,
+ null /*enabledComponents*/,
+ null /*disabledComponents*/,
+ ps.readUserState(nextUserId).domainVerificationStatus,
+ 0, PackageManager.INSTALL_REASON_UNKNOWN);
+ }
+ mSettings.writeKernelMappingLPr(ps);
+ }
+
+ private boolean clearPackageStateForUserLIF(PackageSetting ps, int userId,
+ PackageRemovedInfo outInfo) {
+ final PackageParser.Package pkg;
+ synchronized (mPackages) {
+ pkg = mPackages.get(ps.name);
+ }
+
+ final int[] userIds = (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds()
+ : new int[] {userId};
+ for (int nextUserId : userIds) {
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, "Updating package:" + ps.name + " install state for user:"
+ + nextUserId);
+ }
+
+ destroyAppDataLIF(pkg, userId,
+ StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+ destroyAppProfilesLIF(pkg, userId);
+ clearDefaultBrowserIfNeededForUser(ps.name, userId);
+ removeKeystoreDataIfNeeded(nextUserId, ps.appId);
+ schedulePackageCleaning(ps.name, nextUserId, false);
+ synchronized (mPackages) {
+ if (clearPackagePreferredActivitiesLPw(ps.name, nextUserId)) {
+ scheduleWritePackageRestrictionsLocked(nextUserId);
+ }
+ resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, nextUserId);
+ }
+ }
+
+ 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;
+ }
+
+ return true;
+ }
+
private final class ClearStorageConnection implements ServiceConnection {
IMediaContainerService mContainerService;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (this) {
- mContainerService = IMediaContainerService.Stub.asInterface(service);
+ mContainerService = IMediaContainerService.Stub
+ .asInterface(Binder.allowBlocking(service));
notifyAll();
}
}
}
private void clearExternalStorageDataSync(String packageName, int userId, boolean allData) {
+ if (DEFAULT_CONTAINER_PACKAGE.equals(packageName)) return;
+
final boolean mounted;
if (Environment.isExternalStorageEmulated()) {
mounted = true;
}
final ClearStorageConnection conn = new ClearStorageConnection();
if (mContext.bindServiceAsUser(
- containerIntent, conn, Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
+ containerIntent, conn, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
try {
for (int curUser : users) {
long timeout = SystemClock.uptimeMillis() + 5000;
synchronized (conn) {
- long now = SystemClock.uptimeMillis();
- while (conn.mContainerService == null && now < timeout) {
+ long now;
+ while (conn.mContainerService == null &&
+ (now = SystemClock.uptimeMillis()) < timeout) {
try {
conn.wait(timeout - now);
} catch (InterruptedException e) {
}
@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);
- enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "clear application data");
+
+ final int callingUid = Binder.getCallingUid();
+ 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;
- synchronized (mInstallLock) {
- succeeded = clearApplicationUserDataLI(packageName, userId);
- }
- clearExternalStorageDataSync(packageName, userId, true);
- if (succeeded) {
- // invoke DeviceStorageMonitor's update method to clear any notifications
- DeviceStorageMonitorInternal
- dsm = LocalServices.getService(DeviceStorageMonitorInternal.class);
- if (dsm != null) {
- dsm.checkMemory();
+ if (!filterApp) {
+ try (PackageFreezer freezer = freezePackage(packageName,
+ "clearApplicationUserData")) {
+ synchronized (mInstallLock) {
+ succeeded = clearApplicationUserDataLIF(packageName, userId);
+ }
+ clearExternalStorageDataSync(packageName, userId, true);
+ 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();
+ }
}
+ } else {
+ succeeded = false;
}
- if(observer != null) {
+ if (observer != null) {
try {
observer.onRemoveCompleted(packageName, succeeded);
} catch (RemoteException e) {
});
}
- private boolean clearApplicationUserDataLI(String packageName, int userId) {
+ private boolean clearApplicationUserDataLIF(String packageName, int userId) {
if (packageName == null) {
Slog.w(TAG, "Attempt to delete null packageName.");
return false;
resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, userId);
}
- // Always delete data directories for package, even if we found no other
- // record of app. This helps users recover from UID mismatches without
- // resorting to a full data wipe.
- int retCode = mInstaller.clearUserData(pkg.volumeUuid, packageName, userId);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't remove cache files for package: " + packageName);
- return false;
- }
+ clearAppDataLIF(pkg, userId,
+ StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
- final int appId = pkg.applicationInfo.uid;
+ final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
removeKeystoreDataIfNeeded(userId, appId);
- // 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 (pkg.applicationInfo.primaryCpuAbi != null &&
- !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
- final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
- if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
- nativeLibPath, userId) < 0) {
- Slog.w(TAG, "Failed linking native library dir");
- return false;
- }
+ 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;
}
}
}
+ private void resetNetworkPolicies(int userId) {
+ LocalServices.getService(NetworkPolicyManagerInternal.class).resetUserState(userId);
+ }
+
/**
* Reverts user permission state changes (permissions and flags).
*
return;
}
- final int userSettableFlags = FLAG_PERMISSION_USER_SET
+ // 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_REVOKE_ON_UPGRADE
+ | FLAG_PERMISSION_REVIEW_REQUIRED;
final int policyOrSystemFlags = FLAG_PERMISSION_SYSTEM_FIXED
| FLAG_PERMISSION_POLICY_FIXED;
// Always clear the user settable flags.
final boolean hasInstallState = permissionsState.getInstallPermissionState(
bp.name) != null;
- if (permissionsState.updatePermissionFlags(bp, userId, userSettableFlags, 0)) {
+ // 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 (mPermissionReviewRequired
+ && ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+ flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+ }
+ if (permissionsState.updatePermissionFlags(bp, userId, userSettableMask, flags)) {
if (hasInstallState) {
writeInstallPermissions = true;
} else {
!= PERMISSION_OPERATION_FAILURE) {
writeRuntimePermissions = true;
}
- } else {
+ // If permission review is enabled the permissions for a legacy apps
+ // are represented as constantly granted runtime ones, so don't revoke.
+ } else if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
// Otherwise, reset the permission.
final int revokeResult = permissionsState.revokeRuntimePermission(bp, userId);
switch (revokeResult) {
- case PERMISSION_OPERATION_SUCCESS: {
- writeRuntimePermissions = true;
- } break;
-
+ case PERMISSION_OPERATION_SUCCESS:
case PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
writeRuntimePermissions = true;
final int appId = ps.appId;
mHandler.post(new Runnable() {
@Override
public void run() {
- killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
+ killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
}
});
} break;
@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();
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_CACHE_FILES, null);
+ 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.
- final int userId = UserHandle.getCallingUserId();
mHandler.post(new Runnable() {
public void run() {
- mHandler.removeCallbacks(this);
- final boolean succeded;
- synchronized (mInstallLock) {
- succeded = deleteApplicationCacheFilesLI(packageName, userId);
+ final PackageSetting ps = pkg == null ? null : (PackageSetting) pkg.mExtras;
+ boolean doClearData = true;
+ if (ps != null) {
+ final boolean targetIsInstantApp =
+ ps.getInstantApp(UserHandle.getUserId(callingUid));
+ doClearData = !targetIsInstantApp
+ || hasAccessInstantApps == PackageManager.PERMISSION_GRANTED;
+ }
+ if (doClearData) {
+ synchronized (mInstallLock) {
+ final int flags = StorageManager.FLAG_STORAGE_DE
+ | StorageManager.FLAG_STORAGE_CE;
+ // We're only clearing cache files, so we don't care if the
+ // app is unfrozen and still able to run
+ clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY);
+ clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ }
+ clearExternalStorageDataSync(packageName, userId, false);
}
- clearExternalStorageDataSync(packageName, userId, false);
if (observer != null) {
try {
- observer.onRemoveCompleted(packageName, succeded);
+ observer.onRemoveCompleted(packageName, true);
} catch (RemoteException e) {
Log.i(TAG, "Observer no longer exists.");
}
- } //end if observer
- } //end run
+ }
+ }
});
}
- private boolean deleteApplicationCacheFilesLI(String packageName, int userId) {
- if (packageName == null) {
- Slog.w(TAG, "Attempt to delete null packageName.");
- return false;
- }
- PackageParser.Package p;
+ @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!");
+ }
+
+ private boolean getPackageSizeInfoLI(String packageName, int userId, PackageStats stats) {
+ final PackageSetting ps;
synchronized (mPackages) {
- p = mPackages.get(packageName);
- }
- if (p == null) {
- Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
- return false;
- }
- final ApplicationInfo applicationInfo = p.applicationInfo;
- if (applicationInfo == null) {
- Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
- return false;
- }
- int retCode = mInstaller.deleteCacheFiles(p.volumeUuid, packageName, userId);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't remove cache files for package: "
- + packageName + " u" + userId);
- return false;
- }
- return true;
- }
-
- @Override
- public void getPackageSizeInfo(final String packageName, int userHandle,
- final IPackageStatsObserver observer) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.GET_PACKAGE_SIZE, null);
- if (packageName == null) {
- throw new IllegalArgumentException("Attempt to get size of null packageName");
+ ps = mSettings.mPackages.get(packageName);
+ if (ps == null) {
+ Slog.w(TAG, "Failed to find settings for " + packageName);
+ return false;
+ }
}
- PackageStats stats = new PackageStats(packageName, userHandle);
+ final String[] packageNames = { packageName };
+ final long[] ceDataInodes = { ps.getCeDataInode(userId) };
+ final String[] codePaths = { ps.codePathString };
- /*
- * Queue up an async operation since the package measurement may take a
- * little while.
- */
- Message msg = mHandler.obtainMessage(INIT_COPY);
- msg.obj = new MeasureParams(stats, observer);
- mHandler.sendMessage(msg);
- }
+ try {
+ mInstaller.getAppSize(ps.volumeUuid, packageNames, userId, 0,
+ ps.appId, ceDataInodes, codePaths, stats);
- private boolean getPackageSizeInfoLI(String packageName, int userHandle,
- PackageStats pStats) {
- if (packageName == null) {
- Slog.w(TAG, "Attempt to get size of null packageName.");
- return false;
- }
- PackageParser.Package p;
- boolean dataOnly = false;
- String libDirRoot = null;
- String asecPath = null;
- PackageSetting ps = null;
- synchronized (mPackages) {
- p = mPackages.get(packageName);
- ps = mSettings.mPackages.get(packageName);
- if(p == null) {
- dataOnly = true;
- if((ps == null) || (ps.pkg == null)) {
- Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
- return false;
- }
- p = ps.pkg;
- }
- if (ps != null) {
- libDirRoot = ps.legacyNativeLibraryPathString;
- }
- if (p != null && (p.isForwardLocked() || p.applicationInfo.isExternalAsec())) {
- final long token = Binder.clearCallingIdentity();
- try {
- String secureContainerId = cidFromCodePath(p.applicationInfo.getBaseCodePath());
- if (secureContainerId != null) {
- asecPath = PackageHelper.getSdFilesystem(secureContainerId);
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
- String publicSrcDir = null;
- if(!dataOnly) {
- final ApplicationInfo applicationInfo = p.applicationInfo;
- if (applicationInfo == null) {
- Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
- return false;
+ // For now, ignore code size of packages on system partition
+ if (isSystemApp(ps) && !isUpdatedSystemApp(ps)) {
+ stats.codeSize = 0;
}
- if (p.isForwardLocked()) {
- publicSrcDir = applicationInfo.getBaseResourcePath();
- }
- }
- // TODO: extend to measure size of split APKs
- // TODO(multiArch): Extend getSizeInfo to look at the full subdirectory tree,
- // not just the first level.
- // TODO(multiArch): Extend getSizeInfo to look at *all* instruction sets, not
- // just the primary.
- String[] dexCodeInstructionSets = getDexCodeInstructionSets(getAppDexInstructionSets(ps));
- String apkPath;
- File packageDir = new File(p.codePath);
-
- if (packageDir.isDirectory() && p.canHaveOatDir()) {
- apkPath = packageDir.getAbsolutePath();
- // If libDirRoot is inside a package dir, set it to null to avoid it being counted twice
- if (libDirRoot != null && libDirRoot.startsWith(apkPath)) {
- libDirRoot = null;
- }
- } else {
- apkPath = p.baseCodePath;
- }
+ // External clients expect these to be tracked separately
+ stats.dataSize -= stats.cacheSize;
- int res = mInstaller.getSizeInfo(p.volumeUuid, packageName, userHandle, apkPath,
- libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats);
- if (res < 0) {
+ } catch (InstallerException e) {
+ Slog.w(TAG, String.valueOf(e));
return false;
}
- // Fix-up for forward-locked applications in ASEC containers.
- if (!isExternal(p)) {
- pStats.codeSize += pStats.externalCodeSize;
- pStats.externalCodeSize = 0L;
- }
-
return true;
}
-
- @Override
- public void addPackageToPreferred(String packageName) {
- Slog.w(TAG, "addPackageToPreferred: this is now a no-op");
- }
-
- @Override
- public void removePackageFromPreferred(String packageName) {
- Slog.w(TAG, "removePackageFromPreferred: this is now a no-op");
- }
-
- @Override
- public List<PackageInfo> getPreferredPackages(int flags) {
- return new ArrayList<PackageInfo>();
- }
-
private int getUidTargetSdkVersionLockedLPr(int uid) {
Object obj = mSettings.getUserIdLPr(uid);
if (obj instanceof SharedUserSetting) {
String opname) {
// writer
int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId, true, false, "add preferred activity");
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, false /* checkShell */, "add preferred activity");
if (filter.countActions() == 0) {
Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
return;
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
scheduleWritePackageRestrictionsLocked(userId);
+ postPreferredActivityChangedBroadcast(userId);
}
}
+ private void postPreferredActivityChangedBroadcast(int userId) {
+ mHandler.post(() -> {
+ final IActivityManager am = ActivityManager.getService();
+ if (am == null) {
+ return;
+ }
+
+ final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ try {
+ am.broadcastIntent(null, intent, null, null,
+ 0, null, null, null, android.app.AppOpsManager.OP_NONE,
+ null, false, false, userId);
+ } catch (RemoteException e) {
+ }
+ });
+ }
+
@Override
public void replacePreferredActivity(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity, int userId) {
}
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId, true, false, "replace preferred activity");
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, false /* checkShell */,
+ "replace preferred activity");
synchronized (mPackages) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
@Override
public void clearPackagePreferredActivities(String packageName) {
- final int uid = Binder.getCallingUid();
+ final int callingUid = Binder.getCallingUid();
+ if (getInstantAppPackageName(callingUid) != null) {
+ return;
+ }
// writer
synchronized (mPackages) {
PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null || pkg.applicationInfo.uid != uid) {
+ if (pkg == null || pkg.applicationInfo.uid != callingUid) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
- if (getUidTargetSdkVersionLockedLPr(Binder.getCallingUid())
+ if (getUidTargetSdkVersionLockedLPr(callingUid)
< Build.VERSION_CODES.FROYO) {
Slog.w(TAG, "Ignoring clearPackagePreferredActivities() from uid "
- + Binder.getCallingUid());
+ + callingUid);
return;
}
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
}
}
-
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
+ if (ps != null
+ && filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
+ return;
+ }
int user = UserHandle.getCallingUserId();
if (clearPackagePreferredActivitiesLPw(packageName, user)) {
scheduleWritePackageRestrictionsLocked(user);
changed = true;
}
}
+ if (changed) {
+ postPreferredActivityChangedBroadcast(userId);
+ }
return changed;
}
}
}
+ /** Clears state for all users, and touches intent filter verification policy */
void clearDefaultBrowserIfNeeded(String packageName) {
for (int oneUserId : sUserManager.getUserIds()) {
- String defaultBrowserPackageName = getDefaultBrowserPackageName(oneUserId);
- if (TextUtils.isEmpty(defaultBrowserPackageName)) continue;
+ 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, oneUserId);
+ setDefaultBrowserPackageName(null, userId);
}
}
}
public void resetApplicationPreferences(int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
+ final long identity = Binder.clearCallingIdentity();
// writer
- synchronized (mPackages) {
- final long identity = Binder.clearCallingIdentity();
- try {
+ try {
+ synchronized (mPackages) {
clearPackagePreferredActivitiesLPw(null, userId);
mSettings.applyDefaultPreferredAppsLPw(this, userId);
// TODO: We have to reset the default SMS and Phone. This requires
primeDomainVerificationsLPw(userId);
resetUserChangesToRuntimePermissionsAndFlagsLPw(userId);
scheduleWritePackageRestrictionsLocked(userId);
- } finally {
- Binder.restoreCallingIdentity(identity);
}
+ resetNetworkPolicies(userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
@Override
public int getPreferredActivities(List<IntentFilter> outFilters,
List<ComponentName> outActivities, String packageName) {
-
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return 0;
+ }
int num = 0;
final int userId = UserHandle.getCallingUserId();
// reader
}
synchronized (mPackages) {
Slog.i(TAG, "Adding persistent preferred activity " + activity + " for user " + userId +
- " :");
+ ":");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
new PersistentPreferredActivity(filter, activity));
scheduleWritePackageRestrictionsLocked(userId);
+ postPreferredActivityChangedBroadcast(userId);
}
}
if (changed) {
scheduleWritePackageRestrictionsLocked(userId);
+ postPreferredActivityChangedBroadcast(userId);
}
}
}
}
return;
}
-
+Slog.v(TAG, ":: restoreFromXml() : got to tag " + parser.getName());
// this is supposed to be TAG_PREFERRED_BACKUP
if (!expectedStartTag.equals(parser.getName())) {
if (DEBUG_BACKUP) {
// skip interfering stuff, then we're aligned with the backing implementation
while ((type = parser.next()) == XmlPullParser.TEXT) { }
+Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
functor.apply(parser, userId);
}
}
@Override
+ public byte[] getPermissionGrantBackup(int userId) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the system may call getPermissionGrantBackup()");
+ }
+
+ 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_PERMISSION_BACKUP);
+
+ synchronized (mPackages) {
+ serializeRuntimePermissionGrantsLPr(serializer, userId);
+ }
+
+ serializer.endTag(null, TAG_PERMISSION_BACKUP);
+ 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 restorePermissionGrants(byte[] backup, int userId) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the system may call restorePermissionGrants()");
+ }
+
+ try {
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
+ restoreFromXml(parser, userId, TAG_PERMISSION_BACKUP,
+ new BlobXmlRestorer() {
+ @Override
+ public void apply(XmlPullParser parser, int userId)
+ throws XmlPullParserException, IOException {
+ synchronized (mPackages) {
+ processRestoredPermissionGrantsLPr(parser, userId);
+ }
+ }
+ } );
+ } catch (Exception e) {
+ if (DEBUG_BACKUP) {
+ Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
+ }
+ }
+ }
+
+ private void serializeRuntimePermissionGrantsLPr(XmlSerializer serializer, final int userId)
+ throws IOException {
+ serializer.startTag(null, TAG_ALL_GRANTS);
+
+ final int N = mSettings.mPackages.size();
+ for (int i = 0; i < N; i++) {
+ final PackageSetting ps = mSettings.mPackages.valueAt(i);
+ boolean pkgGrantsKnown = false;
+
+ PermissionsState packagePerms = ps.getPermissionsState();
+
+ for (PermissionState state : packagePerms.getRuntimePermissionStates(userId)) {
+ final int grantFlags = state.getFlags();
+ // only look at grants that are not system/policy fixed
+ if ((grantFlags & SYSTEM_RUNTIME_GRANT_MASK) == 0) {
+ final boolean isGranted = state.isGranted();
+ // And only back up the user-twiddled state bits
+ if (isGranted || (grantFlags & USER_RUNTIME_GRANT_MASK) != 0) {
+ final String packageName = mSettings.mPackages.keyAt(i);
+ if (!pkgGrantsKnown) {
+ serializer.startTag(null, TAG_GRANT);
+ serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
+ pkgGrantsKnown = true;
+ }
+
+ final boolean userSet =
+ (grantFlags & FLAG_PERMISSION_USER_SET) != 0;
+ final boolean userFixed =
+ (grantFlags & FLAG_PERMISSION_USER_FIXED) != 0;
+ final boolean revoke =
+ (grantFlags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0;
+
+ serializer.startTag(null, TAG_PERMISSION);
+ serializer.attribute(null, ATTR_PERMISSION_NAME, state.getName());
+ if (isGranted) {
+ serializer.attribute(null, ATTR_IS_GRANTED, "true");
+ }
+ if (userSet) {
+ serializer.attribute(null, ATTR_USER_SET, "true");
+ }
+ if (userFixed) {
+ serializer.attribute(null, ATTR_USER_FIXED, "true");
+ }
+ if (revoke) {
+ serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true");
+ }
+ serializer.endTag(null, TAG_PERMISSION);
+ }
+ }
+ }
+
+ if (pkgGrantsKnown) {
+ serializer.endTag(null, TAG_GRANT);
+ }
+ }
+
+ serializer.endTag(null, TAG_ALL_GRANTS);
+ }
+
+ private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId)
+ throws XmlPullParserException, IOException {
+ String pkgName = null;
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ final String tagName = parser.getName();
+ if (tagName.equals(TAG_GRANT)) {
+ pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+ if (DEBUG_BACKUP) {
+ Slog.v(TAG, "+++ Restoring grants for package " + pkgName);
+ }
+ } else if (tagName.equals(TAG_PERMISSION)) {
+
+ final boolean isGranted = "true".equals(parser.getAttributeValue(null, ATTR_IS_GRANTED));
+ final String permName = parser.getAttributeValue(null, ATTR_PERMISSION_NAME);
+
+ int newFlagSet = 0;
+ if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) {
+ newFlagSet |= FLAG_PERMISSION_USER_SET;
+ }
+ if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) {
+ newFlagSet |= FLAG_PERMISSION_USER_FIXED;
+ }
+ if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) {
+ newFlagSet |= FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ }
+ if (DEBUG_BACKUP) {
+ Slog.v(TAG, " + Restoring grant: pkg=" + pkgName + " perm=" + permName
+ + " granted=" + isGranted + " bits=0x" + Integer.toHexString(newFlagSet));
+ }
+ final PackageSetting ps = mSettings.mPackages.get(pkgName);
+ if (ps != null) {
+ // Already installed so we apply the grant immediately
+ if (DEBUG_BACKUP) {
+ Slog.v(TAG, " + already installed; applying");
+ }
+ PermissionsState perms = ps.getPermissionsState();
+ BasePermission bp = mSettings.mPermissions.get(permName);
+ if (bp != null) {
+ if (isGranted) {
+ perms.grantRuntimePermission(bp, userId);
+ }
+ if (newFlagSet != 0) {
+ perms.updatePermissionFlags(bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet);
+ }
+ }
+ } else {
+ // Need to wait for post-restore install to apply the grant
+ if (DEBUG_BACKUP) {
+ Slog.v(TAG, " - not yet installed; saving for later");
+ }
+ mSettings.processRestoredPermissionGrantLPr(pkgName, permName,
+ isGranted, newFlagSet, userId);
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <" + TAG_PERMISSION_BACKUP + ">: " + tagName);
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ scheduleWriteSettingsLocked();
+ mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+ }
+
+ @Override
public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage,
int sourceUserId, int targetUserId, int flags) {
mContext.enforceCallingOrSelfPermission(
public void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
- int callingUid = Binder.getCallingUid();
+ final int callingUid = Binder.getCallingUid();
enforceOwnerRights(ownerPackage, callingUid);
enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
synchronized (mPackages) {
if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
return;
}
- int callingUserId = UserHandle.getUserId(callingUid);
+ final int callingUserId = UserHandle.getUserId(callingUid);
PackageInfo pi = getPackageInfo(pkg, 0, callingUserId);
if (pi == null) {
throw new IllegalArgumentException("Unknown package " + pkg + " on user "
@Override
public ComponentName getHomeActivities(List<ResolveInfo> allHomeCandidates) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return null;
+ }
+ return getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getCallingUserId());
+ }
+
+ 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));
+ }
+ }
+ }
+
+ /**
+ * 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;
+ }
- final int callingUserId = UserHandle.getCallingUserId();
- List<ResolveInfo> list = queryIntentActivities(intent, null,
- PackageManager.GET_META_DATA, callingUserId);
+ ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
+ int userId) {
+ Intent intent = getHomeIntent();
+ List<ResolveInfo> list = queryIntentActivitiesInternal(intent, null,
+ PackageManager.GET_META_DATA, userId);
ResolveInfo preferred = findPreferredActivity(intent, null, 0, list, 0,
- true, false, false, callingUserId);
+ true, false, false, userId);
allHomeCandidates.clear();
if (list != null) {
}
@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 void setApplicationEnabledSetting(String appPackageName,
int newState, int flags, int userId, String callingPackage) {
if (!sUserManager.exists(userId)) return;
}
@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;
+ newState);
}
PackageSetting pkgSetting;
- final int uid = Binder.getCallingUid();
- final int permission = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
- enforceCrossUserPermission(uid, userId, false, true, "set enabled");
+ 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);
+ }
+ 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;
int packageUid = -1;
ArrayList<String> components;
- // writer
+ // reader
synchronized (mPackages) {
pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null) {
- if (className == null) {
+ if (!isCallerInstantApp) {
+ if (className == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
throw new IllegalArgumentException(
- "Unknown package: " + packageName);
+ "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));
}
- throw new IllegalArgumentException(
- "Unknown component: " + packageName
- + "/" + className);
}
- // Allow root and verify that userId is not being specified by a different user
- if (!allowedByPermission && !UserHandle.isSameApp(uid, pkgSetting.appId)) {
+ }
+
+ // 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(
- "Permission Denial: attempt to change component state from pid="
- + Binder.getCallingPid()
- + ", uid=" + uid + ", package uid=" + pkgSetting.appId);
+ "Attempt to change component state; "
+ + "pid=" + Binder.getCallingPid()
+ + ", uid=" + callingUid
+ + (className == null
+ ? ", package=" + packageName
+ : ", component=" + packageName + "/" + className));
}
- if (className == null) {
- // We're dealing with an application/package level state change
- if (pkgSetting.getEnabled(userId) == newState) {
- // Nothing to do
- return;
- }
- if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
- || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
- // Don't care about who enables an app.
- callingPackage = null;
- }
- pkgSetting.setEnabled(newState, userId, callingPackage);
- // pkgSetting.pkg.mSetEnabled = newState;
- } else {
+ // Don't allow changing protected packages.
+ if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ throw new SecurityException("Cannot disable a protected package: " + packageName);
+ }
+ }
+
+ 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.isSystemApp();
+ if (isSystemStub
+ && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
+ final File codePath = decompressPackage(deletedPkg);
+ if (codePath == null) {
+ Slog.e(TAG, "couldn't decompress pkg: " + pkgSetting.name);
+ return;
+ }
+ // TODO remove direct parsing of the package object during internal cleanup
+ // of scan package
+ // We need to call parse directly here for no other reason than we need
+ // the new package in order to disable the old one [we use the information
+ // for some internal optimization to optionally create a new package setting
+ // object on replace]. However, we can't get the package from the scan
+ // because the scan modifies live structures and we need to remove the
+ // old [system] package from the system before a scan can be attempted.
+ // Once scan is indempotent we can remove this parse and use the package
+ // object we scanned, prior to adding it to package settings.
+ final PackageParser pp = new PackageParser();
+ pp.setSeparateProcesses(mSeparateProcesses);
+ pp.setDisplayMetrics(mMetrics);
+ pp.setCallback(mPackageParserCallback);
+ final PackageParser.Package tmpPkg;
+ try {
+ final int parseFlags = mDefParseFlags
+ | PackageParser.PARSE_MUST_BE_APK
+ | PackageParser.PARSE_IS_SYSTEM
+ | PackageParser.PARSE_IS_SYSTEM_DIR;
+ tmpPkg = pp.parsePackage(codePath, parseFlags);
+ } catch (PackageParserException e) {
+ Slog.w(TAG, "Failed to parse compressed system package:" + pkgSetting.name, e);
+ return;
+ }
+ synchronized (mInstallLock) {
+ // Disable the stub and remove any package entries
+ removePackageLI(deletedPkg, true);
+ synchronized (mPackages) {
+ disableSystemPackageLPw(deletedPkg, tmpPkg);
+ }
+ final PackageParser.Package newPkg;
+ try (PackageFreezer freezer =
+ freezePackage(deletedPkg.packageName, "setEnabledSetting")) {
+ final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
+ | PackageParser.PARSE_ENFORCE_CODE;
+ newPkg = scanPackageTracedLI(codePath, parseFlags, 0 /*scanFlags*/,
+ 0 /*currentTime*/, null /*user*/);
+ prepareAppDataAfterInstallLIF(newPkg);
+ synchronized (mPackages) {
+ try {
+ updateSharedLibrariesLPr(newPkg, null);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e);
+ }
+ updatePermissionsLPw(newPkg.packageName, newPkg,
+ UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
+ mSettings.writeLPr();
+ }
+ } catch (PackageManagerException e) {
+ // Whoops! Something went wrong; try to roll back to the stub
+ Slog.w(TAG, "Failed to install compressed system package:"
+ + pkgSetting.name, e);
+ // Remove the failed install
+ removeCodePathLI(codePath);
+
+ // Install the system package
+ try (PackageFreezer freezer =
+ freezePackage(deletedPkg.packageName, "setEnabledSetting")) {
+ synchronized (mPackages) {
+ // NOTE: The system package always needs to be enabled; even
+ // if it's for a compressed stub. If we don't, installing the
+ // system package fails during scan [scanning checks the disabled
+ // packages]. We will reverse this later, after we've "installed"
+ // the stub.
+ // This leaves us in a fragile state; the stub should never be
+ // enabled, so, cross your fingers and hope nothing goes wrong
+ // until we can disable the package later.
+ enableSystemPackageLPw(deletedPkg);
+ }
+ installPackageFromSystemLIF(new File(deletedPkg.codePath),
+ false /*isPrivileged*/, null /*allUserHandles*/,
+ null /*origUserHandles*/, null /*origPermissionsState*/,
+ true /*writeSettings*/);
+ } catch (PackageManagerException pme) {
+ Slog.w(TAG, "Failed to restore system package:"
+ + deletedPkg.packageName, pme);
+ } finally {
+ synchronized (mPackages) {
+ mSettings.disableSystemPackageLPw(
+ deletedPkg.packageName, true /*replaced*/);
+ mSettings.writeLPr();
+ }
+ }
+ return;
+ }
+ clearAppDataLIF(newPkg, UserHandle.USER_ALL, FLAG_STORAGE_DE
+ | FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ clearAppProfilesLIF(newPkg, UserHandle.USER_ALL);
+ mDexManager.notifyPackageUpdated(newPkg.packageName,
+ newPkg.baseCodePath, newPkg.splitCodePaths);
+ }
+ }
+ if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ // Don't care about who enables an app.
+ callingPackage = null;
+ }
+ synchronized (mPackages) {
+ pkgSetting.setEnabled(newState, userId, callingPackage);
+ }
+ } else {
+ synchronized (mPackages) {
// We're dealing with a component level state change
// First, verify that this is a valid class name.
PackageParser.Package pkg = pkgSetting.pkg;
if (pkg == null || !pkg.hasComponentClassName(className)) {
- if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
+ if (pkg != null &&
+ pkg.applicationInfo.targetSdkVersion >=
+ Build.VERSION_CODES.JELLY_BEAN) {
throw new IllegalArgumentException("Component class " + className
+ " does not exist in " + packageName);
} else {
}
}
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)) {
+ 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;
- }
- 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) {
}
}
+ @Override
+ public void flushPackageRestrictionsAsUser(int userId) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return;
+ }
+ if (!sUserManager.exists(userId)) {
+ return;
+ }
+ 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)
extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
extras.putInt(Intent.EXTRA_UID, packageUid);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null, null,
+ // 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;
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null,
new int[] {UserHandle.getUserId(packageUid)});
}
@Override
public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
if (!sUserManager.exists(userId)) return;
- final int uid = Binder.getCallingUid();
+ 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);
- enforceCrossUserPermission(uid, userId, true, true, "stop package");
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, true /* checkShell */, "stop package");
// writer
synchronized (mPackages) {
- if (mSettings.setPackageStoppedStateLPw(this, packageName, stopped,
- allowedByPermission, uid, userId)) {
+ 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();
+ if (getInstantAppPackageName(callingUid) != null) {
+ return null;
+ }
// reader
synchronized (mPackages) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
+ return null;
+ }
return mSettings.getInstallerPackageNameLPr(packageName);
}
}
+ public boolean isOrphaned(String packageName) {
+ // reader
+ synchronized (mPackages) {
+ return mSettings.isOrphaned(packageName);
+ }
+ }
+
@Override
public int getApplicationEnabledSetting(String packageName, int userId) {
if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
- int uid = Binder.getCallingUid();
- enforceCrossUserPermission(uid, userId, false, false, "get enabled");
+ int callingUid = Binder.getCallingUid();
+ 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(ComponentName componentName, int userId) {
+ public int getComponentEnabledSetting(ComponentName component, int userId) {
if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
- int uid = Binder.getCallingUid();
- enforceCrossUserPermission(uid, userId, false, false, "get component enabled");
- // reader
+ int callingUid = Binder.getCallingUid();
+ enforceCrossUserPermission(callingUid, userId,
+ false /*requireFullPermission*/, false /*checkShell*/, "getComponentEnabled");
synchronized (mPackages) {
- return mSettings.getComponentEnabledSettingLPr(componentName, userId);
+ if (filterAppAccessLPr(mSettings.getPackageLPr(component.getPackageName()), callingUid,
+ component, TYPE_UNKNOWN, userId)) {
+ return COMPONENT_ENABLED_STATE_DISABLED;
+ }
+ return mSettings.getComponentEnabledSettingLPr(component, userId);
}
}
@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) {
+ mEphemeralAppsDisabled =
+ (Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0) ||
+ (Secure.getInt(resolver, Secure.INSTANT_APPS_ENABLED, 1) == 0);
+ }
+ };
+ mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
+ .getUriFor(Global.ENABLE_EPHEMERAL_FEATURE),
+ false, co, UserHandle.USER_SYSTEM);
+ mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
+ .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_SYSTEM);
+ 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);
// Read the compatibilty setting when the system is ready.
boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
mDefaultPermissionPolicy.grantDefaultPermissions(userId);
}
+ // 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.
+ if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
+ mDefaultPermissionPolicy.scheduleReadDefaultPermissionExceptions();
+ }
+
// Kick off any messages waiting for system ready
if (mPostSystemReadyMessages != null) {
for (Message msg : mPostSystemReadyMessages) {
mInstallerService.systemReady();
mPackageDexOptimizer.systemReady();
- MountServiceInternal mountServiceInternal = LocalServices.getService(
- MountServiceInternal.class);
- mountServiceInternal.addExternalStoragePolicy(
- new MountServiceInternal.ExternalStorageMountPolicy() {
+ StorageManagerInternal StorageManagerInternal = LocalServices.getService(
+ StorageManagerInternal.class);
+ StorageManagerInternal.addExternalStoragePolicy(
+ new StorageManagerInternal.ExternalStorageMountPolicy() {
@Override
public int getMountMode(int uid, String packageName) {
if (Process.isIsolated(uid)) {
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);
+
+ if (mPrivappPermissionsViolations != null) {
+ Slog.wtf(TAG,"Signature|privileged permissions not in "
+ + "privapp-permissions whitelist: " + mPrivappPermissionsViolations);
+ mPrivappPermissionsViolations = null;
+ }
+ }
+
+ public void waitForAppDataPrepared() {
+ if (mPrepareAppDataFuture == null) {
+ return;
+ }
+ ConcurrentUtils.waitForFutureNoInterrupt(mPrepareAppDataFuture, "wait for prepareAppData");
+ mPrepareAppDataFuture = null;
}
@Override
public boolean isSafeMode() {
+ // allow instant applications
return mSafeMode;
}
@Override
public boolean hasSystemUidErrors() {
+ // allow instant applications
return mHasSystemUidErrors;
}
static class DumpState {
public static final int DUMP_LIBS = 1 << 0;
public static final int DUMP_FEATURES = 1 << 1;
- public static final int DUMP_RESOLVERS = 1 << 2;
- public static final int DUMP_PERMISSIONS = 1 << 3;
- public static final int DUMP_PACKAGES = 1 << 4;
- public static final int DUMP_SHARED_USERS = 1 << 5;
- public static final int DUMP_MESSAGES = 1 << 6;
- public static final int DUMP_PROVIDERS = 1 << 7;
- public static final int DUMP_VERIFIERS = 1 << 8;
- public static final int DUMP_PREFERRED = 1 << 9;
- public static final int DUMP_PREFERRED_XML = 1 << 10;
- public static final int DUMP_KEYSETS = 1 << 11;
- public static final int DUMP_VERSION = 1 << 12;
- public static final int DUMP_INSTALLS = 1 << 13;
- public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 14;
- public static final int DUMP_DOMAIN_PREFERRED = 1 << 15;
+ public static final int DUMP_ACTIVITY_RESOLVERS = 1 << 2;
+ public static final int DUMP_SERVICE_RESOLVERS = 1 << 3;
+ public static final int DUMP_RECEIVER_RESOLVERS = 1 << 4;
+ public static final int DUMP_CONTENT_RESOLVERS = 1 << 5;
+ public static final int DUMP_PERMISSIONS = 1 << 6;
+ public static final int DUMP_PACKAGES = 1 << 7;
+ public static final int DUMP_SHARED_USERS = 1 << 8;
+ public static final int DUMP_MESSAGES = 1 << 9;
+ public static final int DUMP_PROVIDERS = 1 << 10;
+ public static final int DUMP_VERIFIERS = 1 << 11;
+ public static final int DUMP_PREFERRED = 1 << 12;
+ public static final int DUMP_PREFERRED_XML = 1 << 13;
+ public static final int DUMP_KEYSETS = 1 << 14;
+ public static final int DUMP_VERSION = 1 << 15;
+ public static final int DUMP_INSTALLS = 1 << 16;
+ public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 17;
+ public static final int DUMP_DOMAIN_PREFERRED = 1 << 18;
+ public static final int DUMP_FROZEN = 1 << 19;
+ public static final int DUMP_DEXOPT = 1 << 20;
+ public static final int DUMP_COMPILER_STATS = 1 << 21;
+ public static final int DUMP_CHANGES = 1 << 22;
+ public static final int DUMP_VOLUMES = 1 << 23;
public static final int OPTION_SHOW_FILTERS = 1 << 0;
}
@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);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump ActivityManager from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " without permission "
- + android.Manifest.permission.DUMP);
- return;
- }
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
DumpState dumpState = new DumpState();
boolean fullPreferred = false;
pw.println(" -h: print this help");
pw.println(" cmd may be one of:");
pw.println(" l[ibraries]: list known shared libraries");
- pw.println(" f[ibraries]: list device features");
+ pw.println(" f[eatures]: list device features");
pw.println(" k[eysets]: print known keysets");
- pw.println(" r[esolvers]: dump intent resolvers");
+ 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(" 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(" enabled-overlays: dump list of enabled overlay packages");
pw.println(" <package.name>: info about given package");
return;
} else if ("--checkin".equals(opt)) {
checkin = true;
} 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");
}
pw.println("Error: check-permission missing package argument");
return;
}
+
String pkg = args[opti];
opti++;
int user = UserHandle.getUserId(Binder.getCallingUid());
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)) {
} else if ("f".equals(cmd) || "features".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_FEATURES);
} else if ("r".equals(cmd) || "resolvers".equals(cmd)) {
- dumpState.setDump(DumpState.DUMP_RESOLVERS);
+ 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)) {
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 ("write".equals(cmd)) {
synchronized (mPackages) {
mSettings.writeLPr();
pw.print(" Required: ");
pw.print(mRequiredVerifierPackage);
pw.print(" (uid=");
- pw.print(getPackageUid(mRequiredVerifierPackage, 0));
+ 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, 0));
+ pw.print(",");
+ pw.println(getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+ UserHandle.USER_SYSTEM));
}
}
pw.print(" Using: ");
pw.print(verifierPackageName);
pw.print(" (uid=");
- pw.print(getPackageUid(verifierPackageName, 0));
+ 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, 0));
+ pw.print(",");
+ pw.println(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
+ UserHandle.USER_SYSTEM));
}
} else {
pw.println();
boolean printedHeader = false;
final Iterator<String> it = mSharedLibraries.keySet().iterator();
while (it.hasNext()) {
- String name = it.next();
- SharedLibraryEntry ent = mSharedLibraries.get(name);
- if (!checkin) {
- if (!printedHeader) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("Libraries:");
- printedHeader = true;
- }
- pw.print(" ");
- } else {
- pw.print("lib,");
- }
- pw.print(name);
- if (!checkin) {
- pw.print(" -> ");
+ String libName = it.next();
+ SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(libName);
+ if (versionedLib == null) {
+ continue;
}
- if (ent.path != null) {
+ final int versionCount = versionedLib.size();
+ for (int i = 0; i < versionCount; i++) {
+ SharedLibraryEntry libEntry = versionedLib.valueAt(i);
if (!checkin) {
- pw.print("(jar) ");
- pw.print(ent.path);
+ if (!printedHeader) {
+ if (dumpState.onTitlePrinted())
+ pw.println();
+ pw.println("Libraries:");
+ printedHeader = true;
+ }
+ pw.print(" ");
} else {
- pw.print(",jar,");
- pw.print(ent.path);
+ pw.print("lib,");
+ }
+ pw.print(libEntry.info.getName());
+ if (libEntry.info.isStatic()) {
+ pw.print(" version=" + libEntry.info.getVersion());
}
- } else {
if (!checkin) {
- pw.print("(apk) ");
- pw.print(ent.apk);
+ pw.print(" -> ");
+ }
+ if (libEntry.path != null) {
+ pw.print(" (jar) ");
+ pw.print(libEntry.path);
} else {
- pw.print(",apk,");
- pw.print(ent.apk);
+ pw.print(" (apk) ");
+ pw.print(libEntry.apk);
}
+ pw.println();
}
- pw.println();
}
}
if (!checkin) {
pw.println("Features:");
}
- Iterator<String> it = mAvailableFeatures.keySet().iterator();
- while (it.hasNext()) {
- String name = it.next();
- if (!checkin) {
- pw.print(" ");
- } else {
- pw.print("feat,");
+
+ 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();
+ }
}
- pw.println(name);
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_RESOLVERS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:"
: "Activity Resolver Table:", " ", packageName,
dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
dumpState.setTitlePrinted(true);
}
+ }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
if (mReceivers.dump(pw, dumpState.getTitlePrinted() ? "\nReceiver Resolver Table:"
: "Receiver Resolver Table:", " ", packageName,
dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
dumpState.setTitlePrinted(true);
}
+ }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
if (mServices.dump(pw, dumpState.getTitlePrinted() ? "\nService Resolver Table:"
: "Service Resolver Table:", " ", packageName,
dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
dumpState.setTitlePrinted(true);
}
+ }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
if (mProviders.dump(pw, dumpState.getTitlePrinted() ? "\nProvider Resolver Table:"
: "Provider Resolver Table:", " ", packageName,
dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
count = 0;
for (PackageSetting ps : allPackageSettings) {
final long status = ps.getDomainVerificationStatusForUser(userId);
- if (status >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+ if (status >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
+ && !DEBUG_DOMAIN_VERIFICATION) {
continue;
}
pw.println(prefix + "Package: " + ps.name);
mSettings.dumpSharedUsersLPr(pw, packageName, permissionNames, dumpState, checkin);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) {
+ 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_PERMISSIONS) && packageName == null) {
+ mSettings.dumpRestoredPermissionGrantsLPr(pw, dumpState);
+ }
+
+ 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();
- mInstallerService.dump(new IndentingPrintWriter(pw, " ", 120));
+
+ 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_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) {
}
}
}
+
+ // 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));
+ }
+ }
+
+ 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);
+ dumpMessagesProto(proto);
+ }
+ proto.flush();
+ }
+
+ private void dumpMessagesProto(ProtoOutputStream proto) {
+ BufferedReader in = null;
+ String line = null;
+ try {
+ in = new BufferedReader(new FileReader(getSettingsProblemFile()));
+ while ((line = in.readLine()) != null) {
+ if (line.contains("ignored: updated version")) continue;
+ proto.write(PackageServiceDumpProto.MESSAGES, line);
+ }
+ } catch (IOException ignored) {
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ private void dumpFeaturesProto(ProtoOutputStream proto) {
+ synchronized (mAvailableFeatures) {
+ final int count = mAvailableFeatures.size();
+ for (int i = 0; i < count; i++) {
+ final FeatureInfo feat = mAvailableFeatures.valueAt(i);
+ final long featureToken = proto.start(PackageServiceDumpProto.FEATURES);
+ proto.write(PackageServiceDumpProto.FeatureProto.NAME, feat.name);
+ proto.write(PackageServiceDumpProto.FeatureProto.VERSION, feat.version);
+ proto.end(featureToken);
+ }
+ }
+ }
+
+ private void dumpSharedLibrariesProto(ProtoOutputStream proto) {
+ final int count = mSharedLibraries.size();
+ for (int i = 0; i < count; i++) {
+ final String libName = mSharedLibraries.keyAt(i);
+ SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(libName);
+ if (versionedLib == null) {
+ continue;
+ }
+ final int versionCount = versionedLib.size();
+ for (int j = 0; j < versionCount; j++) {
+ final SharedLibraryEntry libEntry = versionedLib.valueAt(j);
+ final long sharedLibraryToken =
+ proto.start(PackageServiceDumpProto.SHARED_LIBRARIES);
+ proto.write(PackageServiceDumpProto.SharedLibraryProto.NAME, libEntry.info.getName());
+ final boolean isJar = (libEntry.path != null);
+ proto.write(PackageServiceDumpProto.SharedLibraryProto.IS_JAR, isJar);
+ if (isJar) {
+ proto.write(PackageServiceDumpProto.SharedLibraryProto.PATH, libEntry.path);
+ } else {
+ proto.write(PackageServiceDumpProto.SharedLibraryProto.APK, libEntry.apk);
+ }
+ proto.end(sharedLibraryToken);
+ }
+ }
+ }
+
+ private void dumpDexoptStateLPr(PrintWriter pw, String packageName) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
+ ipw.println();
+ ipw.println("Dexopt state:");
+ ipw.increaseIndent();
+ Collection<PackageParser.Package> packages = null;
+ 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();
+ }
+ }
+
+ private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
+ ipw.println();
+ ipw.println("Compiler stats:");
+ ipw.increaseIndent();
+ Collection<PackageParser.Package> packages = null;
+ 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);
- List<IntentFilter> filters = getAllIntentFilters(packageName);
+ List<IntentFilterVerificationInfo> iviList = getIntentFilterVerifications(packageName)
+ .getList();
+ List<IntentFilter> filters = getAllIntentFilters(packageName).getList();
ArraySet<String> result = new ArraySet<>();
if (iviList.size() > 0) {
*/
@Override
public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
- int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
- throw new SecurityException("Media status can only be updated by the system");
- }
+ enforceSystemOrRoot("Media status can only be updated by the system");
// reader; this apparently protects mMediaMounted, but should probably
// be a different lock in that case.
synchronized (mPackages) {
}
/**
- * Called by MountService when the initial ASECs to scan are available.
+ * Called by StorageManagerService when the initial ASECs to scan are available.
* Should block until all the ASEC containers are finished being scanned.
*/
public void scanAvailableAsecs() {
updateExternalMediaStatusInner(true, false, false);
- if (mShouldRestoreconData) {
- SELinuxMMAC.setRestoreconDone();
- mShouldRestoreconData = false;
- }
}
/*
}
String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
: Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
- sendPackageBroadcast(action, null, extras, null, finishedReceiver, null);
+ sendPackageBroadcast(action, null, extras, 0, null, finishedReceiver, null);
}
}
synchronized (mInstallLock) {
PackageParser.Package pkg = null;
try {
- pkg = scanPackageLI(new File(codePath), parseFlags, 0, 0, null);
+ // Sadly we don't know the package name yet to freeze it
+ pkg = scanPackageTracedLI(new File(codePath), parseFlags,
+ SCAN_IGNORE_FROZEN, 0, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to scan " + codePath + ": " + e.getMessage());
}
/*
* Unload packages mounted on external media. This involves deleting package
- * data from internal structures, sending broadcasts about diabled packages,
+ * data from internal structures, sending broadcasts about disabled packages,
* gc'ing to free up references, unmounting all secure containers
* corresponding to packages on external media, and posting a
* UPDATED_MEDIA_STATUS message if status has been requested. Please note
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Trying to unload pkg : " + pkgName);
// Delete package internally
- PackageRemovedInfo outInfo = new PackageRemovedInfo();
+ PackageRemovedInfo outInfo = new PackageRemovedInfo(this);
synchronized (mInstallLock) {
- boolean res = deletePackageLI(pkgName, null, false, null, null,
- PackageManager.DELETE_KEEP_DATA, outInfo, false);
+ final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
+ final boolean res;
+ try (PackageFreezer freezer = freezePackageForDelete(pkgName, deleteFlags,
+ "unloadMediaPackages")) {
+ res = deletePackageLIF(pkgName, null, false, null, deleteFlags, outInfo, false,
+ null);
+ }
if (res) {
pkgList.add(pkgName);
} else {
}
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(vol.fsUuid);
- packages = mSettings.getVolumePackagesLPr(vol.fsUuid);
+ 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 = scanPackageLI(ps.codePath, parseFlags, SCAN_INITIAL, 0L, null);
+ 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)) {
- deleteCodeCacheDirsLI(ps.volumeUuid, ps.name);
+ clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
+ StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE
+ | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ }
+ }
+ }
+
+ // Reconcile app data for all started/unlocked users
+ final StorageManager sm = mContext.getSystemService(StorageManager.class);
+ final UserManager um = mContext.getSystemService(UserManager.class);
+ UserManagerInternal umInternal = getUserManagerInternal();
+ for (UserInfo user : um.getUsers()) {
+ final int flags;
+ if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
+ flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
+ } else if (umInternal.isUserRunning(user.id)) {
+ flags = StorageManager.FLAG_STORAGE_DE;
+ } else {
+ continue;
+ }
+
+ try {
+ sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, flags);
+ synchronized (mInstallLock) {
+ reconcileAppsDataLI(volumeUuid, user.id, flags, true /* migrateAppData */);
}
+ } catch (IllegalStateException e) {
+ // Device was probably ejected, and we'll process that event momentarily
+ Slog.w(TAG, "Failed to prepare storage: " + e);
}
}
int updateFlags = UPDATE_PERMISSIONS_ALL;
if (ver.sdkVersion != mSdkVersion) {
logCriticalInfo(Log.INFO, "Platform changed from " + ver.sdkVersion + " to "
- + mSdkVersion + "; regranting permissions for " + vol.fsUuid);
+ + mSdkVersion + "; regranting permissions for " + volumeUuid);
updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
- updatePermissionsLPw(null, null, vol.fsUuid, updateFlags);
+ updatePermissionsLPw(null, null, volumeUuid, updateFlags);
// 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) {
}
private void unloadPrivatePackagesInner(VolumeInfo vol) {
- final ArrayList<ApplicationInfo> unloaded = new ArrayList<>();
- synchronized (mInstallLock) {
+ 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(vol.fsUuid);
+ final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid);
for (PackageSetting ps : packages) {
if (ps.pkg == null) continue;
final ApplicationInfo info = ps.pkg.applicationInfo;
- final PackageRemovedInfo outInfo = new PackageRemovedInfo();
- if (deletePackageLI(ps.name, null, false, null, null,
- PackageManager.DELETE_KEEP_DATA, outInfo, false)) {
- unloaded.add(info);
- } else {
- Slog.w(TAG, "Failed to unload " + ps.codePath);
+ 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();
+ }
}
- /**
- * Examine all users present on given mounted volume, and destroy data
- * belonging to users that are no longer valid, or whose user ID has been
- * recycled.
- */
- private void reconcileUsers(String volumeUuid) {
- final File[] files = FileUtils
- .listFilesOrEmpty(Environment.getDataUserDirectory(volumeUuid));
- for (File file : files) {
- if (!file.isDirectory()) continue;
+ private void assertPackageKnown(String volumeUuid, String packageName)
+ throws PackageManagerException {
+ synchronized (mPackages) {
+ // Normalize package name to handle renamed packages
+ packageName = normalizePackageNameLPr(packageName);
- final int userId;
- final UserInfo info;
- try {
- userId = Integer.parseInt(file.getName());
- info = sUserManager.getUserInfo(userId);
- } catch (NumberFormatException e) {
- Slog.w(TAG, "Invalid user directory " + file);
- continue;
+ 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);
}
+ }
+ }
- boolean destroyUser = false;
- if (info == null) {
- logCriticalInfo(Log.WARN, "Destroying user directory " + file
- + " because no matching user was found");
- destroyUser = true;
- } else {
- try {
- UserManagerService.enforceSerialNumber(file, info.serialNumber);
- } catch (IOException e) {
- logCriticalInfo(Log.WARN, "Destroying user directory " + file
- + " because we failed to enforce serial number: " + e);
- destroyUser = true;
- }
- }
+ private void assertPackageKnownAndInstalled(String volumeUuid, String packageName, int userId)
+ throws PackageManagerException {
+ synchronized (mPackages) {
+ // Normalize package name to handle renamed packages
+ packageName = normalizePackageNameLPr(packageName);
- if (destroyUser) {
- synchronized (mInstallLock) {
- mInstaller.removeUserDataDirs(volumeUuid, userId);
- }
+ 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);
}
}
+ }
- final UserManager um = mContext.getSystemService(UserManager.class);
- for (UserInfo user : um.getUsers()) {
- final File userDir = Environment.getDataUserDirectory(volumeUuid, user.id);
- if (userDir.exists()) continue;
-
- try {
- UserManagerService.prepareUserDirectory(mContext, volumeUuid, user.id);
- UserManagerService.enforceSerialNumber(userDir, user.serialNumber);
- } catch (IOException e) {
- Log.wtf(TAG, "Failed to create user directory on " + volumeUuid, e);
+ 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;
}
}
* another volume.
*/
private void reconcileApps(String volumeUuid) {
- final File[] files = FileUtils
- .listFilesOrEmpty(Environment.getDataAppDirectory(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());
continue;
}
- boolean destroyApp = false;
- String packageName = null;
+ 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);
+ }
+ }
+ }
+
+ 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.
+ * @returns list of skipped non-core packages (if {@code onlyCoreApps} is true)
+ */
+ private List<String> reconcileAppsDataLI(String volumeUuid, int userId, int flags,
+ boolean migrateAppData, boolean onlyCoreApps) {
+ Slog.v(TAG, "reconcileAppsData for " + volumeUuid + " u" + userId + " 0x"
+ + Integer.toHexString(flags) + " migrateAppData=" + migrateAppData);
+ List<String> result = onlyCoreApps ? new ArrayList<>() : null;
+
+ final File ceDir = Environment.getDataUserCeDirectory(volumeUuid, userId);
+ final File deDir = Environment.getDataUserDeDirectory(volumeUuid, userId);
+
+ // First look for stale data that doesn't belong, and check if things
+ // have changed since we did our last restorecon
+ if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
+ if (StorageManager.isFileEncryptedNativeOrEmulated()
+ && !StorageManager.isUserKeyUnlocked(userId)) {
+ throw new RuntimeException(
+ "Yikes, someone asked us to reconcile CE storage while " + userId
+ + " was still locked; this would have caused massive data loss!");
+ }
+
+ final File[] files = FileUtils.listFilesOrEmpty(ceDir);
+ for (File file : files) {
+ final String packageName = file.getName();
+ try {
+ assertPackageKnownAndInstalled(volumeUuid, packageName, userId);
+ } catch (PackageManagerException e) {
+ logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
+ try {
+ mInstaller.destroyAppData(volumeUuid, packageName, userId,
+ StorageManager.FLAG_STORAGE_CE, 0);
+ } catch (InstallerException e2) {
+ logCriticalInfo(Log.WARN, "Failed to destroy: " + e2);
+ }
+ }
+ }
+ }
+ if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
+ final File[] files = FileUtils.listFilesOrEmpty(deDir);
+ for (File file : files) {
+ final String packageName = file.getName();
+ try {
+ assertPackageKnownAndInstalled(volumeUuid, packageName, userId);
+ } catch (PackageManagerException e) {
+ logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
+ try {
+ mInstaller.destroyAppData(volumeUuid, packageName, userId,
+ StorageManager.FLAG_STORAGE_DE, 0);
+ } catch (InstallerException e2) {
+ logCriticalInfo(Log.WARN, "Failed to destroy: " + e2);
+ }
+ }
+ }
+ }
+
+ // Ensure that data directories are ready to roll for all packages
+ // installed for this volume and user
+ final List<PackageSetting> packages;
+ synchronized (mPackages) {
+ packages = mSettings.getVolumePackagesLPr(volumeUuid);
+ }
+ int preparedCount = 0;
+ for (PackageSetting ps : packages) {
+ final String packageName = ps.name;
+ if (ps.pkg == null) {
+ Slog.w(TAG, "Odd, missing scanned package " + packageName);
+ // TODO: might be due to legacy ASEC apps; we should circle back
+ // and reconcile again once they're scanned
+ continue;
+ }
+ // Skip non-core apps if requested
+ if (onlyCoreApps && !ps.pkg.coreApp) {
+ result.add(packageName);
+ continue;
+ }
+
+ if (ps.getInstalled(userId)) {
+ prepareAppDataAndMigrateLIF(ps.pkg, userId, flags, migrateAppData);
+ preparedCount++;
+ }
+ }
+
+ Slog.v(TAG, "reconcileAppsData finished " + preparedCount + " packages");
+ return result;
+ }
+
+ /**
+ * Prepare app data for the given app just after it was installed or
+ * upgraded. This method carefully only touches users that it's installed
+ * for, and it forces a restorecon to handle any seinfo changes.
+ * <p>
+ * Verifies that directories exist and that ownership and labeling is
+ * correct for all installed apps. If there is an ownership mismatch, it
+ * will try recovering system apps by wiping data; third-party app data is
+ * left intact.
+ * <p>
+ * <em>Note: To avoid a deadlock, do not call this method with {@code mPackages} lock held</em>
+ */
+ private void prepareAppDataAfterInstallLIF(PackageParser.Package pkg) {
+ final PackageSetting ps;
+ synchronized (mPackages) {
+ ps = mSettings.mPackages.get(pkg.packageName);
+ mSettings.writeKernelMappingLPr(ps);
+ }
+
+ final UserManager um = mContext.getSystemService(UserManager.class);
+ UserManagerInternal umInternal = getUserManagerInternal();
+ for (UserInfo user : um.getUsers()) {
+ final int flags;
+ if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
+ flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
+ } else if (umInternal.isUserRunning(user.id)) {
+ flags = StorageManager.FLAG_STORAGE_DE;
+ } else {
+ continue;
+ }
+
+ if (ps.getInstalled(user.id)) {
+ // TODO: when user data is locked, mark that we're still dirty
+ prepareAppDataLIF(pkg, user.id, flags);
+ }
+ }
+ }
+
+ /**
+ * Prepare app data for the given app.
+ * <p>
+ * Verifies that directories exist and that ownership and labeling is
+ * correct for all installed apps. If there is an ownership mismatch, this
+ * will try recovering system apps by wiping data; third-party app data is
+ * left intact.
+ */
+ private void prepareAppDataLIF(PackageParser.Package pkg, int userId, int flags) {
+ if (pkg == null) {
+ Slog.wtf(TAG, "Package was null!", new Throwable());
+ return;
+ }
+ prepareAppDataLeafLIF(pkg, userId, flags);
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ prepareAppDataLeafLIF(pkg.childPackages.get(i), userId, flags);
+ }
+ }
+
+ private void prepareAppDataAndMigrateLIF(PackageParser.Package pkg, int userId, int flags,
+ boolean maybeMigrateAppData) {
+ prepareAppDataLIF(pkg, userId, flags);
+
+ if (maybeMigrateAppData && maybeMigrateAppDataLIF(pkg, userId)) {
+ // We may have just shuffled around app data directories, so
+ // prepare them one more time
+ prepareAppDataLIF(pkg, userId, flags);
+ }
+ }
+
+ private void prepareAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
+ if (DEBUG_APP_DATA) {
+ Slog.v(TAG, "prepareAppData for " + pkg.packageName + " u" + userId + " 0x"
+ + Integer.toHexString(flags));
+ }
+
+ final String volumeUuid = pkg.volumeUuid;
+ final String packageName = pkg.packageName;
+ final ApplicationInfo app = pkg.applicationInfo;
+ final int appId = UserHandle.getAppId(app.uid);
+
+ Preconditions.checkNotNull(app.seInfo);
+
+ long ceDataInode = -1;
+ try {
+ ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
+ appId, app.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, app.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);
+ }
+ }
+
+ if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
+ // TODO: mark this structure as dirty so we persist it!
+ synchronized (mPackages) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ 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.isSystemApp() && !StorageManager.isFileEncryptedNativeOrEmulated()
+ && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+ final int storageTarget = pkg.applicationInfo.isDefaultToDeviceProtectedStorage()
+ ? StorageManager.FLAG_STORAGE_DE : StorageManager.FLAG_STORAGE_CE;
try {
- final PackageLite pkg = PackageParser.parsePackageLite(file,
- PackageParser.PARSE_MUST_BE_APK);
- packageName = pkg.packageName;
+ 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;
+ }
+ }
- synchronized (mPackages) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
- logCriticalInfo(Log.WARN, "Destroying " + packageName + " on + "
- + volumeUuid + " because we found no install record");
- destroyApp = true;
- } else if (!TextUtils.equals(volumeUuid, ps.volumeUuid)) {
- logCriticalInfo(Log.WARN, "Destroying " + packageName + " on "
- + volumeUuid + " because we expected it on " + ps.volumeUuid);
- destroyApp = true;
+ 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 {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
}
- } catch (PackageParserException e) {
- logCriticalInfo(Log.WARN, "Destroying " + file + " due to parse failure: " + e);
- destroyApp = true;
+ close();
+ } finally {
+ super.finalize();
}
+ }
- if (destroyApp) {
- synchronized (mInstallLock) {
- if (packageName != null) {
- removeDataDirsLI(volumeUuid, packageName);
+ @Override
+ public void close() {
+ mCloseGuard.close();
+ if (mClosed.compareAndSet(false, true)) {
+ synchronized (mPackages) {
+ if (mWeFroze) {
+ mFrozenPackages.remove(mPackageName);
}
- if (file.isDirectory()) {
- mInstaller.rmPackageDir(file.getAbsolutePath());
- } else {
- file.delete();
+
+ if (mChildren != null) {
+ for (PackageFreezer freezer : mChildren) {
+ freezer.close();
+ }
}
}
}
}
}
- private void unfreezePackage(String packageName) {
+ /**
+ * Verify that given package is currently frozen.
+ */
+ private void checkPackageFrozen(String packageName) {
synchronized (mPackages) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps != null) {
- ps.frozen = false;
+ if (!mFrozenPackages.contains(packageName)) {
+ Slog.wtf(TAG, "Expected " + packageName + " to be frozen!", new Throwable());
}
}
}
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(new Runnable() {
@Override
public void run() {
try {
- movePackageInternal(packageName, volumeUuid, moveId);
+ movePackageInternal(packageName, volumeUuid, moveId, callingUid, user);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to move " + packageName, e);
- mMoveCallbacks.notifyStatusChanged(moveId,
- PackageManager.MOVE_FAILED_INTERNAL_ERROR);
+ mMoveCallbacks.notifyStatusChanged(moveId, e.error);
}
}
});
}
private void movePackageInternal(final String packageName, final String volumeUuid,
- final int moveId) throws PackageManagerException {
- final UserHandle user = new UserHandle(UserHandle.getCallingUserId());
+ final int moveId, final int callingUid, UserHandle user)
+ throws PackageManagerException {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
final PackageManager pm = mContext.getPackageManager();
final int appId;
final String seinfo;
final String label;
+ final int targetSdkVersion;
+ final PackageFreezer freezer;
+ final int[] installedUserIds;
// reader
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (pkg == null || ps == null) {
+ 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");
+ }
+
if (pkg.applicationInfo.isExternalAsec()) {
currentAsec = true;
currentVolumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
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 (ps.frozen) {
+ if (mFrozenPackages.contains(packageName)) {
throw new PackageManagerException(MOVE_FAILED_OPERATION_PENDING,
"Failed to move already frozen package");
}
- ps.frozen = true;
codeFile = new File(pkg.codePath);
installerPackageName = ps.installerPackageName;
packageAbiOverride = ps.cpuAbiOverrideString;
appId = UserHandle.getAppId(pkg.applicationInfo.uid);
- seinfo = pkg.applicationInfo.seinfo;
+ seinfo = pkg.applicationInfo.seInfo;
label = String.valueOf(pm.getApplicationLabel(pkg.applicationInfo));
- }
-
- // Now that we're guarded by frozen state, kill app during move
- final long token = Binder.clearCallingIdentity();
- try {
- killApplication(packageName, appId, "move pkg");
- } finally {
- Binder.restoreCallingIdentity(token);
+ targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
+ freezer = freezePackage(packageName, "movePackageInternal");
+ installedUserIds = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
final Bundle extras = new Bundle();
final VolumeInfo volume = storage.findVolumeByUuid(volumeUuid);
if (volume == null || volume.getType() != VolumeInfo.TYPE_PRIVATE
|| !volume.isMountedWritable()) {
- unfreezePackage(packageName);
+ freezer.close();
throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
"Move location not mounted private volume");
}
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) {
- if (!getPackageSizeInfoLI(packageName, -1, stats)) {
- unfreezePackage(packageName);
- throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
- "Failed to measure package size");
+ 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.getFreeSpace();
+ final long startFreeBytes = measurePath.getUsableSpace();
final long sizeBytes;
if (moveCompleteApp) {
sizeBytes = stats.codeSize + stats.dataSize;
}
if (sizeBytes > storage.getStorageBytesUntilLow(measurePath)) {
- unfreezePackage(packageName);
+ freezer.close();
throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
"Not enough free space to move");
}
+ PackageManager.installStatusToString(returnCode, msg));
installedLatch.countDown();
-
- // Regardless of success or failure of the move operation,
- // always unfreeze the package
- unfreezePackage(packageName);
+ freezer.close();
final int status = PackageManager.installStatusToPublicStatus(returnCode);
switch (status) {
} catch (InterruptedException ignored) {
}
- final long deltaFreeBytes = startFreeBytes - measurePath.getFreeSpace();
+ final long deltaFreeBytes = startFreeBytes - measurePath.getUsableSpace();
final int progress = 10 + (int) MathUtils.constrain(
((deltaFreeBytes * 80) / sizeBytes), 0, 80);
mMoveCallbacks.notifyStatusChanged(moveId, progress);
final String dataAppName = codeFile.getName();
move = new MoveInfo(moveId, currentVolumeUuid, volumeUuid, packageName,
- dataAppName, appId, seinfo);
+ dataAppName, appId, seinfo, targetSdkVersion);
} else {
move = null;
}
final Message msg = mHandler.obtainMessage(INIT_COPY);
final OriginInfo origin = OriginInfo.fromExistingFile(codeFile);
- msg.obj = new InstallParams(origin, move, installObserver, installFlags,
- installerPackageName, volumeUuid, null, user, packageAbiOverride, null);
+ final InstallParams params = new InstallParams(origin, move, installObserver, installFlags,
+ installerPackageName, volumeUuid, null /*verificationInfo*/, user,
+ packageAbiOverride, null /*grantedPermissions*/, null /*certificates*/,
+ PackageManager.INSTALL_REASON_UNKNOWN);
+ params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
+ msg.obj = params;
+
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "movePackage",
+ System.identityHashCode(msg.obj));
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+ System.identityHashCode(msg.obj));
+
mHandler.sendMessage(msg);
}
@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 cleanUpUserLILPw(UserManagerService userManager, int userHandle) {
- mDirtyUsers.remove(userHandle);
- mSettings.removeUserLPw(userHandle);
- mPendingBroadcasts.remove(userHandle);
- if (mInstaller != null) {
- // Technically, we shouldn't be doing this with the package lock
- // held. However, this is very rare, and there is already so much
- // other disk I/O going on, that we'll let it slide for now.
- final StorageManager storage = mContext.getSystemService(StorageManager.class);
- for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
- final String volumeUuid = vol.getFsUuid();
- if (DEBUG_INSTALL) Slog.d(TAG, "Removing user data on volume " + volumeUuid);
- mInstaller.removeUserDataDirs(volumeUuid, userHandle);
- }
+ 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);
}
- mUserNeedsBadging.delete(userHandle);
- removeUnusedPackagesLILPw(userManager, userHandle);
}
/**
* that are no longer in use by any other user.
* @param userHandle the user being removed
*/
- private void removeUnusedPackagesLILPw(UserManagerService userManager, final int userHandle) {
+ private void removeUnusedPackagesLPw(UserManagerService userManager, final int userHandle) {
final boolean DEBUG_CLEAN_APKS = false;
- int [] users = userManager.getUserIdsLPr();
+ int [] users = userManager.getUserIds();
Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
if (DEBUG_CLEAN_APKS) {
Slog.i(TAG, "Checking package " + packageName);
}
- boolean keep = false;
- 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]);
+ 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;
}
- break;
}
}
if (!keep) {
}
mHandler.post(new Runnable() {
public void run() {
- deletePackageX(packageName, userHandle, 0);
+ deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
+ userHandle, 0);
} //end run
});
}
}
/** Called by UserManagerService */
- void createNewUserLILPw(int userHandle) {
- if (mInstaller != null) {
- mInstaller.createUserConfig(userHandle);
- mSettings.createNewUserLILPw(this, mInstaller, userHandle);
- applyFactoryDefaultBrowserLPw(userHandle);
- primeDomainVerificationsLPw(userHandle);
+ void createNewUser(int userId, String[] disallowedPackages) {
+ synchronized (mInstallLock) {
+ mSettings.createNewUserLI(this, mInstaller, userId, disallowedPackages);
+ }
+ synchronized (mPackages) {
+ scheduleWritePackageRestrictionsLocked(userId);
+ scheduleWritePackageListLocked(userId);
+ applyFactoryDefaultBrowserLPw(userId);
+ primeDomainVerificationsLPw(userId);
+ }
+ }
+
+ void onNewUserCreated(final int userId) {
+ mDefaultPermissionPolicy.grantDefaultPermissions(userId);
+ // If permission review for legacy apps is required, we represent
+ // dagerous permissions for such apps as always granted runtime
+ // permissions to keep per user flag state whether review is needed.
+ // Hence, if a new user is added we have to propagate dangerous
+ // permission grants for these legacy apps.
+ if (mPermissionReviewRequired) {
+ updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
+ | UPDATE_PERMISSIONS_REPLACE_ALL);
}
- }
-
- void newUserCreated(final int userHandle) {
- mDefaultPermissionPolicy.grantDefaultPermissions(userHandle);
}
@Override
}
// kill any non-foreground processes so we restart them and
// grant/revoke the GID.
- final IActivityManager am = ActivityManagerNative.getDefault();
+ final IActivityManager am = ActivityManager.getService();
if (am != null) {
final long token = Binder.clearCallingIdentity();
try {
@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
@Override
public IPackageInstaller getPackageInstaller() {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return null;
+ }
return mInstallerService;
}
synchronized(mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
- Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
+ 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);
}
KeySetManagerService ksms = mSettings.mKeySetManagerService;
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);
+ Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- if (pkg.applicationInfo.uid != Binder.getCallingUid()
- && Process.SYSTEM_UID != Binder.getCallingUid()) {
+ 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.");
}
KeySetManagerService ksms = mSettings.mKeySetManagerService;
@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) {
- Slog.w(TAG, "KeySet requested for unknown package:" + 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();
@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) {
- Slog.w(TAG, "KeySet requested for unknown package:" + 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();
}
}
- public void getUsageStatsIfNoPackageUsageInfo() {
- if (!mPackageUsage.isHistoricalPackageUsageAvailable()) {
- UsageStatsManager usm = (UsageStatsManager) mContext.getSystemService(Context.USAGE_STATS_SERVICE);
- if (usm == null) {
- throw new IllegalStateException("UsageStatsManager must be initialized");
- }
- long now = System.currentTimeMillis();
- Map<String, UsageStats> stats = usm.queryAndAggregateUsageStats(now - mDexOptLRUThresholdInMills, now);
- for (Map.Entry<String, UsageStats> entry : stats.entrySet()) {
- String packageName = entry.getKey();
- PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- continue;
+ 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(new Runnable() {
+ public void run() {
+ deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
+ 0, PackageManager.DELETE_ALL_USERS);
}
- UsageStats usage = entry.getValue();
- pkg.mLastPackageUsageTimeInMills = usage.getLastTimeUsed();
- mPackageUsage.mIsHistoricalPackageUsageAvailable = true;
- }
+ });
}
}
}
}
- private final class OnPermissionChangeListeners extends Handler {
+ private final static class OnPermissionChangeListeners extends Handler {
private static final int MSG_ON_PERMISSIONS_CHANGED = 1;
private final RemoteCallbackList<IOnPermissionsChangeListener> mPermissionListeners =
}
}
- private class PackageManagerInternalImpl extends PackageManagerInternal {
+ private class PackageManagerNative extends IPackageManagerNative.Stub {
@Override
- public void setLocationPackagesProvider(PackagesProvider provider) {
- synchronized (mPackages) {
- mDefaultPermissionPolicy.setLocationPackagesProviderLPw(provider);
+ 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 void setImePackagesProvider(PackagesProvider provider) {
+ public int getVersionCodeForPackage(String packageName) throws RemoteException {
+ try {
+ int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+ PackageInfo pInfo = getPackageInfo(packageName, 0, callingUser);
+ if (pInfo != null) {
+ return pInfo.versionCode;
+ }
+ } catch (Exception e) {
+ }
+ return 0;
+ }
+ }
+
+ private class PackageManagerInternalImpl extends PackageManagerInternal {
+ @Override
+ public void setLocationPackagesProvider(PackagesProvider provider) {
synchronized (mPackages) {
- mDefaultPermissionPolicy.setImePackagesProviderLPr(provider);
+ mDefaultPermissionPolicy.setLocationPackagesProviderLPw(provider);
}
}
@Override
public void grantDefaultPermissionsToDefaultDialerApp(String packageName, int userId) {
synchronized (mPackages) {
+ mSettings.setDefaultDialerPackageNameLPw(packageName, userId);
mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultDialerAppLPr(
packageName, userId);
}
}
+
@Override
public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
synchronized (mPackages) {
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) {
+ // If we do not support permission review, done.
+ if (!mPermissionReviewRequired) {
+ return false;
+ }
+
+ PackageSetting packageSetting = mSettings.mPackages.get(packageName);
+ if (packageSetting == null) {
+ return false;
+ }
+
+ // Permission review applies only to apps not supporting the new permission model.
+ if (packageSetting.pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
+ return false;
+ }
+
+ // Legacy apps have the permission and get user consent on launch.
+ PermissionsState permissionsState = packageSetting.getPermissionsState();
+ return permissionsState.isPermissionReviewRequired(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 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 ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
+ int userId) {
+ return PackageManagerService.this.getHomeActivitiesAsUser(allHomeCandidates, userId);
+ }
+
+ @Override
+ public void setDeviceAndProfileOwnerPackages(
+ int deviceOwnerUserId, String deviceOwnerPackage,
+ SparseArray<String> profileOwnerPackages) {
+ mProtectedPackages.setDeviceAndProfileOwnerPackages(
+ deviceOwnerUserId, deviceOwnerPackage, profileOwnerPackages);
+ }
+
+ @Override
+ public boolean isPackageDataProtected(int userId, String packageName) {
+ return mProtectedPackages.isPackageDataProtected(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 void grantRuntimePermission(String packageName, String name, int userId,
+ boolean overridePolicy) {
+ PackageManagerService.this.grantRuntimePermission(packageName, name, userId,
+ overridePolicy);
+ }
+
+ @Override
+ public void revokeRuntimePermission(String packageName, String name, int userId,
+ boolean overridePolicy) {
+ PackageManagerService.this.revokeRuntimePermission(packageName, name, userId,
+ overridePolicy);
+ }
+
+ @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 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) {
+ return resolveIntentInternal(
+ intent, resolvedType, flags, userId, true /*resolveForStart*/);
+ }
+
+ @Override
+ public ResolveInfo resolveService(Intent intent, String resolvedType,
+ int flags, int userId, int callingUid) {
+ return resolveServiceInternal(intent, resolvedType, flags, userId, callingUid);
+ }
+
+ @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 boolean canAccessInstantApps(int callingUid, int userId) {
+ return PackageManagerService.this.canViewInstantApps(callingUid, 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
}
}
+ @Override
+ public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) {
+ enforceSystemOrPhoneCaller("grantDefaultPermissionsToEnabledImsServices");
+ synchronized (mPackages) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledImsServicesLPr(
+ packageNames, userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
private static void enforceSystemOrPhoneCaller(String tag) {
int callingUid = Binder.getCallingUid();
if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) {
"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();
+ 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;
+ }
+ 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)) {
+ return false;
+ }
+ if (mExternalSourcesPolicy != null) {
+ int isTrusted = mExternalSourcesPolicy.getPackageTrustedToInstallApps(packageName, uid);
+ if (isTrusted != PackageManagerInternal.ExternalSourcesPolicy.USER_DEFAULT) {
+ return isTrusted == PackageManagerInternal.ExternalSourcesPolicy.USER_TRUSTED;
+ }
+ }
+ return checkUidPermission(appOpPermission, uid) == PERMISSION_GRANTED;
+ }
+
+ @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");
+ 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;
+ }
+}
+
+interface PackageSender {
+ void sendPackageBroadcast(final String action, final String pkg,
+ final Bundle extras, final int flags, final String targetPkg,
+ final IIntentReceiver finishedReceiver, final int[] userIds);
+ void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted,
+ boolean includeStopped, int appId, int... userIds);
}