diff --git a/aosp/art/build/apex/art.rc b/aosp/art/build/apex/art.rc new file mode 100644 index 0000000000000000000000000000000000000000..c126813bedd5faf46a30f6041458434a35991ee3 --- /dev/null +++ b/aosp/art/build/apex/art.rc @@ -0,0 +1,55 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# A service that handles dexopt. See art/artd/README.md. It's a lazy service +# that is started and stopped dynamically as needed. +service artd /apex/com.android.art/bin/artd + interface aidl artd + disabled # Prevents the service from automatically starting at boot. + oneshot # Prevents the service from automatically restarting each time it is stopped. + class core + user artd + group artd + capabilities DAC_OVERRIDE FOWNER CHOWN + +# Same as above, but for Pre-reboot Dexopt. It runs in a chroot environment that +# is set up by dexopt_chroot_setup. It's a lazy service that is started and +# stopped dynamically as needed. +service artd_pre_reboot /apex/com.android.art/bin/art_exec --chroot=/mnt/pre_reboot_dexopt -- /apex/com.android.art/bin/artd --pre-reboot + interface aidl artd_pre_reboot + disabled # Prevents the service from automatically starting at boot. + oneshot # Prevents the service from automatically restarting each time it is stopped. + class core + user artd + group artd + capabilities DAC_OVERRIDE FOWNER CHOWN SYS_CHROOT + seclabel u:r:artd:s0 + +# A service that sets up the chroot environment for Pre-reboot Dexopt. See +# art/dexopt_chroot_setup/README.md. It's a lazy service that is started and +# stopped dynamically as needed. +service dexopt_chroot_setup /apex/com.android.art/bin/dexopt_chroot_setup + interface aidl dexopt_chroot_setup + disabled # Prevents the service from automatically starting at boot. + oneshot # Prevents the service from automatically restarting each time it is stopped. + class core + user artd + group artd + +# Run at boot in Android U and later. +service art_boot /apex/com.android.art/bin/art_boot + disabled # Started explicitly from system/core/rootdir/init.rc + oneshot + class core + user root diff --git a/aosp/external/perfetto/heapprofd.rc b/aosp/external/perfetto/heapprofd.rc new file mode 100644 index 0000000000000000000000000000000000000000..b057a23489b522712a11923216baf280fe69820c --- /dev/null +++ b/aosp/external/perfetto/heapprofd.rc @@ -0,0 +1,53 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +service heapprofd /system/bin/heapprofd + class late_start + disabled + socket heapprofd stream 0666 root root + user nobody + group nobody readproc + # By default, this daemon is idle. When profiling an app, we should unwind + # as fast as possible in the interest of the app being profiled. + task_profiles ProcessCapacityHigh + onrestart exec_background - nobody shell -- /system/bin/heapprofd --cleanup-after-crash + # DAC_READ_SEARCH is denied by SELinux on user builds because the SELinux + # permission is userdebug_or_eng only. + capabilities KILL + +# Allow to start a second heapprofd. We can use that one to profile the +# primary one. See src/memory/profiling/README.md. +service heapprofd_secondary /system/bin/heapprofd + disabled + oneshot + socket heapprofd stream 0666 root root + user nobody + group nobody readproc + # By default, this daemon is idle. When profiling an app, we should unwind + # as fast as possible in the interest of the app being profiled. + task_profiles ProcessCapacityHigh + # DAC_READ_SEARCH is denied by SELinux on user builds because the SELinux + # permission is userdebug_or_eng only. + capabilities KILL + +on property:persist.heapprofd.enable=1 + start heapprofd + +on property:traced.lazy.heapprofd=1 + start heapprofd + +on property:persist.heapprofd.enable="" && property:traced.lazy.heapprofd="" + stop heapprofd + +on property:persist.heapprofd.enable=0 + setprop persist.heapprofd.enable "" diff --git a/aosp/external/perfetto/perfetto.rc b/aosp/external/perfetto/perfetto.rc new file mode 100644 index 0000000000000000000000000000000000000000..c2c09e119956cb75cdeb50cf77233d76be904058 --- /dev/null +++ b/aosp/external/perfetto/perfetto.rc @@ -0,0 +1,157 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +service traced /system/bin/traced + class late_start + disabled + socket traced_consumer stream 0666 root root + socket traced_producer stream 0666 root root + user nobody + group nobody + task_profiles ProcessCapacityHigh + +service traced_probes /system/bin/traced_probes + class late_start + disabled + user nobody + # Despite the "log" group below, traced_probes is allowed to read log + # only on userdebug/eng via selinux (see traced_probes.te). + group nobody readproc log readtracefs + task_profiles ProcessCapacityHigh + # Clean up procfs configuration even if traced_probes crashes + # unexpectedly. + onrestart exec_background - nobody shell -- /system/bin/traced_probes --cleanup-after-crash + file /dev/kmsg w + +on property:persist.device_config.global_settings.sys_traced=1 + setprop persist.traced.enable 1 + +on property:persist.device_config.global_settings.sys_traced=0 + setprop persist.traced.enable 0 + +on property:debug.atrace.user_initiated=1 + stop traced_probes + +on property:persist.traced.enable=1 && property:debug.atrace.user_initiated="" + start traced_probes + +on property:persist.traced.enable=1 + # Trace files need to be: + # - Written by either uid:shell or uid:statsd. + # - Read by shell and incidentd. + mkdir /data/misc/perfetto-traces 0773 root shell + + # Traces in this directory are only accessed by dumpstate (read+unlink) and + # by the bug reporting UI (ls+getattr). + mkdir /data/misc/perfetto-traces/bugreport 0773 root shell + + # Traces in this directory are only accessed by system server + mkdir /data/misc/perfetto-traces/profiling 0773 root shell + + # This directory allows shell to save configs file in a place where the + # perfetto cmdline client can read then. /data/local/tmp/ isn't safe because + # too many other domains can write into that. See b/170404111. + mkdir /data/misc/perfetto-configs 0775 root shell + + start traced + start traced_probes + +on property:persist.traced.enable=0 + stop traced + stop traced_probes + +# Reset the Perfetto guard rail state on boot: +on post-fs-data + rm /data/misc/perfetto-traces/.guardraildata + +############################################################################# +# mm_events - Arms a perfetto trace config that is triggered +# on memory pressure (kmem_activity trigger) +############################################################################# + +service mm_events /system/bin/mm_events + class late_start + disabled + oneshot + user nobody + group nobody + +on property:persist.mm_events.enabled=true && property:persist.traced.enable=1 + restart mm_events # Restart to reset backoff interval + +on property:persist.mm_events.enabled=false + stop mm_events + +############################################################################# +# perfetto_trace_on_boot - Starts a perfetto trace on boot +############################################################################# +# +# There are two separate actions (a trigger action and a start action) to make +# sure that perfetto_trace_on_boot is started only once on boot (otherwise, +# whenever persist.debug.perfetto.boottrace=1 is set, perfetto_trace_on_boot +# would start immediately). +# +# persist.debug.perfetto.boottrace=1 can be manually set after boot (to record +# a trace on the next reboot) and we don't want to immediately start a trace +# when setting the debug property. So we turn "ro.persistent_properties.ready" +# into a trigger, and then check whether we should start tracing when the +# trigger fires. +on perfetto_maybe_trace_on_boot && property:persist.debug.perfetto.boottrace=1 && property:persist.traced.enable=1 + setprop persist.debug.perfetto.boottrace "" + rm /data/misc/perfetto-traces/boottrace.perfetto-trace + # Set by traced after listen()ing on the consumer socket. Without this, + # perfetto could try to connect to traced before traced is ready to listen. + wait_for_prop sys.trace.traced_started 1 + start perfetto_trace_on_boot + +on property:ro.persistent_properties.ready=true + trigger perfetto_maybe_trace_on_boot + +service perfetto_trace_on_boot /system/bin/perfetto -c /data/misc/perfetto-configs/boottrace.pbtxt --txt -o /data/misc/perfetto-traces/boottrace.perfetto-trace + disabled + gentle_kill + oneshot + user shell + group nobody + +# This is meant to stop the boot tracing. +# To use this, add a trigger with mode STOP_TRACING in the configuration used in perfetto_trace_on_boot. +# Then create a new config which contains `activate_triggers: ` where is +# the name specified in configuration used in perfetto_trace_on_boot. +on property:sys.boot_completed=1 && property:init.svc.perfetto_trace_on_boot=running + exec -- /system/bin/perfetto -c /data/misc/perfetto-configs/stopboottracetrigger.pbtxt --txt + +# Forcefully enable select userspace (atrace) tracing categories early into the +# userspace boot. This is primarily for capturing zygote events without waiting +# for perfetto daemons or the /data partition. +on late-init && property:ro.boot.fastboot.boottrace=enabled + setprop debug.atrace.tags.enableflags 802922 + +# disable on boot complete when using textual ftrace tracing without perfetto. +on property:sys.boot_completed=1 && property:ro.boot.fastboot.boottrace=enabled && property:init.svc.perfetto_trace_on_boot= + setprop debug.atrace.tags.enableflags 0 + write /sys/kernel/debug/tracing/tracing_on 0 + write /sys/kernel/tracing/tracing_on 0 + +# These must be set as soon as possible for processes guarded by +# android.sdk_sysprop_guard to find and cache the memory locations of +# where these sysprops are stored, particularly SurfaceFlinger which starts +# very early. +# TODO(b/281329340): remove this when no longer needed. +on init + setprop debug.perfetto.sdk_sysprop_guard_generation 0 + setprop debug.hwui.skia_tracing_enabled false + setprop debug.hwui.skia_use_perfetto_track_events false + setprop debug.renderengine.skia_tracing_enabled false + setprop debug.renderengine.skia_use_perfetto_track_events false diff --git a/aosp/external/perfetto/traced_perf.rc b/aosp/external/perfetto/traced_perf.rc new file mode 100644 index 0000000000000000000000000000000000000000..cbf53a07c5befdb530705ce055f97a7164cd5e2e --- /dev/null +++ b/aosp/external/perfetto/traced_perf.rc @@ -0,0 +1,47 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Notes: +# * socket used for receiving /proc/pid/{maps,mem} file descriptors +# * readproc group to stat(/proc/pid) to find out UID of processes, and read +# /proc/pid/cmdline. +# * KILL capability for sending BIONIC_SIGNAL_PROFILER. +# * DAC_READ_SEARCH capability for stack unwinding and on-device symbolization (requires +# opening libraries/executables for sections not already mapped in). +# * foreground task group as unwinding based on minidebug info is a heavyweight action. +service traced_perf /system/bin/traced_perf + class late_start + disabled + socket traced_perf stream 0666 root root + user nobody + group nobody readproc readtracefs + capabilities KILL + task_profiles ProcessCapacityHigh + +# Daemon run state: +# * initially off +# * |persist.traced_perf.enable| forces daemon to run unconditionally +# * if kernel doesn't have perf_event_open LSM hooks, daemon is stopped +# * otherwise, follow |traced.lazy.traced_perf| as an on-demand service +on property:persist.traced_perf.enable=1 + start traced_perf +on property:persist.traced_perf.enable="" && property:sys.init.perf_lsm_hooks="" + stop traced_perf +on property:persist.traced_perf.enable="" && property:sys.init.perf_lsm_hooks=1 && property:traced.lazy.traced_perf=1 + start traced_perf +on property:persist.traced_perf.enable="" && property:sys.init.perf_lsm_hooks=1 && property:traced.lazy.traced_perf="" + stop traced_perf + +on property:persist.traced_perf.enable=0 + setprop persist.traced_perf.enable "" diff --git a/aosp/frameworks/base/core/java/android/content/pm/IPackageManager.aidl b/aosp/frameworks/base/core/java/android/content/pm/IPackageManager.aidl new file mode 100644 index 0000000000000000000000000000000000000000..7d1f67dc5722f77b6e9eec2008989596eecba7a2 --- /dev/null +++ b/aosp/frameworks/base/core/java/android/content/pm/IPackageManager.aidl @@ -0,0 +1,853 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.content.pm; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ArchivedPackageParcel; +import android.content.pm.ChangedPackages; +import android.content.pm.InstantAppInfo; +import android.content.pm.FeatureInfo; +import android.content.pm.IDexModuleRegisterCallback; +import android.content.pm.InstallSourceInfo; +import android.content.pm.IOnChecksumsReadyListener; +import android.content.pm.IPackageInstaller; +import android.content.pm.IPackageDeleteObserver; +import android.content.pm.IPackageDeleteObserver2; +import android.content.pm.IPackageDataObserver; +import android.content.pm.IPackageMoveObserver; +import android.content.pm.IPackageStatsObserver; +import android.content.pm.IntentFilterVerificationInfo; +import android.content.pm.InstrumentationInfo; +import android.content.pm.KeySet; +import android.content.pm.ModuleInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.ComponentEnabledSetting; +import android.content.pm.ParceledListSlice; +import android.content.pm.ProviderInfo; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.SuspendDialogInfo; +import android.content.pm.UserInfo; +import android.content.pm.VerifierDeviceIdentity; +import android.content.pm.VersionedPackage; +import android.content.pm.dex.IArtManager; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Bundle; +import android.os.IRemoteCallback; +import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; +import android.os.UserHandle; + +import java.util.Map; + +/** + * See {@link PackageManager} for documentation on most of the APIs + * here. + * + * {@hide} + */ +interface IPackageManager { + void checkPackageStartable(String packageName, int userId); + @UnsupportedAppUsage(trackingBug = 171933273) + boolean isPackageAvailable(String packageName, int userId); + PackageInfo getPackageInfo(String packageName, long flags, int userId); + PackageInfo getPackageInfoVersioned(in VersionedPackage versionedPackage, + long flags, int userId); + int getPackageUid(String packageName, long flags, int userId); + int[] getPackageGids(String packageName, long flags, int userId); + + @UnsupportedAppUsage + String[] currentToCanonicalPackageNames(in String[] names); + @UnsupportedAppUsage + String[] canonicalToCurrentPackageNames(in String[] names); + + ApplicationInfo getApplicationInfo(String packageName, long flags, int userId); + + /** + * @return the target SDK for the given package name, or -1 if it cannot be retrieved + */ + int getTargetSdkVersion(String packageName); + + ActivityInfo getActivityInfo(in ComponentName className, long flags, int userId); + + boolean activitySupportsIntentAsUser(in ComponentName className, in Intent intent, + String resolvedType, int userId); + + ActivityInfo getReceiverInfo(in ComponentName className, long flags, int userId); + + ServiceInfo getServiceInfo(in ComponentName className, long flags, int userId); + + ProviderInfo getProviderInfo(in ComponentName className, long flags, int userId); + + boolean isProtectedBroadcast(String actionName); + + int checkSignatures(String pkg1, String pkg2, int userId); + + @UnsupportedAppUsage + int checkUidSignatures(int uid1, int uid2); + + List getAllPackages(); + + @UnsupportedAppUsage + String[] getPackagesForUid(int uid); + + @UnsupportedAppUsage + String getNameForUid(int uid); + String[] getNamesForUids(in int[] uids); + + @UnsupportedAppUsage + int getUidForSharedUser(String sharedUserName); + + @UnsupportedAppUsage + int getFlagsForUid(int uid); + + int getPrivateFlagsForUid(int uid); + + @UnsupportedAppUsage + boolean isUidPrivileged(int uid); + + ResolveInfo resolveIntent(in Intent intent, String resolvedType, long flags, int userId); + + ResolveInfo findPersistentPreferredActivity(in Intent intent, int userId); + + boolean canForwardTo(in Intent intent, String resolvedType, int sourceUserId, int targetUserId); + + ParceledListSlice queryIntentActivities(in Intent intent, + String resolvedType, long flags, int userId); + + ParceledListSlice queryIntentActivityOptions( + in ComponentName caller, in Intent[] specifics, + in String[] specificTypes, in Intent intent, + String resolvedType, long flags, int userId); + + ParceledListSlice queryIntentReceivers(in Intent intent, + String resolvedType, long flags, int userId); + + ResolveInfo resolveService(in Intent intent, + String resolvedType, long flags, int userId); + + ParceledListSlice queryIntentServices(in Intent intent, + String resolvedType, long flags, int userId); + + ParceledListSlice queryIntentContentProviders(in Intent intent, + String resolvedType, long flags, int userId); + + /** + * This implements getInstalledPackages via a "last returned row" + * mechanism that is not exposed in the API. This is to get around the IPC + * limit that kicks in when flags are included that bloat up the data + * returned. + */ + ParceledListSlice getInstalledPackages(long flags, in int userId); + + @EnforcePermission("GET_APP_METADATA") + @nullable ParcelFileDescriptor getAppMetadataFd(String packageName, + int userId); + + /** + * This implements getPackagesHoldingPermissions via a "last returned row" + * mechanism that is not exposed in the API. This is to get around the IPC + * limit that kicks in when flags are included that bloat up the data + * returned. + */ + ParceledListSlice getPackagesHoldingPermissions(in String[] permissions, + long flags, int userId); + + /** + * This implements getInstalledApplications via a "last returned row" + * mechanism that is not exposed in the API. This is to get around the IPC + * limit that kicks in when flags are included that bloat up the data + * returned. + */ + ParceledListSlice getInstalledApplications(long flags, int userId); + + /** + * Retrieve all applications that are marked as persistent. + * + * @return A List containing one entry for each persistent + * application. + */ + ParceledListSlice getPersistentApplications(int flags); + + ProviderInfo resolveContentProvider(String name, long flags, int userId); + + /** + * Retrieve sync information for all content providers. + * + * @param outNames Filled in with a list of the root names of the content + * providers that can sync. + * @param outInfo Filled in with a list of the ProviderInfo for each + * name in 'outNames'. + */ + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + void querySyncProviders(inout List outNames, + inout List outInfo); + + ParceledListSlice queryContentProviders( + String processName, int uid, long flags, String metaDataKey); + + InstrumentationInfo getInstrumentationInfoAsUser( + in ComponentName className, int flags, int userId); + + ParceledListSlice queryInstrumentationAsUser( + String targetPackage, int flags, int userId); + + void finishPackageInstall(int token, boolean didLaunch); + + @UnsupportedAppUsage + void setInstallerPackageName(in String targetPackage, in String installerPackageName); + + void relinquishUpdateOwnership(in String targetPackage); + + void setApplicationCategoryHint(String packageName, int categoryHint, String callerPackageName); + + /** @deprecated rawr, don't call AIDL methods directly! */ + void deletePackageAsUser(in String packageName, int versionCode, + IPackageDeleteObserver observer, int userId, int flags); + + /** + * Delete a package for a specific user. + * + * @param versionedPackage The package to delete. + * @param observer a callback to use to notify when the package deletion in finished. + * @param userId the id of the user for whom to delete the package + * @param flags - possible values: {@link #DELETE_KEEP_DATA} + */ + void deletePackageVersioned(in VersionedPackage versionedPackage, + IPackageDeleteObserver2 observer, int userId, int flags); + + /** + * Delete a package for a specific user. + * + * @param versionedPackage The package to delete. + * @param observer a callback to use to notify when the package deletion in finished. + * @param userId the id of the user for whom to delete the package + */ + void deleteExistingPackageAsUser(in VersionedPackage versionedPackage, + IPackageDeleteObserver2 observer, int userId); + + @UnsupportedAppUsage + String getInstallerPackageName(in String packageName); + + InstallSourceInfo getInstallSourceInfo(in String packageName, int userId); + + void resetApplicationPreferences(int userId); + + @UnsupportedAppUsage + ResolveInfo getLastChosenActivity(in Intent intent, + String resolvedType, int flags); + + @UnsupportedAppUsage + void setLastChosenActivity(in Intent intent, String resolvedType, int flags, + in IntentFilter filter, int match, in ComponentName activity); + + void addPreferredActivity(in IntentFilter filter, int match, + in ComponentName[] set, in ComponentName activity, int userId, boolean removeExisting); + + @UnsupportedAppUsage + void replacePreferredActivity(in IntentFilter filter, int match, + in ComponentName[] set, in ComponentName activity, int userId); + + @UnsupportedAppUsage + void clearPackagePreferredActivities(String packageName); + + @UnsupportedAppUsage + int getPreferredActivities(out List outFilters, + out List outActivities, String packageName); + + void addPersistentPreferredActivity(in IntentFilter filter, in ComponentName activity, int userId); + + void clearPackagePersistentPreferredActivities(String packageName, int userId); + + void clearPersistentPreferredActivity(in IntentFilter filter, int userId); + + void addCrossProfileIntentFilter(in IntentFilter intentFilter, String ownerPackage, + int sourceUserId, int targetUserId, int flags); + + @EnforcePermission("INTERACT_ACROSS_USERS_FULL") + boolean removeCrossProfileIntentFilter(in IntentFilter intentFilter, String ownerPackage, + int sourceUserId, int targetUserId, int flags); + + @EnforcePermission("INTERACT_ACROSS_USERS_FULL") + void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage); + + String[] setDistractingPackageRestrictionsAsUser(in String[] packageNames, int restrictionFlags, + int userId); + + String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended, + in PersistableBundle appExtras, in PersistableBundle launcherExtras, + in SuspendDialogInfo dialogInfo, int flags, String suspendingPackage, + int suspendingUserId, int targetUserId); + + String[] getUnsuspendablePackagesForUser(in String[] packageNames, int userId); + + boolean isPackageSuspendedForUser(String packageName, int userId); + + boolean isPackageQuarantinedForUser(String packageName, int userId); + + boolean isPackageStoppedForUser(String packageName, int userId); + + Bundle getSuspendedPackageAppExtras(String packageName, int userId); + + String getSuspendingPackage(String packageName, int userId); + + /** + * Backup/restore support - only the system uid may use these. + */ + byte[] getPreferredActivityBackup(int userId); + void restorePreferredActivities(in byte[] backup, int userId); + byte[] getDefaultAppsBackup(int userId); + void restoreDefaultApps(in byte[] backup, int userId); + byte[] getDomainVerificationBackup(int userId); + void restoreDomainVerification(in byte[] backup, int userId); + + /** + * Report the set of 'Home' activity candidates, plus (if any) which of them + * is the current "always use this one" setting. + */ + @UnsupportedAppUsage + ComponentName getHomeActivities(out List outHomeCandidates); + + void setHomeActivity(in ComponentName className, int userId); + + /** + * Overrides the label and icon of the component specified by the component name. The component + * must belong to the calling app. + * + * These changes will be reset on the next boot and whenever the package is updated. + * + * Only the app defined as com.android.internal.R.config_overrideComponentUiPackage is allowed + * to call this. + * + * @param componentName The component name to override the label/icon of. + * @param nonLocalizedLabel The label to be displayed. + * @param icon The icon to be displayed. + * @param userId The user id. + */ + void overrideLabelAndIcon(in ComponentName componentName, String nonLocalizedLabel, + int icon, int userId); + + /** + * Restores the label and icon of the activity specified by the component name if either has + * been overridden. The component must belong to the calling app. + * + * Only the app defined as com.android.internal.R.config_overrideComponentUiPackage is allowed + * to call this. + * + * @param componentName The component name. + * @param userId The user id. + */ + void restoreLabelAndIcon(in ComponentName componentName, int userId); + + /** + * As per {@link android.content.pm.PackageManager#setComponentEnabledSetting}. + */ + @UnsupportedAppUsage + void setComponentEnabledSetting(in ComponentName componentName, + in int newState, in int flags, int userId, String callingPackage); + + /** + * As per {@link android.content.pm.PackageManager#setComponentEnabledSettings}. + */ + void setComponentEnabledSettings(in List settings, int userId, + String callingPackage); + + /** + * As per {@link android.content.pm.PackageManager#getComponentEnabledSetting}. + */ + @UnsupportedAppUsage + int getComponentEnabledSetting(in ComponentName componentName, int userId); + + /** + * As per {@link android.content.pm.PackageManager#setApplicationEnabledSetting}. + */ + @UnsupportedAppUsage + void setApplicationEnabledSetting(in String packageName, in int newState, int flags, + int userId, String callingPackage); + + /** + * As per {@link android.content.pm.PackageManager#getApplicationEnabledSetting}. + */ + @UnsupportedAppUsage + int getApplicationEnabledSetting(in String packageName, int userId); + + /** + * Logs process start information (including APK hash) to the security log. + */ + void logAppProcessStartIfNeeded(String packageName, String processName, int uid, String seinfo, String apkFile, int pid); + + /** + * As per {@link android.content.pm.PackageManager#flushPackageRestrictionsAsUser}. + */ + void flushPackageRestrictionsAsUser(in int userId); + + /** + * Set whether the given package should be considered stopped, making + * it not visible to implicit intents that filter out stopped packages. + */ + @UnsupportedAppUsage + void setPackageStoppedState(String packageName, boolean stopped, int userId); + + /** + * Free storage by deleting LRU sorted list of cache files across + * all applications. If the currently available free storage + * on the device is greater than or equal to the requested + * free storage, no cache files are cleared. If the currently + * available storage on the device is less than the requested + * free storage, some or all of the cache files across + * all applications are deleted (based on last accessed time) + * to increase the free storage space on the device to + * the requested value. There is no guarantee that clearing all + * the cache files from all applications will clear up + * enough storage to achieve the desired value. + * @param freeStorageSize The number of bytes of storage to be + * freed by the system. Say if freeStorageSize is XX, + * and the current free storage is YY, + * if XX is less than YY, just return. if not free XX-YY number + * of bytes if possible. + * @param observer call back used to notify when + * the operation is completed + */ + @EnforcePermission("CLEAR_APP_CACHE") + void freeStorageAndNotify(in String volumeUuid, in long freeStorageSize, + int storageFlags, IPackageDataObserver observer); + + /** + * Free storage by deleting LRU sorted list of cache files across + * all applications. If the currently available free storage + * on the device is greater than or equal to the requested + * free storage, no cache files are cleared. If the currently + * available storage on the device is less than the requested + * free storage, some or all of the cache files across + * all applications are deleted (based on last accessed time) + * to increase the free storage space on the device to + * the requested value. There is no guarantee that clearing all + * the cache files from all applications will clear up + * enough storage to achieve the desired value. + * @param freeStorageSize The number of bytes of storage to be + * freed by the system. Say if freeStorageSize is XX, + * and the current free storage is YY, + * if XX is less than YY, just return. if not free XX-YY number + * of bytes if possible. + * @param pi IntentSender call back used to + * notify when the operation is completed.May be null + * to indicate that no call back is desired. + */ + @EnforcePermission("CLEAR_APP_CACHE") + void freeStorage(in String volumeUuid, in long freeStorageSize, + int storageFlags, in IntentSender pi); + + /** + * Delete all the cache files in an applications cache directory + * @param packageName The package name of the application whose cache + * files need to be deleted + * @param observer a callback used to notify when the deletion is finished. + */ + @UnsupportedAppUsage + void deleteApplicationCacheFiles(in String packageName, IPackageDataObserver observer); + + /** + * Delete all the cache files in an applications cache directory + * @param packageName The package name of the application whose cache + * files need to be deleted + * @param userId the user to delete application cache for + * @param observer a callback used to notify when the deletion is finished. + */ + void deleteApplicationCacheFilesAsUser(in String packageName, int userId, IPackageDataObserver observer); + + /** + * Clear the user data directory of an application. + * @param packageName The package name of the application whose cache + * files need to be deleted + * @param observer a callback used to notify when the operation is completed. + */ + @EnforcePermission("CLEAR_APP_USER_DATA") + void clearApplicationUserData(in String packageName, IPackageDataObserver observer, int userId); + + /** + * Clear the profile data of an application. + * @param packageName The package name of the application whose profile data + * need to be deleted + */ + void clearApplicationProfileData(in String packageName); + + /** + * Get package statistics including the code, data and cache size for + * an already installed package + * @param packageName The package name of the application + * @param userHandle Which user the size should be retrieved for + * @param observer a callback to use to notify when the asynchronous + * retrieval of information is complete. + */ + void getPackageSizeInfo(in String packageName, int userHandle, IPackageStatsObserver observer); + + /** + * Get a list of shared libraries that are available on the system. + * + * @deprecated use getSystemSharedLibraryNamesAndPaths() instead + */ + @UnsupportedAppUsage + String[] getSystemSharedLibraryNames(); + + /** + * Get a list of shared library names (key) and paths (values). + */ + Map getSystemSharedLibraryNamesAndPaths(); + + /** + * Get a list of features that are available on the system. + */ + ParceledListSlice getSystemAvailableFeatures(); + + boolean hasSystemFeature(String name, int version); + + List getInitialNonStoppedSystemPackages(); + + void enterSafeMode(); + @UnsupportedAppUsage + boolean isSafeMode(); + @UnsupportedAppUsage + boolean hasSystemUidErrors(); + + /** + * Notify the package manager that a package is going to be used and why. + * + * See PackageManager.NOTIFY_PACKAGE_USE_* for reasons. + */ + oneway void notifyPackageUse(String packageName, int reason); + + /** + * Notify the package manager that a list of dex files have been loaded. + * + * @param loadingPackageName the name of the package who performs the load + * @param classLoaderContextMap a map from file paths to dex files that have been loaded to + * the class loader context that was used to load them. + * @param loaderIsa the ISA of the loader process + */ + oneway void notifyDexLoad(String loadingPackageName, + in Map classLoaderContextMap, String loaderIsa); + + /** + * Register an application dex module with the package manager. + * The package manager will keep track of the given module for future optimizations. + * + * Dex module optimizations will disable the classpath checking at runtime. The client bares + * the responsibility to ensure that the static assumptions on classes in the optimized code + * hold at runtime (e.g. there's no duplicate classes in the classpath). + * + * Note that the package manager already keeps track of dex modules loaded with + * {@link dalvik.system.DexClassLoader} and {@link dalvik.system.PathClassLoader}. + * This can be called for an eager registration. + * + * The call might take a while and the results will be posted on the main thread, using + * the given callback. + * + * If the module is intended to be shared with other apps, make sure that the file + * permissions allow for it. + * If at registration time the permissions allow for others to read it, the module would + * be marked as a shared module which might undergo a different optimization strategy. + * (usually shared modules will generated larger optimizations artifacts, + * taking more disk space). + * + * @param packageName the package name to which the dex module belongs + * @param dexModulePath the absolute path of the dex module. + * @param isSharedModule whether or not the module is intended to be used by other apps. + * @param callback if not null, + * {@link android.content.pm.IDexModuleRegisterCallback.IDexModuleRegisterCallback#onDexModuleRegistered} + * will be called once the registration finishes. + */ + oneway void registerDexModule(in String packageName, in String dexModulePath, + in boolean isSharedModule, IDexModuleRegisterCallback callback); + + /** + * 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. + */ + boolean performDexOptMode(String packageName, boolean checkProfiles, + String targetCompilerFilter, boolean force, boolean bootComplete, String splitName); + + /** + * 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. + */ + boolean performDexOptSecondary(String packageName, + String targetCompilerFilter, boolean force); + + @EnforcePermission("MOUNT_UNMOUNT_FILESYSTEMS") + int getMoveStatus(int moveId); + + @EnforcePermission("MOUNT_UNMOUNT_FILESYSTEMS") + void registerMoveCallback(in IPackageMoveObserver callback); + @EnforcePermission("MOUNT_UNMOUNT_FILESYSTEMS") + void unregisterMoveCallback(in IPackageMoveObserver callback); + + @EnforcePermission("MOVE_PACKAGE") + int movePackage(in String packageName, in String volumeUuid); + @EnforcePermission("MOVE_PACKAGE") + int movePrimaryStorage(in String volumeUuid); + + @EnforcePermission("WRITE_SECURE_SETTINGS") + boolean setInstallLocation(int loc); + @UnsupportedAppUsage + int getInstallLocation(); + + int installExistingPackageAsUser(String packageName, int userId, int installFlags, + int installReason, in List whiteListedPermissions); + + void verifyPendingInstall(int id, int verificationCode); + void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay); + + /** @deprecated */ + void verifyIntentFilter(int id, int verificationCode, in List failedDomains); + /** @deprecated */ + int getIntentVerificationStatus(String packageName, int userId); + /** @deprecated */ + boolean updateIntentVerificationStatus(String packageName, int status, int userId); + /** @deprecated */ + ParceledListSlice getIntentFilterVerifications(String packageName); + ParceledListSlice getAllIntentFilters(String packageName); + + @EnforcePermission("PACKAGE_VERIFICATION_AGENT") + VerifierDeviceIdentity getVerifierDeviceIdentity(); + + boolean isFirstBoot(); + boolean isDeviceUpgrading(); + + /** Reflects current DeviceStorageMonitorService state */ + @UnsupportedAppUsage + boolean isStorageLow(); + + @EnforcePermission("MANAGE_USERS") + @UnsupportedAppUsage + boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, int userId); + boolean getApplicationHiddenSettingAsUser(String packageName, int userId); + + void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden); + boolean setSystemAppInstallState(String packageName, boolean installed, int userId); + + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + IPackageInstaller getPackageInstaller(); + + @EnforcePermission("DELETE_PACKAGES") + boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId); + @UnsupportedAppUsage + boolean getBlockUninstallForUser(String packageName, int userId); + + KeySet getKeySetByAlias(String packageName, String alias); + KeySet getSigningKeySet(String packageName); + boolean isPackageSignedByKeySet(String packageName, in KeySet ks); + boolean isPackageSignedByKeySetExactly(String packageName, in KeySet ks); + + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + String getPermissionControllerPackageName(); + String getSdkSandboxPackageName(); + + ParceledListSlice getInstantApps(int userId); + byte[] getInstantAppCookie(String packageName, int userId); + boolean setInstantAppCookie(String packageName, in byte[] cookie, int userId); + Bitmap getInstantAppIcon(String packageName, int userId); + boolean isInstantApp(String packageName, int userId); + + boolean setRequiredForSystemUser(String packageName, boolean systemUserApp); + + /** + * Sets whether or not an update is available. Ostensibly for instant apps + * to force exteranl resolution. + */ + @EnforcePermission("INSTALL_PACKAGES") + void setUpdateAvailable(String packageName, boolean updateAvaialble); + + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + String getServicesSystemSharedLibraryPackageName(); + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + String getSharedSystemSharedLibraryPackageName(); + + ChangedPackages getChangedPackages(int sequenceNumber, int userId); + + boolean isPackageDeviceAdminOnAnyUser(String packageName); + + int getInstallReason(String packageName, int userId); + + ParceledListSlice getSharedLibraries(in String packageName, long flags, int userId); + + ParceledListSlice getDeclaredSharedLibraries(in String packageName, long flags, int userId); + + boolean canRequestPackageInstalls(String packageName, int userId); + + void deletePreloadsFileCache(); + + ComponentName getInstantAppResolverComponent(); + + ComponentName getInstantAppResolverSettingsComponent(); + + ComponentName getInstantAppInstallerComponent(); + + @EnforcePermission("ACCESS_INSTANT_APPS") + String getInstantAppAndroidId(String packageName, int userId); + + IArtManager getArtManager(); + + void setHarmfulAppWarning(String packageName, CharSequence warning, int userId); + + CharSequence getHarmfulAppWarning(String packageName, int userId); + + boolean hasSigningCertificate(String packageName, in byte[] signingCertificate, int flags); + + boolean hasUidSigningCertificate(int uid, in byte[] signingCertificate, int flags); + + String getDefaultTextClassifierPackageName(); + + String getSystemTextClassifierPackageName(); + + String getAttentionServicePackageName(); + + String getRotationResolverPackageName(); + + String getWellbeingPackageName(); + + String getAppPredictionServicePackageName(); + + String getSystemCaptionsServicePackageName(); + + String getSetupWizardPackageName(); + + String getIncidentReportApproverPackageName(); + + boolean isPackageStateProtected(String packageName, int userId); + + void sendDeviceCustomizationReadyBroadcast(); + + List getInstalledModules(int flags); + + ModuleInfo getModuleInfo(String packageName, int flags); + + int getRuntimePermissionsVersion(int userId); + + void setRuntimePermissionsVersion(int version, int userId); + + void notifyPackagesReplacedReceived(in String[] packages); + + void requestPackageChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId); + + IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage, + String featureId, int userId); + + //------------------------------------------------------------------------ + // + // The following binder interfaces have been moved to IPermissionManager + // + //------------------------------------------------------------------------ + + //------------------------------------------------------------------------ + // We need to keep these in IPackageManager for app compatibility + //------------------------------------------------------------------------ + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + String[] getAppOpPermissionPackages(String permissionName, int userId); + + @UnsupportedAppUsage + PermissionGroupInfo getPermissionGroupInfo(String name, int flags); + + @UnsupportedAppUsage + boolean addPermission(in PermissionInfo info); + + @UnsupportedAppUsage + boolean addPermissionAsync(in PermissionInfo info); + + @UnsupportedAppUsage + void removePermission(String name); + + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + int checkPermission(String permName, String pkgName, int userId); + + @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + void grantRuntimePermission(String packageName, String permissionName, int userId); + + //------------------------------------------------------------------------ + // We need to keep these in IPackageManager for convenience in splitting + // out the permission manager. This should be cleaned up, but, will require + // a large change that modifies many repos. + //------------------------------------------------------------------------ + int checkUidPermission(String permName, int uid); + + void setMimeGroup(String packageName, String group, in List mimeTypes); + + String getSplashScreenTheme(String packageName, int userId); + + void setSplashScreenTheme(String packageName, String themeName, int userId); + + int getUserMinAspectRatio(String packageName, int userId); + + @EnforcePermission("INSTALL_PACKAGES") + void setUserMinAspectRatio(String packageName, int userId, int aspectRatio); + + List getMimeGroup(String packageName, String group); + + boolean isAutoRevokeWhitelisted(String packageName); + + void makeProviderVisible(int recipientAppId, String visibleAuthority); + + @EnforcePermission("MAKE_UID_VISIBLE") + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + + ".permission.MAKE_UID_VISIBLE)") + void makeUidVisible(int recipientAppId, int visibleUid); + + IBinder getHoldLockToken(); + + void holdLock(in IBinder token, in int durationMs); + + PackageManager.Property getPropertyAsUser(String propertyName, String packageName, + String className, int userId); + ParceledListSlice queryProperty(String propertyName, int componentType); + + void setKeepUninstalledPackages(in List packageList); + + boolean[] canPackageQuery(String sourcePackageName, in String[] targetPackageNames, int userId); + + boolean waitForHandler(long timeoutMillis, boolean forBackgroundHandler); + + void registerPackageMonitorCallback(IRemoteCallback callback, int userId); + + void unregisterPackageMonitorCallback(IRemoteCallback callback); + + ArchivedPackageParcel getArchivedPackage(in String packageName, int userId); + + Bitmap getArchivedAppIcon(String packageName, in UserHandle user, String callingPackageName); + + boolean isAppArchivable(String packageName, in UserHandle user); + + @EnforcePermission("GET_APP_METADATA") + int getAppMetadataSource(String packageName, int userId); + + ComponentName getDomainVerificationAgent(); + + void scanFast(String path); +} diff --git a/aosp/frameworks/base/services/core/java/com/android/server/pm/AppIdSettingMap.java b/aosp/frameworks/base/services/core/java/com/android/server/pm/AppIdSettingMap.java new file mode 100644 index 0000000000000000000000000000000000000000..5650b466e139bf1440cc57fae61c9d272c1f81be --- /dev/null +++ b/aosp/frameworks/base/services/core/java/com/android/server/pm/AppIdSettingMap.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.os.Process; +import android.util.Log; + +import com.android.server.utils.SnapshotCache; +import com.android.server.utils.WatchedArrayList; +import com.android.server.utils.WatchedSparseArray; +import com.android.server.utils.Watcher; + +/** + * A wrapper over {@link WatchedArrayList} that tracks the current (app ID -> SettingBase) mapping + * for non-system apps. Also tracks system app settings in an {@link WatchedSparseArray}. + */ +final class AppIdSettingMap { + /** + * We use an ArrayList instead of an SparseArray for non system apps because the number of apps + * might be big, and only ArrayList gives us a constant lookup time. For a given app ID, the + * index to the corresponding SettingBase object is (appId - FIRST_APPLICATION_ID). If an app ID + * doesn't exist (i.e., app is not installed), we fill the corresponding entry with null. + */ + private final WatchedArrayList mNonSystemSettings; + private final SnapshotCache> mNonSystemSettingsSnapshot; + private final WatchedSparseArray mSystemSettings; + private final SnapshotCache> mSystemSettingsSnapshot; + private int mFirstAvailableAppId = Process.FIRST_APPLICATION_UID; + private final AppIdSettingMapHelper settingHelper = new AppIdSettingMapHelper(); + + AppIdSettingMap() { + mNonSystemSettings = new WatchedArrayList<>(); + mNonSystemSettingsSnapshot = new SnapshotCache.Auto<>( + mNonSystemSettings, mNonSystemSettings, "AppIdSettingMap.mNonSystemSettings"); + mSystemSettings = new WatchedSparseArray<>(); + mSystemSettingsSnapshot = new SnapshotCache.Auto<>( + mSystemSettings, mSystemSettings, "AppIdSettingMap.mSystemSettings"); + } + + AppIdSettingMap(AppIdSettingMap orig) { + mNonSystemSettings = orig.mNonSystemSettingsSnapshot.snapshot(); + mNonSystemSettingsSnapshot = new SnapshotCache.Sealed<>(); + mSystemSettings = orig.mSystemSettingsSnapshot.snapshot(); + mSystemSettingsSnapshot = new SnapshotCache.Sealed<>(); + } + + /** Returns true if the requested AppID was valid and not already registered. */ + public boolean registerExistingAppId(int appId, SettingBase setting, Object name) { + if (settingHelper.isAssignAppUid(appId)) { + if (!settingHelper.addAssignAppUid(appId, setting)) { + PackageManagerService.reportSettingsProblem(Log.ERROR, + "Adding duplicate user id: " + appId + + " name=" + name); + return false; + } + } + + if (appId >= Process.FIRST_APPLICATION_UID) { + int size = mNonSystemSettings.size(); + final int index = appId - Process.FIRST_APPLICATION_UID; + // fill the array until our index becomes valid + while (index >= size) { + mNonSystemSettings.add(null); + size++; + } + if (mNonSystemSettings.get(index) != null) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Adding duplicate app id: " + appId + + " name=" + name); + return false; + } + mNonSystemSettings.set(index, setting); + } else { + if (mSystemSettings.get(appId) != null) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Adding duplicate shared id: " + appId + + " name=" + name); + return false; + } + mSystemSettings.put(appId, setting); + } + return true; + } + + public SettingBase getSetting(int appId) { + if (settingHelper.isAssignAppUid(appId)) { + return (SettingBase)settingHelper.getAssignUidObject(appId); + } + if (appId >= Process.FIRST_APPLICATION_UID) { + final int size = mNonSystemSettings.size(); + final int index = appId - Process.FIRST_APPLICATION_UID; + return index < size ? mNonSystemSettings.get(index) : null; + } else { + return mSystemSettings.get(appId); + } + } + + public void removeSetting(int appId) { + if (settingHelper.isAssignAppUid(appId)) { + settingHelper.removeAssignAppUid(appId); + return; + } + + if (appId >= Process.FIRST_APPLICATION_UID) { + final int size = mNonSystemSettings.size(); + final int index = appId - Process.FIRST_APPLICATION_UID; + if (index < size) { + mNonSystemSettings.set(index, null); + } + } else { + mSystemSettings.remove(appId); + } + setFirstAvailableAppId(appId + 1); + } + + // This should be called (at least) whenever an application is removed + private void setFirstAvailableAppId(int uid) { + if (uid > mFirstAvailableAppId) { + mFirstAvailableAppId = uid; + } + } + + public void replaceSetting(int appId, SettingBase setting) { + if (settingHelper.isAssignAppUid(appId)) { + settingHelper.replaceAssignAppUid(appId, setting); + return; + } + + if (appId >= Process.FIRST_APPLICATION_UID) { + final int size = mNonSystemSettings.size(); + final int index = appId - Process.FIRST_APPLICATION_UID; + if (index < size) { + mNonSystemSettings.set(index, setting); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: calling replaceAppIdLpw to" + + " replace SettingBase at appId=" + appId + + " but nothing is replaced."); + } + } else { + mSystemSettings.put(appId, setting); + } + } + + /** Returns a new AppID or -1 if we could not find an available AppID to assign */ + public int acquireAndRegisterNewAppId(SettingBase obj) { + int uid = settingHelper.newAssignAppUid(obj); + if (uid > 0) { + return uid; + } + + final int size = mNonSystemSettings.size(); + for (int i = mFirstAvailableAppId - Process.FIRST_APPLICATION_UID; i < size; i++) { + if (mNonSystemSettings.get(i) == null) { + mNonSystemSettings.set(i, obj); + return Process.FIRST_APPLICATION_UID + i; + } + } + + // None left? + if (size > (settingHelper.getLastApplicationUid() - Process.FIRST_APPLICATION_UID)) { + return -1; + } + + mNonSystemSettings.add(obj); + return Process.FIRST_APPLICATION_UID + size; + } + + public AppIdSettingMap snapshot() { + return new AppIdSettingMap(this); + } + + public void registerObserver(Watcher observer) { + mNonSystemSettings.registerObserver(observer); + mSystemSettings.registerObserver(observer); + } +} diff --git a/aosp/frameworks/base/services/core/java/com/android/server/pm/AppIdSettingMapHelper.java b/aosp/frameworks/base/services/core/java/com/android/server/pm/AppIdSettingMapHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..3cf1ca79ef73199cb958753efe06e3ed808cfdce --- /dev/null +++ b/aosp/frameworks/base/services/core/java/com/android/server/pm/AppIdSettingMapHelper.java @@ -0,0 +1,205 @@ +/* ****************************************************************************** + * Copyright Notice: + * Copyright 2023-2024, Huawei Technologies Co., Ltd. ALL Rights Reserved. + * + * Warning: This computer software sourcecode is protected by copyright law + * and international treaties. Unauthorized reproduction or distribution + * of this sourcecode, or any portion of it, may result in severe civil and + * criminal penalties, and will be prosecuted to the maximum extent + * possible under the law. + * ******************************************************************************* + */ + +package com.android.server.pm; + +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.os.FileObserver; +import android.os.Process; +import android.util.Log; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +import org.xmlpull.v1.XmlPullParserFactory; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +public class AppIdSettingMapHelper { + + private static final String TAG = "AppIdSettingMapHelper"; + private static final int FIRST_ASSIGN_APP_UID = 16000; + private final Object mLock = new Object(); + private final String APP_CONFIG_PATH = "/data/local/tmp/share_apps.xml"; + private Map mAssignAppInfo = new HashMap<>(); + private Map mAssignUids = new HashMap<>(); + private boolean mXmlHasRead = false; + private FileObserver mFileObserver; + + private void startFileObservation() { + File xmlFile = new File(APP_CONFIG_PATH); + mFileObserver = new FileObserver(xmlFile.getPath(), FileObserver.ALL_EVENTS) { + @Override + public void onEvent(int event, String path) { + switch (event) { + case FileObserver.MODIFY: + parseAssignAppXml(); + Log.i(TAG, "share_apps.xml has been modify."); + break; + case FileObserver.DELETE: + case FileObserver.DELETE_SELF: + synchronized (mLock) { + mXmlHasRead = false; + } + Log.i(TAG, "share_apps.xml has been deleted."); + break; + default: + break; + } + } + }; + Log.i(TAG, "Start watching share_apps.xml."); + mFileObserver.startWatching(); + } + + protected static int getFirstAvailableUid() { + return Process.FIRST_APPLICATION_UID; + } + + public int getLastApplicationUid() { + return FIRST_ASSIGN_APP_UID - 1; + } + + public AppIdSettingMapHelper() { + checkXmlFile(); + } + + public boolean isLastApplicationUid(int mFirstAvailableUid, ArrayList mUserIds, int sum) { + // If mFirstAvailableUid increase to Process.LAST_APPLICATION_UID, + // just looking for available uid from index 0. + return (mFirstAvailableUid > Process.LAST_APPLICATION_UID) + || (mFirstAvailableUid == Process.LAST_APPLICATION_UID && mUserIds.get(sum - 1) != null); + } + + private void addAssignAppInfo(String packageName, int uid) { + if (isAssignAppUid(uid)) { + mAssignAppInfo.put(packageName, uid); + } + } + + private void checkXmlFile() { + File file = new File(APP_CONFIG_PATH); + if (!file.exists()) { + Log.i(TAG, "app config file not find."); + return; + } + synchronized (mLock) { + if (mXmlHasRead) { + return; + } + } + parseAssignAppXml(); + // 监听xml是否变化 + startFileObservation(); + } + + private void parseAssignAppXml() { + synchronized (mLock) { + FileReader fileReader = null; + try { + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + factory.setNamespaceAware(true); + XmlPullParser parser = factory.newPullParser(); + File file = new File(APP_CONFIG_PATH); + fileReader = new FileReader(file); + parser.setInput(fileReader); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType != XmlPullParser.START_TAG) { + eventType = parser.next(); + continue; + } + String tag = parser.getName(); + if (tag.equals("package")) { + String packageName = parser.getAttributeValue(null, "name"); + int uid = Integer.parseInt(parser.getAttributeValue(null, "userId")); + addAssignAppInfo(packageName, uid); + Log.i(TAG, "package = " + packageName + " uid = " + uid); + } + eventType = parser.next(); + } + } catch (XmlPullParserException e) { + Log.e(TAG, "xml parse exception," + e); + } catch (IOException e) { + Log.e(TAG, "xml parse IOException," + e); + } catch (Exception e) { + Log.e(TAG, "exception," + e); + } finally { + if (fileReader != null) { + try { + fileReader.close(); + } catch (IOException e) { + Log.e(TAG, "filereader close exception," + e); + } + } + mXmlHasRead = true; + } + } + } + + public boolean isAssignAppUid(int uid) { + if (uid >= FIRST_ASSIGN_APP_UID && uid <= Process.LAST_APPLICATION_UID) { + return true; + } else { + return false; + } + } + + public int newAssignAppUid(Object obj) { + checkXmlFile(); + if (mAssignAppInfo.isEmpty()) { + return -1; + } + if (obj == null || !(obj instanceof PackageSetting)) { + return -1; + } + PackageSetting p = (PackageSetting) obj; + String pkgName = p.getPackageName(); + int uid = mAssignAppInfo.getOrDefault(pkgName, -1); + if (uid == -1) { + return -1; + } + if (mAssignUids.containsKey(uid)) { + return -1; + } + mAssignUids.put(uid, obj); + Log.i(TAG, "Create uid " + uid + "success."); + return uid; + } + + public boolean addAssignAppUid(int uid, Object obj) { + if (mAssignUids.getOrDefault(uid, null) != null) { + return false; + } + mAssignUids.put(uid, obj); + return true; + } + + public void removeAssignAppUid(int uid) { + mAssignUids.remove(uid); + Log.i(TAG, "Remove uid " + uid + "success."); + } + + public Object getAssignUidObject(int uid) { + return mAssignUids.getOrDefault(uid, null); + } + + public void replaceAssignAppUid(int uid, Object obj) { + mAssignUids.put(uid, obj); + } +} + \ No newline at end of file diff --git a/aosp/frameworks/base/services/core/java/com/android/server/pm/DeletePackageHelper.java b/aosp/frameworks/base/services/core/java/com/android/server/pm/DeletePackageHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..63fa99510046ab4b0fbfdea3f4baf4a0085f83ff --- /dev/null +++ b/aosp/frameworks/base/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -0,0 +1,1053 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.Manifest.permission.CONTROL_KEYGUARD; +import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; +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.DELETE_SUCCEEDED; +import static android.content.pm.PackageManager.MATCH_KNOWN_PACKAGES; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; +import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; +import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE; +import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY; +import static com.android.server.pm.PackageManagerService.PACKAGE_SCHEME; +import static com.android.server.pm.PackageManagerService.TAG; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ApplicationExitInfo; +import android.app.ApplicationPackageManager; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.Flags; +import android.content.pm.IPackageDeleteObserver2; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageManager; +import android.content.pm.SharedLibraryInfo; +import android.content.pm.UserInfo; +import android.content.pm.UserProperties; +import android.content.pm.VersionedPackage; +import android.net.Uri; +import android.os.Binder; +import android.os.Process; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; +import android.text.TextUtils; +import android.util.ArraySet; +import android.util.EventLog; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import android.util.SparseBooleanArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.Preconditions; +import com.android.server.pm.Installer.InstallerException; +import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.ArchiveState; +import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.pkg.PackageUserState; +import com.android.server.wm.ActivityTaskManagerInternal; + +import dalvik.system.VMRuntime; + +/** + * Deletes a package. Uninstall if installed, or at least deletes the base directory if it's called + * from a failed installation. Fixes user state after deletion. + * Handles special treatments to system apps. + * Relies on RemovePackageHelper to clear internal data structures and remove app data. + */ +final class DeletePackageHelper { + private static final boolean DEBUG_CLEAN_APKS = false; + // ------- apps on sdcard specific code ------- + private static final boolean DEBUG_SD_INSTALL = false; + + private final PackageManagerService mPm; + private final UserManagerInternal mUserManagerInternal; + private final RemovePackageHelper mRemovePackageHelper; + private final BroadcastHelper mBroadcastHelper; + + // TODO(b/198166813): remove PMS dependency + DeletePackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper, + BroadcastHelper broadcastHelper) { + mPm = pm; + mUserManagerInternal = mPm.mInjector.getUserManagerInternal(); + mRemovePackageHelper = removePackageHelper; + mBroadcastHelper = broadcastHelper; + } + + /** + * This method is an internal method that could be invoked either + * to delete an installed package or to clean up a failed installation. + * After deleting an installed package, a broadcast is sent to notify any + * listeners that the package has been removed. For cleaning up a failed + * installation, the broadcast is not necessary since the package's + * installation wouldn't have sent the initial broadcast either + * The key steps in deleting a package are + * deleting the package information in internal structures like mPackages, + * deleting the packages base directories through installd + * updating mSettings to reflect current status + * persisting settings for later use + * sending a broadcast if necessary + * + * @param removedBySystem A boolean to indicate the package was removed automatically without + * the user-initiated action. + */ + public int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags, + boolean removedBySystem) { + final PackageRemovedInfo info = new PackageRemovedInfo(); + final boolean res; + + final int removeUser = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0 + ? UserHandle.USER_ALL : userId; + + try { + mPm.mInstaller.cleanAppLink(packageName, deleteFlags); + } catch (InstallerException e) { + Slog.w(TAG, String.valueOf(e)); + } + + final PackageSetting uninstalledPs; + final PackageSetting disabledSystemPs; + final AndroidPackage pkg; + + // for the uninstall-updates case and restricted profiles, remember the per- + // user handle installed state + int[] allUsers; + final int freezeUser; + final SparseArray priorUserStates; + + final boolean isInstallerPackage; + /** enabled state of the uninstalled application */ + synchronized (mPm.mLock) { + final Computer computer = mPm.snapshotComputer(); + uninstalledPs = mPm.mSettings.getPackageLPr(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.getVersionCode() != versionCode) { + Slog.w(TAG, "Not removing package " + packageName + " with versionCode " + + uninstalledPs.getVersionCode() + " != " + versionCode); + return PackageManager.DELETE_FAILED_INTERNAL_ERROR; + } + + if (PackageManagerServiceUtils.isUpdatedSystemApp(uninstalledPs) + && ((deleteFlags & PackageManager.DELETE_SYSTEM_APP) == 0)) { + UserInfo userInfo = mUserManagerInternal.getUserInfo(userId); + if (userInfo == null || (!userInfo.isAdmin() && !mUserManagerInternal.getUserInfo( + mUserManagerInternal.getProfileParentId(userId)).isAdmin())) { + Slog.w(TAG, "Not removing package " + packageName + + " as only admin user (or their profile) may downgrade system apps"); + EventLog.writeEvent(0x534e4554, "170646036", -1, packageName); + return PackageManager.DELETE_FAILED_USER_RESTRICTED; + } + } + + disabledSystemPs = mPm.mSettings.getDisabledSystemPkgLPr(packageName); + // 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 = mPm.mPackages.get(packageName); + + allUsers = mUserManagerInternal.getUserIds(); + + if (pkg != null) { + SharedLibraryInfo libraryInfo = null; + if (pkg.getStaticSharedLibraryName() != null) { + libraryInfo = computer.getSharedLibraryInfo(pkg.getStaticSharedLibraryName(), + pkg.getStaticSharedLibraryVersion()); + } else if (pkg.getSdkLibraryName() != null) { + libraryInfo = computer.getSharedLibraryInfo(pkg.getSdkLibraryName(), + pkg.getSdkLibVersionMajor()); + } + + if (libraryInfo != null) { + boolean flagSdkLibIndependence = Flags.sdkLibIndependence(); + for (int currUserId : allUsers) { + if (removeUser != UserHandle.USER_ALL && removeUser != currUserId) { + continue; + } + var libClientPackagesPair = computer.getPackagesUsingSharedLibrary( + libraryInfo, MATCH_KNOWN_PACKAGES, Process.SYSTEM_UID, currUserId); + var libClientPackages = libClientPackagesPair.first; + var libClientOptional = libClientPackagesPair.second; + // We by default don't allow removing a package if the host lib is still be + // used by other client packages + boolean allowLibIndependence = false; + // Only when the sdkLibIndependence flag is enabled we will respect the + // "optional" attr in uses-sdk-library. Only allow to remove sdk-lib host + // package if no required clients depend on it + if ((pkg.getSdkLibraryName() != null) + && !ArrayUtils.isEmpty(libClientPackages) + && !ArrayUtils.isEmpty(libClientOptional) + && (libClientPackages.size() == libClientOptional.size()) + && flagSdkLibIndependence) { + allowLibIndependence = true; + for (int i = 0; i < libClientPackages.size(); i++) { + boolean usesSdkLibOptional = libClientOptional.get(i); + if (!usesSdkLibOptional) { + allowLibIndependence = false; + break; + } + } + } + if (!ArrayUtils.isEmpty(libClientPackages) && !allowLibIndependence) { + Slog.w(TAG, "Not removing package " + pkg.getManifestPackageName() + + " hosting lib " + libraryInfo.getName() + " version " + + libraryInfo.getLongVersion() + " used by " + libClientPackages + + " for user " + currUserId); + return PackageManager.DELETE_FAILED_USED_SHARED_LIBRARY; + } + } + } + } + + info.mOrigUsers = uninstalledPs.queryInstalledUsers(allUsers, true); + + if (PackageManagerServiceUtils.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; + priorUserStates = new SparseArray<>(); + for (int i = 0; i < allUsers.length; i++) { + PackageUserState userState = uninstalledPs.readUserState(allUsers[i]); + priorUserStates.put(allUsers[i], + new TempUserState(userState.getEnabledState(), + userState.getLastDisableAppCaller(), userState.isInstalled())); + } + } else { + freezeUser = removeUser; + priorUserStates = null; + } + + isInstallerPackage = mPm.mSettings.isInstallerPackage(packageName); + } + + synchronized (mPm.mInstallLock) { + if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageX: pkg=" + packageName + " user=" + userId); + try (PackageFreezer freezer = mPm.freezePackageForDelete(packageName, freezeUser, + deleteFlags, "deletePackageX", ApplicationExitInfo.REASON_OTHER)) { + res = deletePackageLIF(packageName, UserHandle.of(removeUser), true, allUsers, + deleteFlags | PackageManager.DELETE_CHATTY, info, true); + } + if (res && pkg != null) { + final boolean packageInstalledForSomeUsers; + synchronized (mPm.mLock) { + packageInstalledForSomeUsers = mPm.mPackages.get(pkg.getPackageName()) != null; + } + mPm.mInstantAppRegistry.onPackageUninstalled(pkg, uninstalledPs, + info.mRemovedUsers, packageInstalledForSomeUsers); + } + synchronized (mPm.mLock) { + if (res) { + mPm.updateSequenceNumberLP(uninstalledPs, info.mRemovedUsers); + mPm.updateInstantAppInstallerLocked(packageName); + } + } + ApplicationPackageManager.invalidateGetPackagesForUidCache(); + } + + if (res) { + final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0; + final boolean isArchived = (deleteFlags & PackageManager.DELETE_ARCHIVE) != 0; + mBroadcastHelper.sendPackageRemovedBroadcasts(info, mPm, killApp, + removedBySystem, isArchived); + mBroadcastHelper.sendSystemPackageUpdatedBroadcasts(info); + PackageMetrics.onUninstallSucceeded(info, deleteFlags, removeUser); + } + + // 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(); + + // Delete the resources here after sending the broadcast to let + // other processes clean up before deleting resources. + synchronized (mPm.mInstallLock) { + if (info.mArgs != null) { + mRemovePackageHelper.cleanUpResources(info.mArgs.getPackageName(), + info.mArgs.getCodeFile(), info.mArgs.getInstructionSets()); + } + + boolean reEnableStub = false; + + if (priorUserStates != null) { + synchronized (mPm.mLock) { + PackageSetting pkgSetting = mPm.getPackageSettingForMutation(packageName); + if (pkgSetting != null) { + AndroidPackage aPkg = pkgSetting.getPkg(); + boolean pkgEnabled = aPkg != null && aPkg.isEnabled(); + for (int i = 0; i < allUsers.length; i++) { + TempUserState priorUserState = priorUserStates.get(allUsers[i]); + int enabledState = priorUserState.enabledState; + pkgSetting.setEnabled(enabledState, allUsers[i], + priorUserState.lastDisableAppCaller); + if (!reEnableStub && priorUserState.installed + && ( + (enabledState == COMPONENT_ENABLED_STATE_DEFAULT && pkgEnabled) + || enabledState == COMPONENT_ENABLED_STATE_ENABLED)) { + reEnableStub = true; + } + } + } else { + // This should not happen. If priorUserStates != null, we are uninstalling + // an update of a system app. In that case, mPm.mSettings.getPackageLpr() + // should return a non-null value for the target packageName because + // restoreDisabledSystemPackageLIF() is called during deletePackageLIF(). + Slog.w(TAG, "Missing PackageSetting after uninstalling the update for" + + " system app: " + packageName + ". This should not happen."); + } + mPm.mSettings.writeAllUsersPackageRestrictionsLPr(); + } + } + + final AndroidPackage stubPkg = + (disabledSystemPs == null) ? null : disabledSystemPs.getPkg(); + if (stubPkg != null && stubPkg.isStub()) { + final PackageSetting stubPs; + synchronized (mPm.mLock) { + stubPs = mPm.mSettings.getPackageLPr(stubPkg.getPackageName()); + } + + if (stubPs != null) { + if (reEnableStub) { + if (DEBUG_COMPRESSION) { + Slog.i(TAG, "Enabling system stub after removal; pkg: " + + stubPkg.getPackageName()); + } + mPm.enableCompressedPackage(stubPkg, stubPs); + } else if (DEBUG_COMPRESSION) { + Slog.i(TAG, "System stub disabled for all users, leaving uncompressed " + + "after removal; pkg: " + stubPkg.getPackageName()); + } + } + } + } + + if (res && isInstallerPackage) { + final PackageInstallerService packageInstallerService = + mPm.mInjector.getPackageInstallerService(); + packageInstallerService.onInstallerPackageDeleted(uninstalledPs.getAppId(), removeUser); + } + + return res ? DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR; + } + + /** Deletes dexopt artifacts for the given package*/ + private void deleteArtDexoptArtifacts(String packageName) { + try (PackageManagerLocal.FilteredSnapshot filteredSnapshot = + PackageManagerServiceUtils.getPackageManagerLocal() + .withFilteredSnapshot()) { + try { + DexOptHelper.getArtManagerLocal().deleteDexoptArtifacts( + filteredSnapshot, packageName); + } catch (IllegalArgumentException | IllegalStateException e) { + Slog.w(TAG, e.toString()); + } + } + } + + /* + * This method handles package deletion in general + */ + @GuardedBy("mPm.mInstallLock") + public boolean deletePackageLIF(@NonNull String packageName, UserHandle user, + boolean deleteCodeAndResources, @NonNull int[] allUserHandles, int flags, + @NonNull PackageRemovedInfo outInfo, boolean writeSettings) { + final DeletePackageAction action; + synchronized (mPm.mLock) { + final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); + if (ps == null) { + if (DEBUG_REMOVE) { + Slog.d(TAG, "Attempted to remove non-existent package " + packageName); + } + return false; + } + final PackageSetting disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(ps); + if (PackageManagerServiceUtils.isSystemApp(ps) + && mPm.checkPermission(CONTROL_KEYGUARD, packageName, UserHandle.USER_SYSTEM) + == PERMISSION_GRANTED) { + Slog.w(TAG, "Attempt to delete keyguard system package " + packageName); + return false; + } + action = mayDeletePackageLocked(outInfo, ps, disabledPs, flags, user); + } + if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user); + if (null == action) { + if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: action was null"); + return false; + } + + try { + executeDeletePackageLIF(action, packageName, deleteCodeAndResources, + allUserHandles, writeSettings); + } catch (SystemDeleteException e) { + if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: system deletion failure", e); + return false; + } + return true; + } + + /** + * @return a {@link DeletePackageAction} if the provided package and related state may be + * deleted, {@code null} otherwise. + */ + @Nullable + public static DeletePackageAction mayDeletePackageLocked(@NonNull PackageRemovedInfo outInfo, + PackageSetting ps, @Nullable PackageSetting disabledPs, + int flags, UserHandle user) { + if (ps == null) { + return null; + } + if (PackageManagerServiceUtils.isSystemApp(ps)) { + final boolean deleteSystem = (flags & PackageManager.DELETE_SYSTEM_APP) != 0; + final boolean deleteAllUsers = + user == null || user.getIdentifier() == UserHandle.USER_ALL; + if ((!deleteSystem || deleteAllUsers) && disabledPs == null) { + Slog.w(TAG, "Attempt to delete unknown system package " + + ps.getPkg().getPackageName()); + return null; + } + // Confirmed if the system package has been updated + // An updated system app can be deleted. This will also have to restore + // the system pkg from system partition reader + } + return new DeletePackageAction(ps, disabledPs, outInfo, flags, user); + } + + public void executeDeletePackage(DeletePackageAction action, String packageName, + boolean deleteCodeAndResources, @NonNull int[] allUserHandles, boolean writeSettings) + throws SystemDeleteException { + synchronized (mPm.mInstallLock) { + executeDeletePackageLIF(action, packageName, deleteCodeAndResources, allUserHandles, + writeSettings); + } + } + + /** Deletes a package. Only throws when install of a disabled package fails. */ + @GuardedBy("mPm.mInstallLock") + private void executeDeletePackageLIF(DeletePackageAction action, + String packageName, boolean deleteCodeAndResources, + @NonNull int[] allUserHandles, boolean writeSettings) throws SystemDeleteException { + final PackageSetting ps = action.mDeletingPs; + final PackageRemovedInfo outInfo = action.mRemovedInfo; + final UserHandle user = action.mUser; + final int flags = action.mFlags; + final boolean systemApp = PackageManagerServiceUtils.isSystemApp(ps); + + // We need to get the permission state before package state is (potentially) destroyed. + final SparseBooleanArray hadSuspendAppsPermission = new SparseBooleanArray(); + for (int userId : allUserHandles) { + hadSuspendAppsPermission.put(userId, mPm.checkPermission( + Manifest.permission.SUSPEND_APPS, packageName, userId) == PERMISSION_GRANTED); + } + + final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier(); + // Remember which users are affected, before the installed states are modified + outInfo.mRemovedUsers = (systemApp || userId == UserHandle.USER_ALL) + ? ps.queryUsersInstalledOrHasData(allUserHandles) + : new int[]{userId}; + outInfo.populateBroadcastUsers(ps); + outInfo.mDataRemoved = (flags & PackageManager.DELETE_KEEP_DATA) == 0; + outInfo.mRemovedPackage = ps.getPackageName(); + outInfo.mInstallerPackageName = ps.getInstallSource().mInstallerPackageName; + outInfo.mIsStaticSharedLib = + ps.getPkg() != null && ps.getPkg().getStaticSharedLibraryName() != null; + outInfo.mIsExternal = ps.isExternalStorage(); + outInfo.mRemovedPackageVersionCode = ps.getVersionCode(); + + if ((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0) + && userId != UserHandle.USER_ALL) { + // The caller is asking that the package only be deleted for a single + // user. To do this, we just mark its uninstalled state and delete + // its data. If this is a system app, we only allow this to happen if + // they have set the special DELETE_SYSTEM_APP which requests different + // semantics than normal for uninstalling system apps. + final boolean clearPackageStateAndReturn; + synchronized (mPm.mLock) { + markPackageUninstalledForUserLPw(ps, user, flags); + if (!systemApp) { + // Do not uninstall the APK if an app should be cached + boolean keepUninstalledPackage = + mPm.shouldKeepUninstalledPackageLPr(packageName); + if (ps.isInstalledOnAnyOtherUser( + mUserManagerInternal.getUserIds(), userId) || keepUninstalledPackage) { + // Other users still have this package installed, so all + // we need to do is clear this user's data and save that + // it is uninstalled. + if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users"); + clearPackageStateAndReturn = true; + } else { + if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete"); + mPm.mSettings.writeKernelMappingLPr(ps); + clearPackageStateAndReturn = false; + } + } else { + // This is a system app, so we assume that the + // other users still have this package installed, so all + // we need to do is clear this user's data and save that + // it is uninstalled. + if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app"); + clearPackageStateAndReturn = true; + } + } + if (clearPackageStateAndReturn) { + mRemovePackageHelper.clearPackageStateForUserLIF(ps, userId, flags); + // Legacy behavior to report appId as UID here. + // The final broadcasts will contain a per-user UID. + outInfo.mUid = ps.getAppId(); + outInfo.mIsAppIdRemoved = true; + mPm.scheduleWritePackageRestrictions(user); + return; + } + } + + // TODO(b/109941548): break reasons for ret = false out into mayDelete method + if (systemApp) { + if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.getPackageName()); + // When an updated system application is deleted we delete the existing resources + // as well and fall back to existing code in system partition + deleteInstalledSystemPackage(action, allUserHandles, writeSettings); + mPm.restoreDisabledSystemPackageLIF(action, allUserHandles, writeSettings); + } else { + if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.getPackageName()); + if (ps.isIncremental()) { + // Explicitly delete dexopt artifacts for incremental app because the + // artifacts are not stored in the same directory as the APKs + deleteArtDexoptArtifacts(packageName); + } + deleteInstalledPackageLIF(ps, userId, deleteCodeAndResources, flags, allUserHandles, + outInfo, writeSettings); + } + + // If the package removed had SUSPEND_APPS, unset any restrictions that might have been in + // place for all affected users. + final Computer snapshot = mPm.snapshotComputer(); + for (final int affectedUserId : outInfo.mRemovedUsers) { + if (hadSuspendAppsPermission.get(affectedUserId)) { + mPm.unsuspendForSuspendingPackage(snapshot, packageName, affectedUserId); + mPm.removeAllDistractingPackageRestrictions(snapshot, affectedUserId); + } + } + + // Take a note whether we deleted the package for all users + synchronized (mPm.mLock) { + outInfo.mRemovedForAllUsers = mPm.mPackages.get(ps.getPackageName()) == null; + } + } + + @GuardedBy("mPm.mInstallLock") + private void deleteInstalledPackageLIF(PackageSetting ps, int userId, + boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles, + @NonNull PackageRemovedInfo outInfo, boolean writeSettings) { + synchronized (mPm.mLock) { + // Since the package is being deleted in all users, report appId as the uid + outInfo.mUid = ps.getAppId(); + outInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList( + mPm.snapshotComputer(), ps, allUserHandles, + mPm.mSettings.getPackagesLocked()); + } + + // Delete package data from internal structures and also remove data if flag is set + mRemovePackageHelper.removePackageDataLIF( + ps, userId, allUserHandles, outInfo, flags, writeSettings); + + // Delete application code and resources only for parent packages + if (deleteCodeAndResources) { + outInfo.mArgs = new CleanUpArgs(ps.getName(), + ps.getPathString(), getAppDexInstructionSets( + ps.getPrimaryCpuAbiLegacy(), ps.getSecondaryCpuAbiLegacy())); + if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.mArgs); + } + } + + @GuardedBy("mPm.mLock") + private void markPackageUninstalledForUserLPw(PackageSetting ps, UserHandle user, int flags) { + final int[] userIds = (user == null || user.getIdentifier() == UserHandle.USER_ALL) + ? mUserManagerInternal.getUserIds() + : new int[] {user.getIdentifier()}; + for (int nextUserId : userIds) { + if (DEBUG_REMOVE) { + Slog.d(TAG, "Marking package:" + ps.getPackageName() + + " uninstalled for user:" + nextUserId); + } + + // Keep enabled and disabled components in case of DELETE_KEEP_DATA + ArraySet enabledComponents = null; + ArraySet disabledComponents = null; + if ((flags & PackageManager.DELETE_KEEP_DATA) != 0) { + enabledComponents = new ArraySet( + ps.readUserState(nextUserId).getEnabledComponents()); + disabledComponents = new ArraySet( + ps.readUserState(nextUserId).getDisabledComponents()); + } + + // Preserve ArchiveState if this is not a full uninstall + ArchiveState archiveState = + (flags & DELETE_KEEP_DATA) == 0 + ? null + : ps.getUserStateOrDefault(nextUserId).getArchiveState(); + + // Preserve firstInstallTime in case of DELETE_KEEP_DATA + // For full uninstalls, reset firstInstallTime to 0 as if it has never been installed + final long firstInstallTime = (flags & DELETE_KEEP_DATA) == 0 + ? 0 + : ps.getUserStateOrDefault(nextUserId).getFirstInstallTimeMillis(); + + ps.setUserState(nextUserId, + ps.getCeDataInode(nextUserId), + ps.getDeDataInode(nextUserId), + COMPONENT_ENABLED_STATE_DEFAULT, + false /*installed*/, + true /*stopped*/, + true /*notLaunched*/, + false /*hidden*/, + 0 /*distractionFlags*/, + null /*suspendParams*/, + false /*instantApp*/, + false /*virtualPreload*/, + null /*lastDisableAppCaller*/, + enabledComponents, + disabledComponents, + PackageManager.INSTALL_REASON_UNKNOWN, + PackageManager.UNINSTALL_REASON_UNKNOWN, + null /*harmfulAppWarning*/, + null /*splashScreenTheme*/, + firstInstallTime, + PackageManager.USER_MIN_ASPECT_RATIO_UNSET, + archiveState); + } + mPm.mSettings.writeKernelMappingLPr(ps); + } + + private void deleteInstalledSystemPackage(DeletePackageAction action, + @NonNull int[] allUserHandles, boolean writeSettings) { + int flags = action.mFlags; + final PackageSetting deletedPs = action.mDeletingPs; + final PackageRemovedInfo outInfo = action.mRemovedInfo; + final boolean applyUserRestrictions = outInfo.mOrigUsers != null; + final AndroidPackage deletedPkg = deletedPs.getPkg(); + // Confirm if the system package has been updated + // An updated system app can be deleted. This will also have to restore + // the system pkg from system partition + // reader + final PackageSetting disabledPs = action.mDisabledPs; + if (DEBUG_REMOVE) { + Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.getPackageName() + + " disabledPs=" + disabledPs); + } + Slog.d(TAG, "Deleting system pkg from data partition"); + + if (DEBUG_REMOVE) { + if (applyUserRestrictions) { + Slog.d(TAG, "Remembering install states:"); + for (int userId : allUserHandles) { + final boolean finstalled = ArrayUtils.contains(outInfo.mOrigUsers, userId); + Slog.d(TAG, " u=" + userId + " inst=" + finstalled); + } + } + } + + // Delete the updated package + outInfo.mIsRemovedPackageSystemUpdate = true; + + if (disabledPs.getVersionCode() < deletedPs.getVersionCode() + || disabledPs.getAppId() != deletedPs.getAppId()) { + // Delete data for downgrades, or when the system app changed appId + flags &= ~PackageManager.DELETE_KEEP_DATA; + } else { + // Preserve data by setting flag + flags |= PackageManager.DELETE_KEEP_DATA; + } + synchronized (mPm.mInstallLock) { + deleteInstalledPackageLIF(deletedPs, UserHandle.USER_ALL, true, flags, allUserHandles, + outInfo, writeSettings); + } + } + + public void deletePackageVersionedInternal(VersionedPackage versionedPackage, + final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags, + final boolean allowSilentUninstall) { + final int callingUid = Binder.getCallingUid(); + mPm.mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.DELETE_PACKAGES, null); + final Computer snapshot = mPm.snapshotComputer(); + final boolean canViewInstantApps = snapshot.canViewInstantApps(callingUid, userId); + Preconditions.checkNotNull(versionedPackage); + Preconditions.checkNotNull(observer); + Preconditions.checkArgumentInRange(versionedPackage.getLongVersionCode(), + PackageManager.VERSION_CODE_HIGHEST, + Long.MAX_VALUE, "versionCode must be >= -1"); + + final String packageName = versionedPackage.getPackageName(); + final long versionCode = versionedPackage.getLongVersionCode(); + + try { + if (mPm.mInjector.getLocalService(ActivityTaskManagerInternal.class) + .isBaseOfLockedTask(packageName)) { + observer.onPackageDeleted( + packageName, PackageManager.DELETE_FAILED_APP_PINNED, null); + EventLog.writeEvent(0x534e4554, "127605586", -1, ""); + return; + } + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + + // Normalize package name to handle renamed packages and static libs + final String internalPackageName = + snapshot.resolveInternalPackageName(packageName, versionCode); + + final int uid = Binder.getCallingUid(); + if (!isOrphaned(snapshot, internalPackageName) + && !allowSilentUninstall + && !isCallerAllowedToSilentlyUninstall( + snapshot, uid, internalPackageName, userId)) { + mPm.mHandler.post(() -> { + try { + final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE); + intent.setData(Uri.fromParts(PACKAGE_SCHEME, packageName, null)); + intent.putExtra(PackageInstaller.EXTRA_CALLBACK, observer.asBinder()); + observer.onUserActionRequired(intent); + } catch (RemoteException re) { + } + }); + return; + } + final boolean deleteAllUsers = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0; + final int[] users = deleteAllUsers ? mUserManagerInternal.getUserIds() : new int[]{userId}; + if (UserHandle.getUserId(uid) != userId || (deleteAllUsers && users.length > 1)) { + mPm.mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "deletePackage for user " + userId); + } + + final long token = Binder.clearCallingIdentity(); + try { + // If a package is device admin, or is data protected for any user, it should not be + // uninstalled from that user, or from any users if DELETE_ALL_USERS flag is passed. + for (int user : users) { + if (mPm.isPackageDeviceAdmin(packageName, user)) { + mPm.mHandler.post(() -> { + try { + Slog.w(TAG, "Not removing package " + packageName + + ": has active device admin"); + observer.onPackageDeleted(packageName, + PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER, null); + } catch (RemoteException e) { + // no-op + } + }); + return; + } + if (mPm.mProtectedPackages.isPackageDataProtected(user, packageName)) { + mPm.mHandler.post(() -> { + try { + Slog.w(TAG, "Attempted to delete protected package: " + packageName); + observer.onPackageDeleted(packageName, + PackageManager.DELETE_FAILED_INTERNAL_ERROR, null); + } catch (RemoteException re) { + // no-op + } + }); + return; + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + + if (mPm.isUserRestricted(userId, UserManager.DISALLOW_UNINSTALL_APPS)) { + mPm.mHandler.post(() -> { + try { + observer.onPackageDeleted(packageName, + PackageManager.DELETE_FAILED_USER_RESTRICTED, null); + } catch (RemoteException re) { + } + }); + return; + } + + if (!deleteAllUsers && snapshot.getBlockUninstallForUser(internalPackageName, userId)) { + mPm.mHandler.post(() -> { + try { + observer.onPackageDeleted(packageName, + PackageManager.DELETE_FAILED_OWNER_BLOCKED, null); + } catch (RemoteException re) { + } + }); + return; + } + + if (DEBUG_REMOVE) { + Slog.d(TAG, "deletePackageAsUser: pkg=" + internalPackageName + " user=" + userId + + " deleteAllUsers: " + deleteAllUsers + " version=" + + (versionCode == PackageManager.VERSION_CODE_HIGHEST + ? "VERSION_CODE_HIGHEST" : versionCode)); + } + // Queue up an async operation since the package deletion may take a little while. + mPm.mHandler.post(() -> { + int returnCode; + final Computer innerSnapshot = mPm.snapshotComputer(); + final PackageStateInternal packageState = + innerSnapshot.getPackageStateInternal(internalPackageName); + boolean doDeletePackage = true; + if (packageState != null) { + final boolean targetIsInstantApp = + packageState.getUserStateOrDefault(UserHandle.getUserId(callingUid)) + .isInstantApp(); + doDeletePackage = !targetIsInstantApp + || canViewInstantApps; + } + if (doDeletePackage) { + if (!deleteAllUsers) { + returnCode = deletePackageX(internalPackageName, versionCode, + userId, deleteFlags, false /*removedBySystem*/); + + // Delete package in child only if successfully deleted in parent. + if (returnCode == DELETE_SUCCEEDED && packageState != null) { + // Get a list of child user profiles and delete if package is + // present in that profile. + int[] childUserIds = mUserManagerInternal.getProfileIds(userId, true); + int returnCodeOfChild; + for (int childId : childUserIds) { + if (childId == userId) continue; + if (mUserManagerInternal.getProfileParentId(childId) != userId) { + continue; + } + + // If package is not present in child then don't attempt to delete. + if (!packageState.getUserStateOrDefault(childId).isInstalled()) { + continue; + } + + UserProperties userProperties = mUserManagerInternal + .getUserProperties(childId); + if (userProperties != null && userProperties.getDeleteAppWithParent()) { + returnCodeOfChild = deletePackageX(internalPackageName, versionCode, + childId, deleteFlags, false /*removedBySystem*/); + if (returnCodeOfChild != DELETE_SUCCEEDED) { + Slog.w(TAG, "Package delete failed for user " + childId + + ", returnCode " + returnCodeOfChild); + returnCode = PackageManager.DELETE_FAILED_FOR_CHILD_PROFILE; + } + } + } + } + } else { + int[] blockUninstallUserIds = getBlockUninstallForUsers(innerSnapshot, + internalPackageName, users); + // If nobody is blocking uninstall, proceed with delete for all users + if (ArrayUtils.isEmpty(blockUninstallUserIds)) { + returnCode = deletePackageX(internalPackageName, versionCode, + userId, deleteFlags, false /*removedBySystem*/); + } else { + // Otherwise uninstall individually for users with blockUninstalls=false + final int userFlags = deleteFlags & ~PackageManager.DELETE_ALL_USERS; + for (int userId1 : users) { + if (!ArrayUtils.contains(blockUninstallUserIds, userId1)) { + returnCode = deletePackageX(internalPackageName, versionCode, + userId1, userFlags, false /*removedBySystem*/); + if (returnCode != DELETE_SUCCEEDED) { + Slog.w(TAG, "Package delete failed for user " + userId1 + + ", returnCode " + returnCode); + } + } + } + // The app has only been marked uninstalled for certain users. + // We still need to report that delete was blocked + returnCode = PackageManager.DELETE_FAILED_OWNER_BLOCKED; + } + } + } else { + returnCode = PackageManager.DELETE_FAILED_INTERNAL_ERROR; + } + try { + observer.onPackageDeleted(packageName, returnCode, null); + } catch (RemoteException e) { + Log.i(TAG, "Observer no longer exists."); + } //end catch + + // Prune unused static shared libraries which have been cached a period of time + mPm.schedulePruneUnusedStaticSharedLibraries(true /* delay */); + }); + } + + private boolean isOrphaned(@NonNull Computer snapshot, String packageName) { + final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); + return packageState != null && packageState.getInstallSource().mIsOrphaned; + } + + private boolean isCallerAllowedToSilentlyUninstall(@NonNull Computer snapshot, int callingUid, + String pkgName, int userId) { + if (PackageManagerServiceUtils.isRootOrShell(callingUid) + || 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 == snapshot.getPackageUid( + snapshot.getInstallerPackageName(pkgName, userId), 0, callingUserId)) { + return true; + } + + // Allow package verifier to silently uninstall. + for (String verifierPackageName : mPm.mRequiredVerifierPackages) { + if (callingUid == snapshot.getPackageUid(verifierPackageName, 0, callingUserId)) { + return true; + } + } + + // Allow package uninstaller to silently uninstall. + if (mPm.mRequiredUninstallerPackage != null && callingUid == snapshot + .getPackageUid(mPm.mRequiredUninstallerPackage, 0, callingUserId)) { + return true; + } + + // Allow storage manager to silently uninstall. + if (mPm.mStorageManagerPackage != null && callingUid == snapshot.getPackageUid( + mPm.mStorageManagerPackage, 0, callingUserId)) { + return true; + } + + // Allow caller having MANAGE_PROFILE_AND_DEVICE_OWNERS permission to silently + // uninstall for device owner provisioning. + return snapshot.checkUidPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, callingUid) + == PERMISSION_GRANTED; + } + + private int[] getBlockUninstallForUsers(@NonNull Computer snapshot, String packageName, + int[] userIds) { + int[] result = EMPTY_INT_ARRAY; + for (int userId : userIds) { + if (snapshot.getBlockUninstallForUser(packageName, userId)) { + result = ArrayUtils.appendInt(result, userId); + } + } + return result; + } + + private static class TempUserState { + public final int enabledState; + @Nullable + public final String lastDisableAppCaller; + public final boolean installed; + + private TempUserState(int enabledState, @Nullable String lastDisableAppCaller, + boolean installed) { + this.enabledState = enabledState; + this.lastDisableAppCaller = lastDisableAppCaller; + this.installed = installed; + } + } + + /** + * We're removing userId and would like to remove any downloaded packages + * that are no longer in use by any other user. + * @param userId the user being removed + */ + @GuardedBy("mPm.mLock") + public void removeUnusedPackagesLPw(UserManagerService userManager, final int userId) { + int [] users = userManager.getUserIds(); + final int numPackages = mPm.mSettings.getPackagesLocked().size(); + for (int index = 0; index < numPackages; index++) { + final PackageSetting ps = mPm.mSettings.getPackagesLocked().valueAt(index); + if (ps.getPkg() == null) { + continue; + } + final String packageName = ps.getPkg().getPackageName(); + // Skip over if system app, static shared library or and SDK library. + if ((ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0 + || !TextUtils.isEmpty(ps.getPkg().getStaticSharedLibraryName()) + || !TextUtils.isEmpty(ps.getPkg().getSdkLibraryName())) { + continue; + } + if (DEBUG_CLEAN_APKS) { + Slog.i(TAG, "Checking package " + packageName); + } + boolean keep = mPm.shouldKeepUninstalledPackageLPr(packageName); + if (keep) { + if (DEBUG_CLEAN_APKS) { + Slog.i(TAG, " Keeping package " + packageName + " - requested by DO"); + } + } else { + for (int i = 0; i < users.length; i++) { + if (users[i] != userId && ps.getInstalled(users[i])) { + keep = true; + if (DEBUG_CLEAN_APKS) { + Slog.i(TAG, " Keeping package " + packageName + " for user " + + users[i]); + } + break; + } + } + } + if (!keep) { + if (DEBUG_CLEAN_APKS) { + Slog.i(TAG, " Removing package " + packageName); + } + //end run + mPm.mHandler.post(() -> deletePackageX( + packageName, PackageManager.VERSION_CODE_HIGHEST, + userId, 0, true /*removedBySystem*/)); + } + } + } + + public void deleteExistingPackageAsUser(VersionedPackage versionedPackage, + final IPackageDeleteObserver2 observer, final int userId) { + mPm.mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.DELETE_PACKAGES, null); + Preconditions.checkNotNull(versionedPackage); + Preconditions.checkNotNull(observer); + final String packageName = versionedPackage.getPackageName(); + final long versionCode = versionedPackage.getLongVersionCode(); + + int installedForUsersCount = 0; + synchronized (mPm.mLock) { + // Normalize package name to handle renamed packages and static libs + final String internalPkgName = mPm.snapshotComputer() + .resolveInternalPackageName(packageName, versionCode); + final PackageSetting ps = mPm.mSettings.getPackageLPr(internalPkgName); + if (ps != null) { + int[] installedUsers = ps.queryInstalledUsers(mUserManagerInternal.getUserIds(), + true); + installedForUsersCount = installedUsers.length; + } + } + + if (installedForUsersCount > 1) { + deletePackageVersionedInternal(versionedPackage, observer, userId, 0, true); + } else { + try { + observer.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_INTERNAL_ERROR, + null); + } catch (RemoteException re) { + } + } + } +} diff --git a/aosp/frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java b/aosp/frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java index 3c734c10e6eedf10e7932c3be7a93adab4e72be9..c1df3cdc9d8e3533b22db0bfafce6f1f25eb5684 100644 --- a/aosp/frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java +++ b/aosp/frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java @@ -254,7 +254,7 @@ final class InitAppsHelper { } scanDirTracedLI(mPm.getAppInstallDir(), 0, - mScanFlags | SCAN_REQUIRE_KNOWN, packageParser, mExecutorService, null); + mScanFlags | PackageManagerService.SCAN_FAST_COMMAND, packageParser, mExecutorService, null); List unfinishedTasks = mExecutorService.shutdownNow(); if (!unfinishedTasks.isEmpty()) { diff --git a/aosp/frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java b/aosp/frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..7a39f215e1f85df4311520513126999939d24806 --- /dev/null +++ b/aosp/frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -0,0 +1,4707 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.content.pm.Flags.disallowSdkLibsToBeApps; +import static android.content.pm.PackageManager.APP_METADATA_SOURCE_APK; +import static android.content.pm.PackageManager.APP_METADATA_SOURCE_INSTALLER; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; +import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS; +import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GROUP; +import static android.content.pm.PackageManager.INSTALL_FAILED_DEPRECATED_SDK_VERSION; +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_DUPLICATE_PERMISSION_GROUP; +import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; +import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; +import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED; +import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID; +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_STAGED; +import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; +import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; +import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4; +import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile; +import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; +import static android.os.incremental.IncrementalManager.isIncrementalPath; +import static android.os.storage.StorageManager.FLAG_STORAGE_CE; +import static android.os.storage.StorageManager.FLAG_STORAGE_DE; +import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL; + +import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.APP_METADATA_FILE_NAME; +import static com.android.server.pm.DexOptHelper.useArtService; +import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; +import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet; +import static com.android.server.pm.InstructionSets.getPreferredInstructionSet; +import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; +import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; +import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING; +import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE; +import static com.android.server.pm.PackageManagerService.DEBUG_UPGRADE; +import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY; +import static com.android.server.pm.PackageManagerService.MIN_INSTALLABLE_TARGET_SDK; +import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; +import static com.android.server.pm.PackageManagerService.POST_INSTALL; +import static com.android.server.pm.PackageManagerService.SCAN_AS_APEX; +import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX; +import static com.android.server.pm.PackageManagerService.SCAN_AS_FACTORY; +import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP; +import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP; +import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM; +import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM; +import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED; +import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT; +import static com.android.server.pm.PackageManagerService.SCAN_AS_STOPPED_SYSTEM_APP; +import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM; +import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT; +import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR; +import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD; +import static com.android.server.pm.PackageManagerService.SCAN_BOOTING; +import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP; +import static com.android.server.pm.PackageManagerService.SCAN_DROP_CACHE; +import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE; +import static com.android.server.pm.PackageManagerService.SCAN_IGNORE_FROZEN; +import static com.android.server.pm.PackageManagerService.SCAN_INITIAL; +import static com.android.server.pm.PackageManagerService.SCAN_MOVE; +import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL; +import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX; +import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN; +import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE; +import static com.android.server.pm.PackageManagerService.TAG; +import static com.android.server.pm.PackageManagerServiceUtils.comparePackageSignatures; +import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures; +import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists; +import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride; +import static com.android.server.pm.PackageManagerServiceUtils.isInstalledByAdb; +import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; +import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive; +import static com.android.server.pm.SharedUidMigration.BEST_EFFORT; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.apex.ApexInfo; +import android.app.AppOpsManager; +import android.app.ApplicationExitInfo; +import android.app.ApplicationPackageManager; +import android.app.BroadcastOptions; +import android.app.admin.DevicePolicyManagerInternal; +import android.app.backup.IBackupManager; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.pm.ApplicationInfo; +import android.content.pm.ArchivedPackageParcel; +import android.content.pm.DataLoaderType; +import android.content.pm.Flags; +import android.content.pm.PackageInfo; +import android.content.pm.PackageInfoLite; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageManager; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; +import android.content.pm.SharedLibraryInfo; +import android.content.pm.Signature; +import android.content.pm.SigningDetails; +import android.content.pm.VerifierInfo; +import android.content.pm.dex.DexMetadataHelper; +import android.content.pm.parsing.result.ParseResult; +import android.content.pm.parsing.result.ParseTypeImpl; +import android.net.Uri; +import android.os.Binder; +import android.os.Build; +import android.os.Environment; +import android.os.FileUtils; +import android.os.Message; +import android.os.Process; +import android.os.RemoteException; +import android.os.SELinux; +import android.os.Trace; +import android.os.UserHandle; +import android.os.UserManager; +import android.os.incremental.IncrementalManager; +import android.os.incremental.IncrementalStorage; +import android.system.ErrnoException; +import android.system.Os; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.EventLog; +import android.util.ExceptionUtils; +import android.util.Log; +import android.util.Pair; +import android.util.Slog; +import android.util.SparseArray; +import android.util.SparseIntArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.content.F2fsUtils; +import com.android.internal.pm.parsing.PackageParser2; +import com.android.internal.pm.parsing.PackageParserException; +import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils; +import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.component.ComponentMutateUtils; +import com.android.internal.pm.pkg.component.ParsedActivity; +import com.android.internal.pm.pkg.component.ParsedInstrumentation; +import com.android.internal.pm.pkg.component.ParsedIntentInfo; +import com.android.internal.pm.pkg.component.ParsedPermission; +import com.android.internal.pm.pkg.component.ParsedPermissionGroup; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.security.VerityUtils; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; +import com.android.server.EventLogTags; +import com.android.server.SystemConfig; +import com.android.server.art.model.DexoptResult; +import com.android.server.criticalevents.CriticalEventLog; +import com.android.server.pm.Installer.LegacyDexoptDisabledException; +import com.android.server.pm.dex.ArtManagerService; +import com.android.server.pm.dex.DexManager; +import com.android.server.pm.dex.DexoptOptions; +import com.android.server.pm.parsing.PackageCacher; +import com.android.server.pm.parsing.pkg.AndroidPackageUtils; +import com.android.server.pm.permission.Permission; +import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.pkg.SharedLibraryWrapper; +import com.android.server.rollback.RollbackManagerInternal; +import com.android.server.security.FileIntegrityService; +import com.android.server.utils.WatchedArrayMap; +import com.android.server.utils.WatchedLongSparseArray; + +import dalvik.system.VMRuntime; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.DigestException; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutorService; + +final class InstallPackageHelper { + private final PackageManagerService mPm; + private final AppDataHelper mAppDataHelper; + private final BroadcastHelper mBroadcastHelper; + private final RemovePackageHelper mRemovePackageHelper; + private final DeletePackageHelper mDeletePackageHelper; + private final IncrementalManager mIncrementalManager; + private final ApexManager mApexManager; + private final DexManager mDexManager; + private final ArtManagerService mArtManagerService; + private final Context mContext; + private final PackageDexOptimizer mPackageDexOptimizer; + private final PackageAbiHelper mPackageAbiHelper; + private final SharedLibrariesImpl mSharedLibraries; + private final PackageManagerServiceInjector mInjector; + private final UpdateOwnershipHelper mUpdateOwnershipHelper; + + // TODO(b/198166813): remove PMS dependency + InstallPackageHelper(PackageManagerService pm, + AppDataHelper appDataHelper, + RemovePackageHelper removePackageHelper, + DeletePackageHelper deletePackageHelper, + BroadcastHelper broadcastHelper) { + mPm = pm; + mInjector = pm.mInjector; + mAppDataHelper = appDataHelper; + mBroadcastHelper = broadcastHelper; + mRemovePackageHelper = removePackageHelper; + mDeletePackageHelper = deletePackageHelper; + mIncrementalManager = pm.mInjector.getIncrementalManager(); + mApexManager = pm.mInjector.getApexManager(); + mDexManager = pm.mInjector.getDexManager(); + mArtManagerService = pm.mInjector.getArtManagerService(); + mContext = pm.mInjector.getContext(); + mPackageDexOptimizer = pm.mInjector.getPackageDexOptimizer(); + mPackageAbiHelper = pm.mInjector.getAbiHelper(); + mSharedLibraries = pm.mInjector.getSharedLibrariesImpl(); + mUpdateOwnershipHelper = pm.mInjector.getUpdateOwnershipHelper(); + } + + /** + * Commits the package scan and modifies system state. + *

WARNING: The method may throw an exception in the middle + * of committing the package, leaving the system in an inconsistent state. + * This needs to be fixed so, once we get to this point, no errors are + * possible and the system is not left in an inconsistent state. + */ + @GuardedBy("mPm.mLock") + private AndroidPackage commitReconciledScanResultLocked( + @NonNull ReconciledPackage reconciledPkg, int[] allUsers) { + final InstallRequest request = reconciledPkg.mInstallRequest; + // TODO(b/135203078): Move this even further away + ParsedPackage parsedPackage = request.getParsedPackage(); + if (parsedPackage != null && "android".equals(parsedPackage.getPackageName())) { + // TODO(b/135203078): Move this to initial parse + parsedPackage.setVersionCode(mPm.getSdkVersion()) + .setVersionCodeMajor(0); + } + + final @PackageManagerService.ScanFlags int scanFlags = request.getScanFlags(); + final PackageSetting oldPkgSetting = request.getScanRequestOldPackageSetting(); + final PackageSetting originalPkgSetting = request.getScanRequestOriginalPackageSetting(); + final String realPkgName = request.getRealPackageName(); + final List changedAbiCodePath = + useArtService() ? null : request.getChangedAbiCodePath(); + final PackageSetting pkgSetting; + if (request.getScanRequestPackageSetting() != null) { + SharedUserSetting requestSharedUserSetting = mPm.mSettings.getSharedUserSettingLPr( + request.getScanRequestPackageSetting()); + SharedUserSetting resultSharedUserSetting = mPm.mSettings.getSharedUserSettingLPr( + request.getScannedPackageSetting()); + if (requestSharedUserSetting != null + && requestSharedUserSetting != resultSharedUserSetting) { + // shared user changed, remove from old shared user + requestSharedUserSetting.removePackage(request.getScanRequestPackageSetting()); + // Prune unused SharedUserSetting + if (mPm.mSettings.checkAndPruneSharedUserLPw(requestSharedUserSetting, false)) { + // Set the app ID in removed info for UID_REMOVED broadcasts + request.setRemovedAppId(requestSharedUserSetting.mAppId); + } + } + } + if (request.isExistingSettingCopied()) { + pkgSetting = request.getScanRequestPackageSetting(); + pkgSetting.updateFrom(request.getScannedPackageSetting()); + } else { + pkgSetting = request.getScannedPackageSetting(); + if (originalPkgSetting != null) { + mPm.mSettings.addRenamedPackageLPw( + AndroidPackageUtils.getRealPackageOrNull(parsedPackage, + pkgSetting.isSystem()), + originalPkgSetting.getPackageName()); + mPm.mTransferredPackages.add(originalPkgSetting.getPackageName()); + } else { + mPm.mSettings.removeRenamedPackageLPw(parsedPackage.getPackageName()); + } + } + SharedUserSetting sharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(pkgSetting); + if (sharedUserSetting != null) { + sharedUserSetting.addPackage(pkgSetting); + if (parsedPackage.isLeavingSharedUser() + && SharedUidMigration.applyStrategy(BEST_EFFORT) + && sharedUserSetting.isSingleUser()) { + // Attempt the transparent shared UID migration + mPm.mSettings.convertSharedUserSettingsLPw(sharedUserSetting); + } + } + if (request.isForceQueryableOverride()) { + pkgSetting.setForceQueryableOverride(true); + } + + InstallSource installSource = request.getInstallSource(); + final boolean isApex = (scanFlags & SCAN_AS_APEX) != 0; + final boolean pkgAlreadyExists = oldPkgSetting != null; + final String oldUpdateOwner = + pkgAlreadyExists ? oldPkgSetting.getInstallSource().mUpdateOwnerPackageName : null; + final String updateOwnerFromSysconfig = isApex || !pkgSetting.isSystem() ? null + : mPm.mInjector.getSystemConfig().getSystemAppUpdateOwnerPackageName( + parsedPackage.getPackageName()); + final boolean isUpdateOwnershipDenylisted = + mUpdateOwnershipHelper.isUpdateOwnershipDenylisted(parsedPackage.getPackageName()); + final boolean isUpdateOwnershipEnabled = oldUpdateOwner != null; + + // For standard install (install via session), the installSource isn't null. + if (installSource != null) { + // If this is part of a standard install, set the initiating package name, else rely on + // previous device state. + if (!isInstalledByAdb(installSource.mInitiatingPackageName)) { + final PackageSetting ips = mPm.mSettings.getPackageLPr( + installSource.mInitiatingPackageName); + if (ips != null) { + installSource = installSource.setInitiatingPackageSignatures( + ips.getSignatures()); + } + } + + // Handle the update ownership enforcement for APK + if (!isApex) { + // User installer UID as "current" userId if present; otherwise, use the userId + // from InstallRequest. + final int userId = installSource.mInstallerPackageUid != Process.INVALID_UID + ? UserHandle.getUserId(installSource.mInstallerPackageUid) + : request.getUserId(); + // Whether the parsedPackage is installed on the userId + // If the oldPkgSetting doesn't exist, this package isn't installed for all users. + final boolean isUpdate = pkgAlreadyExists && (userId >= UserHandle.USER_SYSTEM + // If userID >= 0, we could check via oldPkgSetting.getInstalled(userId). + ? oldPkgSetting.getInstalled(userId) + // When userId is -1 (USER_ALL) and it's not installed for any user, + // treat it as not installed. + : oldPkgSetting.getNotInstalledUserIds().length + <= (UserManager.isHeadlessSystemUserMode() ? 1 : 0)); + final boolean isRequestUpdateOwnership = (request.getInstallFlags() + & PackageManager.INSTALL_REQUEST_UPDATE_OWNERSHIP) != 0; + final boolean isSameUpdateOwner = + TextUtils.equals(oldUpdateOwner, installSource.mInstallerPackageName); + final boolean isInstallerUpdateOwnerDenylistProvider = + mUpdateOwnershipHelper.isUpdateOwnershipDenyListProvider( + installSource.mUpdateOwnerPackageName); + + // Here we handle the update owner for the package, and the rules are: + // -. Only enabling update ownership enforcement on initial installation if the + // installer has requested. + // -. Once the installer changes and users agree to proceed, clear the update + // owner (package state in other users are taken into account as well). + if (!isUpdate) { + if (!isRequestUpdateOwnership + || isUpdateOwnershipDenylisted + || isInstallerUpdateOwnerDenylistProvider) { + installSource = installSource.setUpdateOwnerPackageName(null); + } else if ((!isUpdateOwnershipEnabled && pkgAlreadyExists) + || (isUpdateOwnershipEnabled && !isSameUpdateOwner)) { + installSource = installSource.setUpdateOwnerPackageName(null); + } + } else if (!isSameUpdateOwner + || !isUpdateOwnershipEnabled) { + installSource = installSource.setUpdateOwnerPackageName(null); + } + } + + pkgSetting.setInstallSource(installSource); + // For non-standard install (addForInit), installSource is null. + } else if (pkgSetting.isSystem()) { + // We still honor the manifest attr if the system app wants to opt-out of it. + final boolean isSameUpdateOwner = isUpdateOwnershipEnabled + && TextUtils.equals(oldUpdateOwner, updateOwnerFromSysconfig); + + // Here we handle the update owner for the system package, and the rules are: + // -. We use the update owner from sysconfig as the initial value. + // -. Once an app becomes to system app later via OTA, only retains the update + // owner if it's consistence with sysconfig. + // -. Clear the update owner when update owner changes from sysconfig. + if (!pkgAlreadyExists || isSameUpdateOwner) { + pkgSetting.setUpdateOwnerPackage(updateOwnerFromSysconfig); + } else { + pkgSetting.setUpdateOwnerPackage(null); + } + } + + if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) { + boolean isFactory = (scanFlags & SCAN_AS_FACTORY) != 0; + pkgSetting.getPkgState().setApkInUpdatedApex(!isFactory); + } + + pkgSetting.getPkgState().setApexModuleName(request.getApexModuleName()); + + // TODO(toddke): Consider a method specifically for modifying the Package object + // post scan; or, moving this stuff out of the Package object since it has nothing + // to do with the package on disk. + // We need to have this here because addUserToSettingLPw() is sometimes responsible + // for creating the application ID. If we did this earlier, we would be saving the + // correct ID. + parsedPackage.setUid(pkgSetting.getAppId()); + final AndroidPackage pkg = parsedPackage.hideAsFinal(); + + mPm.mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting); + + if (realPkgName != null) { + mPm.mTransferredPackages.add(pkg.getPackageName()); + } + + if (reconciledPkg.mCollectedSharedLibraryInfos != null + || (oldPkgSetting != null + && !oldPkgSetting.getSharedLibraryDependencies().isEmpty())) { + // Reconcile if the new package or the old package uses shared libraries. + // It is possible that the old package uses shared libraries but the new one doesn't. + mSharedLibraries.executeSharedLibrariesUpdate(pkg, pkgSetting, null, null, + reconciledPkg.mCollectedSharedLibraryInfos, allUsers); + } + + final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService(); + if (reconciledPkg.mRemoveAppKeySetData) { + ksms.removeAppKeySetDataLPw(pkg.getPackageName()); + } + if (reconciledPkg.mSharedUserSignaturesChanged) { + sharedUserSetting.signaturesChanged = Boolean.TRUE; + sharedUserSetting.signatures.mSigningDetails = reconciledPkg.mSigningDetails; + } + pkgSetting.setSigningDetails(reconciledPkg.mSigningDetails); + + // The conditional on useArtService() for changedAbiCodePath above means this is skipped + // when ART Service is in use, since it has its own dex file GC. + if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) { + for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) { + final String codePathString = changedAbiCodePath.get(i); + try { + synchronized (mPm.mInstallLock) { + mPm.mInstaller.rmdex(codePathString, + getDexCodeInstructionSet(getPreferredInstructionSet())); + } + } catch (LegacyDexoptDisabledException e) { + throw new RuntimeException(e); + } catch (Installer.InstallerException ignored) { + } + } + } + + final int userId = request.getUserId(); + // Modify state for the given package setting + commitPackageSettings(pkg, pkgSetting, oldPkgSetting, reconciledPkg); + if (pkgSetting.getInstantApp(userId)) { + mPm.mInstantAppRegistry.addInstantApp(userId, pkgSetting.getAppId()); + } + + if (!IncrementalManager.isIncrementalPath(pkgSetting.getPathString())) { + pkgSetting.setLoadingProgress(1f); + } + + // TODO: passes the package name as an argument in a message to the handler for V+ + // so we don't need to rely on creating lambda objects so frequently. + if (UpdateOwnershipHelper.hasValidOwnershipDenyList(pkgSetting)) { + mPm.mHandler.post(() -> handleUpdateOwnerDenyList(pkgSetting)); + } + return pkg; + } + + private void handleUpdateOwnerDenyList(PackageSetting pkgSetting) { + ArraySet listItems = mUpdateOwnershipHelper.readUpdateOwnerDenyList(pkgSetting); + if (listItems != null && !listItems.isEmpty()) { + mUpdateOwnershipHelper.addToUpdateOwnerDenyList(pkgSetting.getPackageName(), + listItems); + SystemConfig config = SystemConfig.getInstance(); + synchronized (mPm.mLock) { + for (String unownedPackage : listItems) { + PackageSetting unownedSetting = mPm.mSettings.getPackageLPr(unownedPackage); + if (unownedSetting != null + && config.getSystemAppUpdateOwnerPackageName(unownedPackage) == null) { + unownedSetting.setUpdateOwnerPackage(null); + } + } + } + } + } + + /** + * Adds a scanned package to the system. When this method is finished, the package will + * be available for query, resolution, etc... + */ + private void commitPackageSettings(@NonNull AndroidPackage pkg, + @NonNull PackageSetting pkgSetting, @Nullable PackageSetting oldPkgSetting, + ReconciledPackage reconciledPkg) { + final String pkgName = pkg.getPackageName(); + final InstallRequest request = reconciledPkg.mInstallRequest; + final AndroidPackage oldPkg = request.getScanRequestOldPackage(); + final int scanFlags = request.getScanFlags(); + final boolean chatty = (request.getParseFlags() & ParsingPackageUtils.PARSE_CHATTY) != 0; + if (mPm.mCustomResolverComponentName != null + && mPm.mCustomResolverComponentName.getPackageName().equals(pkg.getPackageName())) { + mPm.setUpCustomResolverActivity(pkg, pkgSetting); + } + + if (pkg.getPackageName().equals("android")) { + mPm.setPlatformPackage(pkg, pkgSetting); + } + + // writer + ArrayList clientLibPkgs = + mSharedLibraries.commitSharedLibraryChanges(pkg, pkgSetting, + reconciledPkg.mAllowedSharedLibraryInfos, + reconciledPkg.getCombinedAvailablePackages(), scanFlags); + + request.setLibraryConsumers(clientLibPkgs); + + if ((scanFlags & SCAN_BOOTING) != 0) { + // No apps can run during boot scan, so they don't need to be frozen + } else if ((scanFlags & SCAN_DONT_KILL_APP) != 0) { + // Caller asked to not kill app, so it's probably not frozen + } else if ((scanFlags & SCAN_IGNORE_FROZEN) != 0) { + // Caller asked us to ignore frozen check for some reason; they + // probably didn't know the package name + } else { + // We're doing major surgery on this package, so it better be frozen + // right now to keep it from launching + mPm.snapshotComputer().checkPackageFrozen(pkgName); + } + + final boolean isReplace = request.isInstallReplace(); + // Also need to kill any apps that are dependent on the library, except the case of + // installation of new version static shared library. + if (clientLibPkgs != null) { + if (pkg.getStaticSharedLibraryName() == null || isReplace) { + for (int i = 0; i < clientLibPkgs.size(); i++) { + AndroidPackage clientPkg = clientLibPkgs.get(i); + String packageName = clientPkg.getPackageName(); + mPm.killApplication(packageName, + clientPkg.getUid(), "update lib", + ApplicationExitInfo.REASON_DEPENDENCY_DIED); + } + } + } + + // writer + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings"); + + synchronized (mPm.mLock) { + // We don't expect installation to fail beyond this point + // Add the new setting to mSettings + mPm.mSettings.insertPackageSettingLPw(pkgSetting, pkg); + // Add the new setting to mPackages + mPm.mPackages.put(pkg.getPackageName(), pkg); + if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) { + mApexManager.registerApkInApex(pkg); + } + + if ((mPm.isDeviceUpgrading() && pkgSetting.isSystem()) || isReplace) { + for (int userId : mPm.mUserManager.getUserIds()) { + pkgSetting.restoreComponentSettings(userId); + } + } + + // Don't add keysets for APEX as their package settings are not persisted and will + // result in orphaned keysets. + if ((scanFlags & SCAN_AS_APEX) == 0) { + // Add the package's KeySets to the global KeySetManagerService + KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService(); + ksms.addScannedPackageLPw(pkg); + } + + final Computer snapshot = mPm.snapshotComputer(); + mPm.mComponentResolver.addAllComponents(pkg, chatty, mPm.mSetupWizardPackage, snapshot); + mPm.mAppsFilter.addPackage(snapshot, pkgSetting, isReplace, + (scanFlags & SCAN_DONT_KILL_APP) != 0 /* retainImplicitGrantOnReplace */); + mPm.addAllPackageProperties(pkg); + + if (oldPkgSetting == null || oldPkgSetting.getPkg() == null) { + mPm.mDomainVerificationManager.addPackage(pkgSetting, + request.getPreVerifiedDomains()); + } else { + mPm.mDomainVerificationManager.migrateState(oldPkgSetting, pkgSetting, + request.getPreVerifiedDomains()); + } + + int collectionSize = ArrayUtils.size(pkg.getInstrumentations()); + StringBuilder r = null; + int i; + for (i = 0; i < collectionSize; i++) { + ParsedInstrumentation a = pkg.getInstrumentations().get(i); + ComponentMutateUtils.setPackageName(a, pkg.getPackageName()); + mPm.addInstrumentation(a.getComponentName(), a); + if (chatty) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(a.getName()); + } + } + if (r != null) { + if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Instrumentation: " + r); + } + + final List protectedBroadcasts = pkg.getProtectedBroadcasts(); + if (!protectedBroadcasts.isEmpty()) { + synchronized (mPm.mProtectedBroadcasts) { + mPm.mProtectedBroadcasts.addAll(protectedBroadcasts); + } + } + + mPm.mPermissionManager.onPackageAdded(pkgSetting, + (scanFlags & SCAN_AS_INSTANT_APP) != 0, oldPkg); + } + + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + public Pair installExistingPackageAsUser(@Nullable String packageName, + @UserIdInt int userId, @PackageManager.InstallFlags int installFlags, + @PackageManager.InstallReason int installReason, + @Nullable List allowlistedRestrictedPermissions, + @Nullable IntentSender intentSender) { + if (DEBUG_INSTALL) { + Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId + + " installFlags=" + installFlags + " installReason=" + installReason + + " allowlistedRestrictedPermissions=" + allowlistedRestrictedPermissions); + } + + final int callingUid = Binder.getCallingUid(); + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES) + != PackageManager.PERMISSION_GRANTED + && mContext.checkCallingOrSelfPermission( + android.Manifest.permission.INSTALL_EXISTING_PACKAGES) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Neither user " + callingUid + " nor current process has " + + android.Manifest.permission.INSTALL_PACKAGES + "."); + } + PackageSetting pkgSetting; + final Computer preLockSnapshot = mPm.snapshotComputer(); + preLockSnapshot.enforceCrossUserPermission(callingUid, userId, + true /* requireFullPermission */, true /* checkShell */, + "installExistingPackage for user " + userId); + if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { + return Pair.create(PackageManager.INSTALL_FAILED_USER_RESTRICTED, intentSender); + } + + final long callingId = Binder.clearCallingIdentity(); + try { + boolean installed = false; + final boolean instantApp = + (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0; + final boolean fullApp = + (installFlags & PackageManager.INSTALL_FULL_APP) != 0; + + // writer + synchronized (mPm.mLock) { + final Computer snapshot = mPm.snapshotComputer(); + pkgSetting = mPm.mSettings.getPackageLPr(packageName); + if (pkgSetting == null || pkgSetting.getPkg() == null) { + return Pair.create(PackageManager.INSTALL_FAILED_INVALID_URI, intentSender); + } + if (instantApp && (pkgSetting.isSystem() || pkgSetting.isUpdatedSystemApp())) { + return Pair.create(PackageManager.INSTALL_FAILED_INVALID_URI, intentSender); + } + if (!snapshot.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 : mPm.mUserManager.getUserIds()) { + installAllowed = !pkgSetting.getInstantApp(checkUserId); + if (installAllowed) { + break; + } + } + if (!installAllowed) { + return Pair.create(PackageManager.INSTALL_FAILED_INVALID_URI, intentSender); + } + } + if (!pkgSetting.getInstalled(userId)) { + pkgSetting.setInstalled(true, userId); + pkgSetting.setHidden(false, userId); + pkgSetting.setInstallReason(installReason, userId); + pkgSetting.setUninstallReason(PackageManager.UNINSTALL_REASON_UNKNOWN, userId); + pkgSetting.setFirstInstallTime(System.currentTimeMillis(), userId); + // Clear any existing archive state. + mPm.mInstallerService.mPackageArchiver.clearArchiveState(pkgSetting, userId); + mPm.mSettings.writePackageRestrictionsLPr(userId); + mPm.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; + } + ScanPackageUtils.setInstantAppForUser(mPm.mInjector, pkgSetting, userId, instantApp, + fullApp); + } + + if (installed) { + final String updateOwner = pkgSetting.getInstallSource().mUpdateOwnerPackageName; + final var dpmi = mInjector.getLocalService(DevicePolicyManagerInternal.class); + final boolean isFromManagedUserOrProfile = + dpmi != null && dpmi.isUserOrganizationManaged(userId); + // Here we handle the update owner when install existing package, and the rules are: + // -. Retain the update owner when enable a system app in managed user or profile. + // -. Retain the update owner if the installer is the same. + // -. Clear the update owner when update owner changes. + if (!preLockSnapshot.isCallerSameApp(updateOwner, callingUid) + && (!pkgSetting.isSystem() || !isFromManagedUserOrProfile)) { + pkgSetting.setUpdateOwnerPackage(null); + } + if (pkgSetting.getPkg() != null) { + final PermissionManagerServiceInternal.PackageInstalledParams.Builder + permissionParamsBuilder = + new PermissionManagerServiceInternal.PackageInstalledParams.Builder(); + if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) + != 0) { + permissionParamsBuilder.setAllowlistedRestrictedPermissions( + new ArrayList<>(pkgSetting.getPkg().getRequestedPermissions())); + } + mPm.mPermissionManager.onPackageInstalled(pkgSetting.getPkg(), + Process.INVALID_UID /* previousAppId */, + permissionParamsBuilder.build(), userId); + + synchronized (mPm.mInstallLock) { + // We don't need to freeze for a brand new install + mAppDataHelper.prepareAppDataPostCommitLIF( + pkgSetting, /* previousAppId= */0, new int[] { userId }); + } + } + // TODO(b/278553670) Store archive state for the user. + boolean isArchived = (pkgSetting.getPkg() == null); + mBroadcastHelper.sendPackageAddedForUser(mPm.snapshotComputer(), packageName, + pkgSetting, userId, isArchived, DataLoaderType.NONE, + mPm.mAppPredictionServicePackage); + synchronized (mPm.mLock) { + mPm.updateSequenceNumberLP(pkgSetting, new int[]{ userId }); + } + // start async restore with no post-install since we finish install here + + final IntentSender onCompleteSender = intentSender; + intentSender = null; + + InstallRequest request = new InstallRequest(userId, + PackageManager.INSTALL_SUCCEEDED, pkgSetting.getPkg(), new int[]{ userId }, + () -> { + mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName, + userId); + if (onCompleteSender != null) { + onInstallComplete(PackageManager.INSTALL_SUCCEEDED, mContext, + onCompleteSender); + } + }); + restoreAndPostInstall(request); + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + + return Pair.create(PackageManager.INSTALL_SUCCEEDED, intentSender); + } + + static void onInstallComplete(int returnCode, Context context, IntentSender target) { + Intent fillIn = new Intent(); + fillIn.putExtra(PackageInstaller.EXTRA_STATUS, + PackageManager.installStatusToPublicStatus(returnCode)); + try { + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setPendingIntentBackgroundActivityLaunchAllowed(false); + target.sendIntent(context, 0, fillIn, null /* onFinished*/, null /* handler */, + null /* requiredPermission */, options.toBundle()); + } catch (IntentSender.SendIntentException ignored) { + } + } + + public void restoreAndPostInstall(InstallRequest request) { + final int userId = request.getUserId(); + if (DEBUG_INSTALL) { + Log.v(TAG, + "restoreAndPostInstall userId=" + userId + " package=" + request.getPkg()); + } + + PackageSetting packageSetting = null; + + final boolean update = request.isUpdate(); + boolean doRestore = false; + if (request.getPkg() != null && !request.isArchived()) { + // A restore should be requested at this point: + // if the install succeeded and it's not an archived install + if (!update) { + // AND the operation is not an update, + doRestore = true; + } else { + // OR the package has never been restored. + String packageName = request.getPkg().getPackageName(); + synchronized (mPm.mLock) { + packageSetting = mPm.mSettings.getPackageLPr(packageName); + if (packageSetting != null && packageSetting.isPendingRestore()) { + doRestore = true; + } + } + } + } + + // Set up the post-install work request bookkeeping. This will be used + // and cleaned up by the post-install event handling regardless of whether + // there's a restore pass performed. Token values are >= 1. + int token; + if (mPm.mNextInstallToken < 0) mPm.mNextInstallToken = 1; + token = mPm.mNextInstallToken++; + mPm.mRunningInstalls.put(token, request); + + if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token); + + final boolean succeeded = request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED; + if (succeeded && doRestore) { + // Pass responsibility to the Backup Manager. It will perform a + // restore if appropriate, then pass responsibility back to the + // Package Manager to run the post-install observer callbacks + // and broadcasts. + request.closeFreezer(); + doRestore = performBackupManagerRestore(userId, token, request); + } + + // If this is an update to a package that might be potentially downgraded, then we + // need to check with the rollback manager whether there's any userdata that might + // need to be snapshotted or restored for the package. + // + // TODO(narayan): Get this working for cases where userId == UserHandle.USER_ALL. + if (succeeded && !doRestore && update) { + doRestore = performRollbackManagerRestore(userId, token, request); + } + + if (succeeded && !request.hasPostInstallRunnable()) { + boolean hasNeverBeenRestored = + packageSetting != null && packageSetting.isPendingRestore(); + request.setPostInstallRunnable(() -> { + // Permissions should be restored on each user that has the app installed for the + // first time, unless it's an unarchive install for an archived app, in which case + // the permissions should be restored on each user that has the app updated. + int[] userIdsToRestorePermissions = hasNeverBeenRestored + ? request.getUpdateBroadcastUserIds() + : request.getFirstTimeBroadcastUserIds(); + for (int restorePermissionUserId : userIdsToRestorePermissions) { + mPm.restorePermissionsAndUpdateRolesForNewUserInstall(request.getName(), + restorePermissionUserId); + } + }); + } + + if (doRestore) { + if (packageSetting != null) { + synchronized (mPm.mLock) { + packageSetting.setPendingRestore(false); + } + } + } else { + // 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 = mPm.mHandler.obtainMessage(POST_INSTALL, token, 0); + mPm.mHandler.sendMessage(msg); + } + } + + /** + * Perform Backup Manager restore for a given {@link InstallRequest}. + * Returns whether the restore successfully completed. + */ + private boolean performBackupManagerRestore(int userId, int token, InstallRequest request) { + if (request.getPkg() == null) { + return false; + } + IBackupManager iBackupManager = mInjector.getIBackupManager(); + if (iBackupManager != null) { + // For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM + // in the BackupManager. USER_ALL is used in compatibility tests. + if (userId == UserHandle.USER_ALL) { + userId = UserHandle.USER_SYSTEM; + } + if (DEBUG_INSTALL) { + Log.v(TAG, "token " + token + " to BM for possible restore for user " + userId); + } + Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token); + try { + if (iBackupManager.isUserReadyForBackup(userId)) { + iBackupManager.restoreAtInstallForUser( + userId, request.getPkg().getPackageName(), token); + } else { + Slog.w(TAG, "User " + userId + " is not ready. Restore at install " + + "didn't take place."); + return false; + } + } catch (RemoteException e) { + // can't happen; the backup manager is local + } catch (Exception e) { + Slog.e(TAG, "Exception trying to enqueue restore", e); + return false; + } + } else { + Slog.e(TAG, "Backup Manager not found!"); + return false; + } + return true; + } + + /** + * Perform Rollback Manager restore for a given {@link InstallRequest}. + * Returns whether the restore successfully completed. + */ + private boolean performRollbackManagerRestore(int userId, int token, InstallRequest request) { + if (request.getPkg() == null) { + return false; + } + final String packageName = request.getPkg().getPackageName(); + final int[] allUsers = mPm.mUserManager.getUserIds(); + final int[] installedUsers; + + final PackageSetting ps; + int appId = -1; + long ceDataInode = -1; + synchronized (mPm.mLock) { + ps = mPm.mSettings.getPackageLPr(packageName); + if (ps != null) { + appId = ps.getAppId(); + ceDataInode = ps.getCeDataInode(userId); + // NOTE: We ignore the user specified in the InstallParam because we know this is + // an update, and hence need to restore data for all installed users. + installedUsers = ps.queryInstalledUsers(allUsers, true); + } else { + installedUsers = new int[0]; + } + } + + final int installFlags = request.getInstallFlags(); + boolean doSnapshotOrRestore = ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0 + || (installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0); + + if (ps != null && doSnapshotOrRestore) { + final String seInfo = ps.getSeInfo(); + final RollbackManagerInternal rollbackManager = + mInjector.getLocalService(RollbackManagerInternal.class); + rollbackManager.snapshotAndRestoreUserData(packageName, + UserHandle.toUserHandles(installedUsers), appId, ceDataInode, seInfo, token); + return true; + } + return false; + } + + void installPackagesTraced(List requests) { + synchronized (mPm.mInstallLock) { + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages"); + installPackagesLI(requests); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + } + + /** + * Installs one or more packages atomically. This operation is broken up into four phases: + *

    + *
  • Prepare + *
    Analyzes any current install state, parses the package and does initial + * validation on it.
  • + *
  • Scan + *
    Interrogates the parsed packages given the context collected in prepare.
  • + *
  • Reconcile + *
    Validates scanned packages in the context of each other and the current system + * state to ensure that the install will be successful. + *
  • Commit + *
    Commits all scanned packages and updates system state. This is the only place + * that system state may be modified in the install flow and all predictable errors + * must be determined before this phase.
  • + *
+ * + * Failure at any phase will result in a full failure to install all packages. + */ + @GuardedBy("mPm.mInstallLock") + private void installPackagesLI(List requests) { + final Set scannedPackages = new ArraySet<>(requests.size()); + final Map versionInfos = new ArrayMap<>(requests.size()); + final Map createdAppId = new ArrayMap<>(requests.size()); + CriticalEventLog.getInstance().logInstallPackagesStarted(); + boolean success = false; + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI"); + for (InstallRequest request : requests) { + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage"); + request.onPrepareStarted(); + preparePackageLI(request); + } catch (PrepareFailure prepareFailure) { + request.setError(prepareFailure.error, + prepareFailure.getMessage()); + request.setOriginPackage(prepareFailure.mConflictingPackage); + request.setOriginPermission(prepareFailure.mConflictingPermission); + return; + } finally { + request.onPrepareFinished(); + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + final ParsedPackage packageToScan = request.getParsedPackage(); + if (packageToScan == null) { + request.setError(INSTALL_FAILED_SESSION_INVALID, + "Failed to obtain package to scan"); + return; + } + request.setReturnCode(PackageManager.INSTALL_SUCCEEDED); + final String packageName = packageToScan.getPackageName(); + try { + request.onScanStarted(); + final ScanResult scanResult = scanPackageTracedLI(request.getParsedPackage(), + request.getParseFlags(), request.getScanFlags(), + System.currentTimeMillis(), request.getUser(), + request.getAbiOverride()); + request.setScanResult(scanResult); + request.onScanFinished(); + if (!scannedPackages.add(packageName)) { + request.setError( + PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE, + "Duplicate package " + + packageName + + " in multi-package install request."); + return; + } + if (!checkNoAppStorageIsConsistent( + request.getScanRequestOldPackage(), packageToScan)) { + // TODO: INSTALL_FAILED_UPDATE_INCOMPATIBLE is about incomptabible + // signatures. Is there a better error code? + request.setError( + INSTALL_FAILED_UPDATE_INCOMPATIBLE, + "Update attempted to change value of " + + PackageManager.PROPERTY_NO_APP_DATA_STORAGE); + return; + } + final boolean isApex = (request.getScanFlags() & SCAN_AS_APEX) != 0; + final boolean isSdkLibrary = packageToScan.isSdkLibrary(); + if (isApex || (isSdkLibrary && disallowSdkLibsToBeApps())) { + request.getScannedPackageSetting().setAppId(Process.INVALID_UID); + } else { + createdAppId.put(packageName, optimisticallyRegisterAppId(request)); + } + versionInfos.put(packageName, + mPm.getSettingsVersionForPackage(packageToScan)); + } catch (PackageManagerException e) { + request.setError("Scanning Failed.", e); + return; + } + } + + List reconciledPackages; + synchronized (mPm.mLock) { + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages"); + reconciledPackages = ReconcilePackageUtils.reconcilePackages( + requests, Collections.unmodifiableMap(mPm.mPackages), + versionInfos, mSharedLibraries, mPm.mSettings.getKeySetManagerService(), + mPm.mSettings, mPm.mInjector.getSystemConfig()); + } catch (ReconcileFailure e) { + for (InstallRequest request : requests) { + request.setError("Reconciliation failed...", e); + } + return; + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + if (Flags.improveInstallFreeze()) { + // Postpone freezer until after reconcile + for (ReconciledPackage reconciledPkg : reconciledPackages) { + InstallRequest installRequest = reconciledPkg.mInstallRequest; + String packageName = installRequest.getParsedPackage().getPackageName(); + PackageFreezer freezer = freezePackageForInstall(packageName, + UserHandle.USER_ALL, installRequest.getInstallFlags(), + "installPackageLI", ApplicationExitInfo.REASON_PACKAGE_UPDATED, + installRequest); + installRequest.setFreezer(freezer); + } + } + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages"); + commitPackagesLocked(reconciledPackages, mPm.mUserManager.getUserIds()); + success = true; + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + executePostCommitStepsLIF(reconciledPackages); + } finally { + if (success) { + for (InstallRequest request : requests) { + if (request.getDataLoaderType() != DataLoaderType.INCREMENTAL) { + continue; + } + if (request.getSignatureSchemeVersion() != SIGNING_BLOCK_V4) { + continue; + } + // For incremental installs, we bypass the verifier prior to install. Now + // that we know the package is valid, send a notice to the verifier with + // the root hash of the base.apk. + final String baseCodePath = request.getPkg().getBaseApkPath(); + final String[] splitCodePaths = request.getPkg().getSplitCodePaths(); + final Uri originUri = request.getOriginUri(); + final int verificationId = mPm.mPendingVerificationToken++; + final String rootHashString = PackageManagerServiceUtils + .buildVerificationRootHashString(baseCodePath, splitCodePaths); + VerificationUtils.broadcastPackageVerified(verificationId, originUri, + PackageManager.VERIFICATION_ALLOW, rootHashString, + request.getDataLoaderType(), request.getUser(), mContext); + } + } else { + for (InstallRequest installRequest : requests) { + if (installRequest.getParsedPackage() != null && createdAppId.getOrDefault( + installRequest.getParsedPackage().getPackageName(), false)) { + cleanUpAppIdCreation(installRequest); + } + } + // TODO(b/194319951): create a more descriptive reason than unknown + // mark all non-failure installs as UNKNOWN so we do not treat them as success + for (InstallRequest request : requests) { + request.closeFreezer(); + if (request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) { + request.setReturnCode(PackageManager.INSTALL_UNKNOWN); + } + } + } + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + @GuardedBy("mPm.mInstallLock") + private boolean checkNoAppStorageIsConsistent(AndroidPackage oldPkg, AndroidPackage newPkg) { + if (oldPkg == null) { + // New install, nothing to check against. + return true; + } + final PackageManager.Property curProp = + oldPkg.getProperties().get(PackageManager.PROPERTY_NO_APP_DATA_STORAGE); + final PackageManager.Property newProp = + newPkg.getProperties().get(PackageManager.PROPERTY_NO_APP_DATA_STORAGE); + if (curProp == null || !curProp.getBoolean()) { + return newProp == null || !newProp.getBoolean(); + } + return newProp != null && newProp.getBoolean(); + } + + @GuardedBy("mPm.mInstallLock") + private void preparePackageLI(InstallRequest request) throws PrepareFailure { + final int[] allUsers = mPm.mUserManager.getUserIds(); + final int installFlags = request.getInstallFlags(); + final boolean onExternal = request.getVolumeUuid() != null; + final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0); + final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0); + final boolean virtualPreload = + ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0); + final boolean isApex = ((installFlags & PackageManager.INSTALL_APEX) != 0); + final boolean isRollback = + request.getInstallReason() == PackageManager.INSTALL_REASON_ROLLBACK; + @PackageManagerService.ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE; + if (request.isInstallMove()) { + // moving a complete application; perform an initial scan on the new install location + scanFlags |= SCAN_INITIAL; + } + if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) { + scanFlags |= SCAN_DONT_KILL_APP; + } + if (instantApp) { + scanFlags |= SCAN_AS_INSTANT_APP; + } + if (fullApp) { + scanFlags |= SCAN_AS_FULL_APP; + } + if (virtualPreload) { + scanFlags |= SCAN_AS_VIRTUAL_PRELOAD; + } + if (isApex) { + scanFlags |= SCAN_AS_APEX; + } + + final File tmpPackageFile = new File( + isApex ? request.getApexInfo().modulePath : request.getCodePath()); + if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile); + + // Validity check + if (instantApp && onExternal) { + Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal); + throw new PrepareFailure(PackageManager.INSTALL_FAILED_SESSION_INVALID); + } + + // Retrieve PackageSettings and parse package + @ParsingPackageUtils.ParseFlags final int parseFlags = + mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY + | ParsingPackageUtils.PARSE_ENFORCE_CODE + | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0); + + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); + final ParsedPackage parsedPackage; + final ArchivedPackageParcel archivedPackage; + try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) { + if (request.getPackageLite() == null || !request.isArchived()) { + // TODO: pass packageLite from install request instead of reparsing the package + parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false); + AndroidPackageUtils.validatePackageDexMetadata(parsedPackage); + archivedPackage = null; + } else { + // Archived install mode, no APK. + parsedPackage = pp.parsePackageFromPackageLite(request.getPackageLite(), + parseFlags); + archivedPackage = request.getPackageLite().getArchivedPackage(); + } + } catch (PackageParserException e) { + throw new PrepareFailure(e.error, + ExceptionUtils.getCompleteMessage("Failed parse during installPackageLI", e)); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + // Block the install of apps using a lower target SDK version than required. + // This helps improve security and privacy as malware can target older SDK versions + // to avoid enforcement of new API behavior. + boolean bypassLowTargetSdkBlock = + ((installFlags & PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK) != 0); + + // Skip enforcement when the testOnly flag is set + if (!bypassLowTargetSdkBlock && parsedPackage.isTestOnly()) { + bypassLowTargetSdkBlock = true; + } + + // Enforce the low target sdk install block except when + // the --bypass-low-target-sdk-block is set for the install + if (!bypassLowTargetSdkBlock + && parsedPackage.getTargetSdkVersion() < MIN_INSTALLABLE_TARGET_SDK) { + Slog.w(TAG, "App " + parsedPackage.getPackageName() + + " targets deprecated sdk version"); + throw new PrepareFailure(INSTALL_FAILED_DEPRECATED_SDK_VERSION, + "App package must target at least SDK version " + + MIN_INSTALLABLE_TARGET_SDK + ", but found " + + parsedPackage.getTargetSdkVersion()); + } + + // Instant apps have several additional install-time checks. + if (instantApp) { + if (parsedPackage.getTargetSdkVersion() < Build.VERSION_CODES.O) { + Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName() + + " does not target at least O"); + throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID, + "Instant app package must target at least O"); + } + if (parsedPackage.getSharedUserId() != null) { + Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName() + + " may not declare sharedUserId."); + throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID, + "Instant app package may not declare a sharedUserId"); + } + } + + if (parsedPackage.isStaticSharedLibrary()) { + // Static shared libraries have synthetic package names + PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage); + + // No static shared libs on external storage + if (onExternal) { + Slog.i(TAG, "Static shared libs can only be installed on internal storage."); + throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION, + "Static shared libs can only be installed on internal storage."); + } + } + + String pkgName = parsedPackage.getPackageName(); + request.setName(pkgName); + if (parsedPackage.isTestOnly()) { + if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) { + throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, + "Failed to install test-only apk. Did you forget to add -t?"); + } + } + + // either use what we've been given or parse directly from the APK + if (request.getSigningDetails() != SigningDetails.UNKNOWN) { + parsedPackage.setSigningDetails(request.getSigningDetails()); + } else { + final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); + final ParseResult result = ParsingPackageUtils.getSigningDetails( + input, parsedPackage, false /*skipVerify*/); + if (result.isError()) { + throw new PrepareFailure("Failed collect during installPackageLI", + result.getException()); + } + parsedPackage.setSigningDetails(result.getResult()); + } + + if (instantApp && parsedPackage.getSigningDetails().getSignatureSchemeVersion() + < SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2) { + Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName() + + " is not signed with at least APK Signature Scheme v2"); + throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID, + "Instant app package must be signed with APK Signature Scheme v2 or greater"); + } + + boolean systemApp = false; + boolean replace = false; + synchronized (mPm.mLock) { + final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName); + // Check if installing already existing package + if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { + String oldName = mPm.mSettings.getRenamedPackageLPr(pkgName); + if (parsedPackage.getOriginalPackages().contains(oldName) + && mPm.mPackages.containsKey(oldName)) { + // This package is derived from an original package, + // and this device has been updating from that original + // name. We must continue using the original name, so + // rename the new package here. + parsedPackage.setPackageName(oldName); + pkgName = parsedPackage.getPackageName(); + replace = true; + if (DEBUG_INSTALL) { + Slog.d(TAG, "Replacing existing renamed package: oldName=" + + oldName + " pkgName=" + pkgName); + } + } else if (ps != null) { + // This package, under its official name, already exists + // on the device; we should replace it. + replace = true; + if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing package: " + pkgName); + } + if (replace) { + // Prevent apps opting out from runtime permissions + final int oldTargetSdk = ps.getTargetSdkVersion(); + final int newTargetSdk = parsedPackage.getTargetSdkVersion(); + if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1 + && newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) { + throw new PrepareFailure( + PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE, + "Package " + parsedPackage.getPackageName() + + " new target SDK " + newTargetSdk + + " doesn't support runtime permissions but the old" + + " target SDK " + oldTargetSdk + " does."); + } + // Prevent persistent apps from being updated + if (ps.isPersistent() + && ((installFlags & PackageManager.INSTALL_STAGED) == 0)) { + throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK, + "Package " + pkgName + " is a persistent app. " + + "Persistent apps are not updateable."); + } + } + } + + PackageSetting signatureCheckPs = ps; + + // SDK libs can have other major versions with different package names. + if (signatureCheckPs == null && parsedPackage.isSdkLibrary()) { + WatchedLongSparseArray libraryInfos = + mSharedLibraries.getSharedLibraryInfos( + parsedPackage.getSdkLibraryName()); + if (libraryInfos != null && libraryInfos.size() > 0) { + // Any existing version would do. + SharedLibraryInfo libraryInfo = libraryInfos.valueAt(0); + signatureCheckPs = mPm.mSettings.getPackageLPr(libraryInfo.getPackageName()); + } + } + + // 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. + if (parsedPackage.isStaticSharedLibrary()) { + SharedLibraryInfo libraryInfo = + mSharedLibraries.getLatestStaticSharedLibraVersion(parsedPackage); + if (libraryInfo != null) { + signatureCheckPs = mPm.mSettings.getPackageLPr(libraryInfo.getPackageName()); + } + } + + if (signatureCheckPs != null) { + if (DEBUG_INSTALL) { + Slog.d(TAG, + "Existing package for signature checking: " + signatureCheckPs); + } + + // Quick validity check that we're signed correctly if updating; + // we'll check this again later when scanning, but we want to + // bail early here before tripping over redefined permissions. + final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService(); + final SharedUserSetting signatureCheckSus = mPm.mSettings.getSharedUserSettingLPr( + signatureCheckPs); + if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, signatureCheckSus, + scanFlags)) { + if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) { + throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + + parsedPackage.getPackageName() + " upgrade keys do not match the " + + "previously installed version"); + } + } else { + try { + final boolean compareCompat = + ReconcilePackageUtils.isCompatSignatureUpdateNeeded( + mPm.getSettingsVersionForPackage(parsedPackage)); + final boolean compareRecover = + ReconcilePackageUtils.isRecoverSignatureUpdateNeeded( + mPm.getSettingsVersionForPackage(parsedPackage)); + // We don't care about disabledPkgSetting on install for now. + final boolean compatMatch = + PackageManagerServiceUtils.verifySignatures(signatureCheckPs, + signatureCheckSus, null, + parsedPackage.getSigningDetails(), compareCompat, compareRecover, + isRollback); + // The new KeySets will be re-added later in the scanning process. + if (compatMatch) { + synchronized (mPm.mLock) { + ksms.removeAppKeySetDataLPw(parsedPackage.getPackageName()); + } + } + } catch (PackageManagerException e) { + throw new PrepareFailure(e.error, e.getMessage()); + } + } + } + + if (ps != null) { + if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps); + + systemApp = ps.isSystem(); + request.setOriginUsers(ps.queryUsersInstalledOrHasData( + allUsers)); + } + + final int numGroups = ArrayUtils.size(parsedPackage.getPermissionGroups()); + for (int groupNum = 0; groupNum < numGroups; groupNum++) { + final ParsedPermissionGroup group = + parsedPackage.getPermissionGroups().get(groupNum); + final PermissionGroupInfo sourceGroup = mPm.getPermissionGroupInfo(group.getName(), + 0); + + if (sourceGroup != null && cannotInstallWithBadPermissionGroups(parsedPackage)) { + final String sourcePackageName = sourceGroup.packageName; + + if ((replace || !parsedPackage.getPackageName().equals(sourcePackageName)) + && !doesSignatureMatchForPermissions(sourcePackageName, parsedPackage, + scanFlags)) { + EventLog.writeEvent(0x534e4554, "146211400", -1, + parsedPackage.getPackageName()); + + throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP, + "Package " + + parsedPackage.getPackageName() + + " attempting to redeclare permission group " + + group.getName() + " already owned by " + + sourcePackageName); + } + } + } + + // TODO: Move logic for checking permission compatibility into PermissionManagerService + final int n = ArrayUtils.size(parsedPackage.getPermissions()); + for (int i = n - 1; i >= 0; i--) { + final ParsedPermission perm = parsedPackage.getPermissions().get(i); + final Permission bp = mPm.mPermissionManager.getPermissionTEMP(perm.getName()); + + // Don't allow anyone but the system to define ephemeral permissions. + if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0 + && !systemApp) { + Slog.w(TAG, "Non-System package " + parsedPackage.getPackageName() + + " attempting to delcare ephemeral permission " + + perm.getName() + "; Removing ephemeral."); + ComponentMutateUtils.setProtectionLevel(perm, + perm.getProtectionLevel() & ~PermissionInfo.PROTECTION_FLAG_INSTANT); + } + + // Check whether the newly-scanned package wants to define an already-defined perm + if (bp != null) { + final String sourcePackageName = bp.getPackageName(); + + if (!doesSignatureMatchForPermissions(sourcePackageName, parsedPackage, + scanFlags)) { + // If the owning package is the system itself, we log but allow + // install to proceed; we fail the install on all other permission + // redefinitions. + if (!sourcePackageName.equals("android")) { + throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION, + "Package " + + parsedPackage.getPackageName() + + " attempting to redeclare permission " + + perm.getName() + " already owned by " + + sourcePackageName) + .conflictsWithExistingPermission(perm.getName(), + sourcePackageName); + } else { + Slog.w(TAG, "Package " + parsedPackage.getPackageName() + + " attempting to redeclare system permission " + + perm.getName() + "; ignoring new declaration"); + parsedPackage.removePermission(i); + } + } else if (!PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())) { + // Prevent apps to change protection level to dangerous from any other + // type as this would allow a privilege escalation where an app adds a + // normal/signature permission in other app's group and later redefines + // it as dangerous leading to the group auto-grant. + if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_MASK_BASE) + == PermissionInfo.PROTECTION_DANGEROUS) { + if (!bp.isRuntime()) { + Slog.w(TAG, "Package " + parsedPackage.getPackageName() + + " trying to change a non-runtime permission " + + perm.getName() + + " to runtime; keeping old protection level"); + ComponentMutateUtils.setProtectionLevel(perm, + bp.getProtectionLevel()); + } + } + } + } + + if (perm.getGroup() != null + && cannotInstallWithBadPermissionGroups(parsedPackage)) { + boolean isPermGroupDefinedByPackage = false; + for (int groupNum = 0; groupNum < numGroups; groupNum++) { + if (parsedPackage.getPermissionGroups().get(groupNum).getName() + .equals(perm.getGroup())) { + isPermGroupDefinedByPackage = true; + break; + } + } + + if (!isPermGroupDefinedByPackage) { + final PermissionGroupInfo sourceGroup = + mPm.getPermissionGroupInfo(perm.getGroup(), 0); + + if (sourceGroup == null) { + EventLog.writeEvent(0x534e4554, "146211400", -1, + parsedPackage.getPackageName()); + + throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP, + "Package " + + parsedPackage.getPackageName() + + " attempting to declare permission " + + perm.getName() + " in non-existing group " + + perm.getGroup()); + } else { + String groupSourcePackageName = sourceGroup.packageName; + + if (!PLATFORM_PACKAGE_NAME.equals(groupSourcePackageName) + && !doesSignatureMatchForPermissions(groupSourcePackageName, + parsedPackage, scanFlags)) { + EventLog.writeEvent(0x534e4554, "146211400", -1, + parsedPackage.getPackageName()); + + throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP, + "Package " + + parsedPackage.getPackageName() + + " attempting to declare permission " + + perm.getName() + " in group " + + perm.getGroup() + " owned by package " + + groupSourcePackageName + + " with incompatible certificate"); + } + } + } + } + } + } + + if (systemApp) { + if (onExternal) { + // Abort update; system app can't be replaced with app on sdcard + throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION, + "Cannot install updates to system apps on sdcard"); + } else if (instantApp) { + // Abort update; system app can't be replaced with an instant app + throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID, + "Cannot update a system app with an instant app"); + } + } + + if (request.isInstallMove()) { + // We did an in-place move, so dex is ready to roll + scanFlags |= SCAN_NO_DEX; + scanFlags |= SCAN_MOVE; + + synchronized (mPm.mLock) { + final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName); + if (ps == null) { + request.setError(PackageManagerException.ofInternalError( + "Missing settings for moved package " + pkgName, + PackageManagerException.INTERNAL_ERROR_MISSING_SETTING_FOR_MOVE)); + } + + // We moved the entire application as-is, so bring over the + // previously derived ABI information. + parsedPackage.setPrimaryCpuAbi(ps.getPrimaryCpuAbiLegacy()) + .setSecondaryCpuAbi(ps.getSecondaryCpuAbiLegacy()); + } + + } else { + // Enable SCAN_NO_DEX flag to skip dexopt at a later stage + scanFlags |= SCAN_NO_DEX; + // The native libs of Apex is located in apex_payload.img, don't need to parse it from + // the original apex file + if (!isApex) { + try { + PackageSetting pkgSetting; + synchronized (mPm.mLock) { + pkgSetting = mPm.mSettings.getPackageLPr(pkgName); + } + boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null + && pkgSetting.isUpdatedSystemApp(); + final String abiOverride = deriveAbiOverride(request.getAbiOverride()); + + // TODO: Are these system flags actually set properly at this stage? + boolean isUpdatedSystemAppInferred = + pkgSetting != null && pkgSetting.isSystem(); + // derivePackageAbi works OK for archived packages despite logging some errors. + final Pair + derivedAbi = mPackageAbiHelper.derivePackageAbi(parsedPackage, + systemApp, (isUpdatedSystemAppFromExistingSetting + || isUpdatedSystemAppInferred), abiOverride, + ScanPackageUtils.getAppLib32InstallDir()); + derivedAbi.first.applyTo(parsedPackage); + derivedAbi.second.applyTo(parsedPackage); + } catch (PackageManagerException pme) { + Slog.e(TAG, "Error deriving application ABI", pme); + throw PrepareFailure.ofInternalError( + "Error deriving application ABI: " + pme.getMessage(), + PackageManagerException.INTERNAL_ERROR_DERIVING_ABI); + } + } + } + + if (!isApex) { + doRenameLI(request, parsedPackage); + + try { + setUpFsVerity(parsedPackage); + } catch (Installer.InstallerException | IOException | DigestException + | NoSuchAlgorithmException e) { + throw PrepareFailure.ofInternalError( + "Failed to set up verity: " + e, + PackageManagerException.INTERNAL_ERROR_VERITY_SETUP); + } + } else { + // Use the path returned by apexd + parsedPackage.setPath(request.getApexInfo().modulePath); + parsedPackage.setBaseApkPath(request.getApexInfo().modulePath); + } + + PackageFreezer freezer = null; + if (!Flags.improveInstallFreeze()) { + freezer = freezePackageForInstall(pkgName, UserHandle.USER_ALL, installFlags, + "installPackageLI", ApplicationExitInfo.REASON_PACKAGE_UPDATED, request); + } + + + boolean shouldCloseFreezerBeforeReturn = true; + try { + final PackageSetting oldPackageState; + final AndroidPackage oldPackage; + String renamedPackage; + boolean sysPkg = false; + int targetScanFlags = scanFlags; + int targetParseFlags = parseFlags; + final PackageSetting ps; + final PackageSetting disabledPs; + final SharedUserSetting sharedUserSetting; + if (replace) { + final String pkgName11 = parsedPackage.getPackageName(); + synchronized (mPm.mLock) { + oldPackageState = mPm.mSettings.getPackageLPr(pkgName11); + } + oldPackage = oldPackageState.getAndroidPackage(); + if (parsedPackage.isStaticSharedLibrary()) { + // Static libs have a synthetic package name containing the version + // and cannot be updated as an update would get a new package name, + // unless this is installed from adb which is useful for development. + if (oldPackage != null + && (installFlags & PackageManager.INSTALL_FROM_ADB) == 0) { + throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PACKAGE, + "Packages declaring " + + "static-shared libs cannot be updated"); + } + } + + final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0; + + final int[] installedUsers; + final int[] uninstalledUsers; + + synchronized (mPm.mLock) { + if (DEBUG_INSTALL) { + Slog.d(TAG, + "replacePackageLI: new=" + parsedPackage + + ", old=" + oldPackageState.getName()); + } + + ps = mPm.mSettings.getPackageLPr(pkgName11); + disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(ps); + sharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(ps); + + // verify signatures are valid + final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService(); + if (ksms.shouldCheckUpgradeKeySetLocked(ps, sharedUserSetting, scanFlags)) { + if (!ksms.checkUpgradeKeySetLocked(ps, parsedPackage)) { + throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, + "New package not signed by keys specified by upgrade-keysets: " + + pkgName11); + } + } else { + SigningDetails parsedPkgSigningDetails = parsedPackage.getSigningDetails(); + SigningDetails oldPkgSigningDetails = oldPackageState.getSigningDetails(); + // default to original signature matching + if (!parsedPkgSigningDetails.checkCapability(oldPkgSigningDetails, + SigningDetails.CertCapabilities.INSTALLED_DATA) + && !oldPkgSigningDetails.checkCapability(parsedPkgSigningDetails, + SigningDetails.CertCapabilities.ROLLBACK)) { + // Allow the update to proceed if this is a rollback and the parsed + // package's current signing key is the current signer or in the lineage + // of the old package; this allows a rollback to a previously installed + // version after an app's signing key has been rotated without requiring + // the rollback capability on the previous signing key. + if (!isRollback || !oldPkgSigningDetails.hasAncestorOrSelf( + parsedPkgSigningDetails)) { + throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, + "New package has a different signature: " + pkgName11); + } + } + } + + // don't allow a system upgrade unless the upgrade hash matches + if (oldPackageState.getRestrictUpdateHash() != null + && oldPackageState.isSystem()) { + final byte[] digestBytes; + try { + final MessageDigest digest = MessageDigest.getInstance("SHA-512"); + updateDigest(digest, new File(parsedPackage.getBaseApkPath())); + if (!ArrayUtils.isEmpty(parsedPackage.getSplitCodePaths())) { + for (String path : parsedPackage.getSplitCodePaths()) { + updateDigest(digest, new File(path)); + } + } + digestBytes = digest.digest(); + } catch (NoSuchAlgorithmException | IOException e) { + throw new PrepareFailure(INSTALL_FAILED_INVALID_APK, + "Could not compute hash: " + pkgName11); + } + if (!Arrays.equals(oldPackageState.getRestrictUpdateHash(), digestBytes)) { + throw new PrepareFailure(INSTALL_FAILED_INVALID_APK, + "New package fails restrict-update check: " + pkgName11); + } + // retain upgrade restriction + parsedPackage.setRestrictUpdateHash( + oldPackageState.getRestrictUpdateHash()); + } + + if (oldPackage != null) { + // APK should not change its sharedUserId declarations + final var oldSharedUid = oldPackage.getSharedUserId() != null + ? oldPackage.getSharedUserId() : ""; + final var newSharedUid = parsedPackage.getSharedUserId() != null + ? parsedPackage.getSharedUserId() : ""; + if (!oldSharedUid.equals(newSharedUid)) { + throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED, + "Package " + parsedPackage.getPackageName() + + " shared user changed from " + + oldSharedUid + " to " + newSharedUid); + } + + // APK should not re-join shared UID + if (oldPackage.isLeavingSharedUser() + && !parsedPackage.isLeavingSharedUser()) { + throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED, + "Package " + parsedPackage.getPackageName() + + " attempting to rejoin " + newSharedUid); + } + } + + // In case of rollback, remember per-user/profile install state + installedUsers = ps.queryInstalledUsers(allUsers, true); + uninstalledUsers = ps.queryInstalledUsers(allUsers, false); + + // don't allow an upgrade from full to ephemeral + if (isInstantApp) { + if (request.getUserId() == UserHandle.USER_ALL) { + for (int currentUser : allUsers) { + if (!ps.getInstantApp(currentUser)) { + // can't downgrade from full to instant + Slog.w(TAG, + "Can't replace full app with instant app: " + pkgName11 + + " for user: " + currentUser); + throw new PrepareFailure( + PackageManager.INSTALL_FAILED_SESSION_INVALID); + } + } + } else if (!ps.getInstantApp(request.getUserId())) { + // can't downgrade from full to instant + Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11 + + " for user: " + request.getUserId()); + throw new PrepareFailure( + PackageManager.INSTALL_FAILED_SESSION_INVALID); + } + } + } + + // Update what is removed + PackageRemovedInfo removedInfo = new PackageRemovedInfo(); + removedInfo.mUid = ps.getAppId(); + removedInfo.mRemovedPackage = ps.getPackageName(); + removedInfo.mInstallerPackageName = + ps.getInstallSource().mInstallerPackageName; + removedInfo.mIsStaticSharedLib = + parsedPackage.getStaticSharedLibraryName() != null; + removedInfo.mIsUpdate = true; + removedInfo.mOrigUsers = installedUsers; + removedInfo.mInstallReasons = new SparseIntArray(installedUsers.length); + for (int i = 0; i < installedUsers.length; i++) { + final int userId = installedUsers[i]; + removedInfo.mInstallReasons.put(userId, + ps.getInstallReason(userId)); + } + removedInfo.mUninstallReasons = new SparseIntArray(uninstalledUsers.length); + for (int i = 0; i < uninstalledUsers.length; i++) { + final int userId = uninstalledUsers[i]; + removedInfo.mUninstallReasons.put(userId, + ps.getUninstallReason(userId)); + } + removedInfo.mIsExternal = oldPackageState.isExternalStorage(); + removedInfo.mRemovedPackageVersionCode = oldPackageState.getVersionCode(); + request.setRemovedInfo(removedInfo); + + sysPkg = oldPackageState.isSystem(); + if (sysPkg) { + // Set the system/privileged/oem/vendor/product flags as needed + final boolean privileged = oldPackageState.isPrivileged(); + final boolean oem = oldPackageState.isOem(); + final boolean vendor = oldPackageState.isVendor(); + final boolean product = oldPackageState.isProduct(); + final boolean odm = oldPackageState.isOdm(); + final boolean systemExt = oldPackageState.isSystemExt(); + final @ParsingPackageUtils.ParseFlags int systemParseFlags = parseFlags; + final @PackageManagerService.ScanFlags int systemScanFlags = scanFlags + | SCAN_AS_SYSTEM + | (privileged ? SCAN_AS_PRIVILEGED : 0) + | (oem ? SCAN_AS_OEM : 0) + | (vendor ? SCAN_AS_VENDOR : 0) + | (product ? SCAN_AS_PRODUCT : 0) + | (odm ? SCAN_AS_ODM : 0) + | (systemExt ? SCAN_AS_SYSTEM_EXT : 0); + + if (DEBUG_INSTALL) { + Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage + + ", old=" + oldPackageState.getName()); + } + request.setReturnCode(PackageManager.INSTALL_SUCCEEDED); + request.setApexModuleName(oldPackageState.getApexModuleName()); + targetParseFlags = systemParseFlags; + targetScanFlags = systemScanFlags; + } else { // non system replace + if (DEBUG_INSTALL) { + Slog.d(TAG, + "replaceNonSystemPackageLI: new=" + parsedPackage + ", old=" + + oldPackageState.getName()); + } + } + } else { // new package install + ps = null; + disabledPs = null; + oldPackageState = null; + // Remember this for later, in case we need to rollback this install + String pkgName1 = parsedPackage.getPackageName(); + + if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + parsedPackage); + + // TODO(b/194319951): MOVE TO RECONCILE + synchronized (mPm.mLock) { + renamedPackage = mPm.mSettings.getRenamedPackageLPr(pkgName1); + if (renamedPackage != null) { + // A package with the same name is already installed, though + // it has been renamed to an older name. The package we + // are trying to install should be installed as an update to + // the existing one, but that has not been requested, so bail. + throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS, + "Attempt to re-install " + pkgName1 + + " without first uninstalling package running as " + + renamedPackage); + } + if (mPm.mPackages.containsKey(pkgName1)) { + // Don't allow installation over an existing package with the same name. + throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS, + "Attempt to re-install " + pkgName1 + + " without first uninstalling."); + } + } + } + // we're passing the freezer back to be closed in a later phase of install + shouldCloseFreezerBeforeReturn = false; + + request.setPrepareResult(replace, targetScanFlags, targetParseFlags, + oldPackageState, parsedPackage, archivedPackage, + replace /* clearCodeCache */, sysPkg, ps, disabledPs); + } finally { + if (freezer != null) { + request.setFreezer(freezer); + if (shouldCloseFreezerBeforeReturn) { + freezer.close(); + } + } + } + } + + /** + * Rename package into final resting place. All paths on the given + * scanned package should be updated to reflect the rename. + */ + @GuardedBy("mPm.mInstallLock") + private void doRenameLI(InstallRequest request, + ParsedPackage parsedPackage) throws PrepareFailure { + final int status = request.getReturnCode(); + final String statusMsg = request.getReturnMsg(); + if (request.isInstallMove()) { + if (status != PackageManager.INSTALL_SUCCEEDED) { + mRemovePackageHelper.cleanUpForMoveInstall(request.getMoveToUuid(), + request.getMovePackageName(), request.getMoveFromCodePath()); + throw new PrepareFailure(status, statusMsg); + } + return; + } + // For file installations + if (status != PackageManager.INSTALL_SUCCEEDED) { + mRemovePackageHelper.removeCodePath(request.getCodeFile()); + throw new PrepareFailure(status, statusMsg); + } + + final File targetDir = resolveTargetDir(request.getInstallFlags(), request.getCodeFile()); + final File beforeCodeFile = request.getCodeFile(); + final File afterCodeFile = PackageManagerServiceUtils.getNextCodePath(targetDir, + parsedPackage.getPackageName()); + + if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile); + final boolean onIncremental = mPm.mIncrementalManager != null + && isIncrementalPath(beforeCodeFile.getAbsolutePath()); + try { + makeDirRecursive(afterCodeFile.getParentFile(), 0771); + if (onIncremental) { + // Just link files here. The stage dir will be removed when the installation + // session is completed. + mPm.mIncrementalManager.linkCodePath(beforeCodeFile, afterCodeFile); + } else { + Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath()); + } + } catch (IOException | ErrnoException e) { + Slog.w(TAG, "Failed to rename", e); + throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE, + "Failed to rename"); + } + + if (!onIncremental && !SELinux.restoreconRecursive(afterCodeFile)) { + Slog.w(TAG, "Failed to restorecon"); + throw new PrepareFailure(PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE, + "Failed to restorecon"); + } + + // Reflect the rename internally + request.setCodeFile(afterCodeFile); + + // Reflect the rename in scanned details + try { + parsedPackage.setPath(afterCodeFile.getCanonicalPath()); + } catch (IOException e) { + Slog.e(TAG, "Failed to get path: " + afterCodeFile, e); + throw new PrepareFailure(PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE, + "Failed to get path: " + afterCodeFile); + } + parsedPackage.setBaseApkPath(FileUtils.rewriteAfterRename(beforeCodeFile, + afterCodeFile, parsedPackage.getBaseApkPath())); + parsedPackage.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile, + afterCodeFile, parsedPackage.getSplitCodePaths())); + } + + // TODO(b/168126411): Once staged install flow starts using the same folder as non-staged + // flow, we won't need this method anymore. + private File resolveTargetDir(int installFlags, File codeFile) { + boolean isStagedInstall = (installFlags & INSTALL_STAGED) != 0; + if (isStagedInstall) { + return Environment.getDataAppDirectory(null); + } else { + return codeFile.getParentFile(); + } + } + + /* + * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges + * as this only works for packages that are installed + * + * TODO: Move logic for permission group compatibility into PermissionManagerService + */ + @SuppressWarnings("AndroidFrameworkCompatChange") + private static boolean cannotInstallWithBadPermissionGroups(ParsedPackage parsedPackage) { + return parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S; + } + + private boolean doesSignatureMatchForPermissions(@NonNull String sourcePackageName, + @NonNull ParsedPackage parsedPackage, int scanFlags) { + // If the defining package is signed with our cert, it's okay. This + // also includes the "updating the same package" case, of course. + // "updating same package" could also involve key-rotation. + + final PackageSetting sourcePackageSetting; + final KeySetManagerService ksms; + final SharedUserSetting sharedUserSetting; + synchronized (mPm.mLock) { + sourcePackageSetting = mPm.mSettings.getPackageLPr(sourcePackageName); + ksms = mPm.mSettings.getKeySetManagerService(); + sharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(sourcePackageSetting); + } + + final SigningDetails sourceSigningDetails = (sourcePackageSetting == null + ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails()); + if (sourcePackageName.equals(parsedPackage.getPackageName()) + && (ksms.shouldCheckUpgradeKeySetLocked( + sourcePackageSetting, sharedUserSetting, scanFlags))) { + return ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage); + } else { + + // in the event of signing certificate rotation, we need to see if the + // package's certificate has rotated from the current one, or if it is an + // older certificate with which the current is ok with sharing permissions + if (sourceSigningDetails.checkCapability( + parsedPackage.getSigningDetails(), + SigningDetails.CertCapabilities.PERMISSION)) { + return true; + } else if (parsedPackage.getSigningDetails().checkCapability( + sourceSigningDetails, + SigningDetails.CertCapabilities.PERMISSION)) { + // the scanned package checks out, has signing certificate rotation + // history, and is newer; bring it over + synchronized (mPm.mLock) { + sourcePackageSetting.setSigningDetails(parsedPackage.getSigningDetails()); + } + return true; + } else { + return false; + } + } + } + + /** + * Set up fs-verity for the given package. For older devices that do not support fs-verity, + * this is a no-op. + */ + private void setUpFsVerity(AndroidPackage pkg) throws Installer.InstallerException, + PrepareFailure, IOException, DigestException, NoSuchAlgorithmException { + if (!PackageManagerServiceUtils.isApkVerityEnabled()) { + return; + } + + if (isIncrementalPath(pkg.getPath()) && IncrementalManager.getVersion() + < IncrementalManager.MIN_VERSION_TO_SUPPORT_FSVERITY) { + return; + } + + // Collect files we care for fs-verity setup. + ArrayMap fsverityCandidates = new ArrayMap<>(); + fsverityCandidates.put(pkg.getBaseApkPath(), + VerityUtils.getFsveritySignatureFilePath(pkg.getBaseApkPath())); + + final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk( + pkg.getBaseApkPath()); + if (new File(dmPath).exists()) { + fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath)); + } + + for (String path : pkg.getSplitCodePaths()) { + fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path)); + + final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path); + if (new File(splitDmPath).exists()) { + fsverityCandidates.put(splitDmPath, + VerityUtils.getFsveritySignatureFilePath(splitDmPath)); + } + } + + var fis = FileIntegrityService.getService(); + for (Map.Entry entry : fsverityCandidates.entrySet()) { + try { + final String filePath = entry.getKey(); + if (VerityUtils.hasFsverity(filePath)) { + continue; + } + + final String signaturePath = entry.getValue(); + if (new File(signaturePath).exists()) { + // If signature is provided, enable fs-verity first so that the file can be + // measured for signature check below. + VerityUtils.setUpFsverity(filePath); + + if (!fis.verifyPkcs7DetachedSignature(signaturePath, filePath)) { + throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE, + "fs-verity signature does not verify against a known key"); + } + } + } catch (IOException e) { + throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE, + "Failed to enable fs-verity: " + e); + } + } + } + + private PackageFreezer freezePackageForInstall(String packageName, int userId, int installFlags, + String killReason, int exitInfoReason, InstallRequest request) { + if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) { + return new PackageFreezer(mPm, request); + } else { + return mPm.freezePackage(packageName, userId, killReason, exitInfoReason, request); + } + } + + private static void updateDigest(MessageDigest digest, File file) throws IOException { + try (DigestInputStream digestStream = + new DigestInputStream(new FileInputStream(file), digest)) { + int length, total = 0; + while ((length = digestStream.read()) != -1) { + total += length; + } // just plow through the file + } + } + + @GuardedBy("mPm.mLock") + private void commitPackagesLocked(List reconciledPackages, + @NonNull int[] allUsers) { + // TODO: remove any expected failures from this method; this should only be able to fail due + // to unavoidable errors (I/O, etc.) + for (ReconciledPackage reconciledPkg : reconciledPackages) { + final InstallRequest installRequest = reconciledPkg.mInstallRequest; + final ParsedPackage parsedPackage = installRequest.getParsedPackage(); + final String packageName = parsedPackage.getPackageName(); + + installRequest.onCommitStarted(); + if (installRequest.isInstallReplace()) { + AndroidPackage oldPackage = mPm.mPackages.get(packageName); + + // Set the update and install times + PackageStateInternal deletedPkgSetting = mPm.snapshotComputer() + .getPackageStateInternal(packageName); + // TODO(b/225756739): For rebootless APEX, consider using lastUpdateMillis provided + // by apexd to be more accurate. + installRequest.setScannedPackageSettingFirstInstallTimeFromReplaced( + deletedPkgSetting, allUsers); + installRequest.setScannedPackageSettingLastUpdateTime( + System.currentTimeMillis()); + + installRequest.getRemovedInfo().mBroadcastAllowList = + mPm.mAppsFilter.getVisibilityAllowList(mPm.snapshotComputer(), + installRequest.getScannedPackageSetting(), + allUsers, mPm.mSettings.getPackagesLocked()); + if (installRequest.isInstallSystem()) { + // Remove existing system package + mRemovePackageHelper.removePackage(oldPackage, true); + if (!disableSystemPackageLPw(oldPackage)) { + // We didn't need to disable the .apk as a current system package, + // which means we are replacing another update that is already + // installed. We need to make sure to delete the older one's .apk. + installRequest.getRemovedInfo().mArgs = new CleanUpArgs( + packageName, + oldPackage.getPath(), + getAppDexInstructionSets( + deletedPkgSetting.getPrimaryCpuAbi(), + deletedPkgSetting.getSecondaryCpuAbi())); + } else { + installRequest.getRemovedInfo().mArgs = null; + } + } else { + try { + // Settings will be written during the call to updateSettingsLI(). + mDeletePackageHelper.executeDeletePackage( + reconciledPkg.mDeletePackageAction, packageName, + true, allUsers, false); + } catch (SystemDeleteException e) { + if (mPm.mIsEngBuild) { + throw new RuntimeException("Unexpected failure", e); + // ignore; not possible for non-system app + } + } + + if (installRequest.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) { + PackageSetting ps2 = mPm.mSettings.getPackageLPr( + parsedPackage.getPackageName()); + if (ps2 != null) { + installRequest.getRemovedInfo().mRemovedForAllUsers = + mPm.mPackages.get(ps2.getPackageName()) == null; + } + } + } + } + + AndroidPackage pkg = commitReconciledScanResultLocked(reconciledPkg, allUsers); + updateSettingsLI(pkg, allUsers, installRequest); + + final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); + if (ps != null) { + installRequest.setNewUsers( + ps.queryInstalledUsers(allUsers, true)); + ps.setUpdateAvailable(false /*updateAvailable*/); + + File appMetadataFile = new File(ps.getPath(), APP_METADATA_FILE_NAME); + if (appMetadataFile.exists()) { + ps.setAppMetadataFilePath(appMetadataFile.getAbsolutePath()); + if (Flags.aslInApkAppMetadataSource()) { + ps.setAppMetadataSource(APP_METADATA_SOURCE_INSTALLER); + } + } else { + if (Flags.aslInApkAppMetadataSource() + && parsedPackage.isAppMetadataFileInApk()) { + ps.setAppMetadataFilePath(appMetadataFile.getAbsolutePath()); + ps.setAppMetadataSource(APP_METADATA_SOURCE_APK); + } else { + ps.setAppMetadataFilePath(null); + } + } + } + if (installRequest.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) { + // If this is an archival installation then we'll initialize the archive status, + // while also marking package as not installed. + // Doing this at the very end of the install as we are using ps.getInstalled + // to figure out which users were changed. + mPm.markPackageAsArchivedIfNeeded(ps, + installRequest.getArchivedPackage(), + installRequest.getNewUsers()); + mPm.updateSequenceNumberLP(ps, installRequest.getNewUsers()); + mPm.updateInstantAppInstallerLocked(packageName); + } + installRequest.onCommitFinished(); + } + ApplicationPackageManager.invalidateGetPackagesForUidCache(); + } + + @GuardedBy("mPm.mLock") + private boolean disableSystemPackageLPw(AndroidPackage oldPkg) { + return mPm.mSettings.disableSystemPackageLPw(oldPkg.getPackageName(), true); + } + + private void updateSettingsLI(AndroidPackage newPackage, + int[] allUsers, InstallRequest installRequest) { + updateSettingsInternalLI(newPackage, allUsers, installRequest); + } + + private void updateSettingsInternalLI(AndroidPackage pkg, + int[] allUsers, InstallRequest installRequest) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettingsInternal"); + + final String pkgName = pkg.getPackageName(); + final int[] installedForUsers = installRequest.getOriginUsers(); + final int installReason = installRequest.getInstallReason(); + final String installerPackageName = installRequest.getInstallerPackageName(); + + if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath()); + final int userId = installRequest.getUserId(); + if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT + && !mPm.mUserManager.exists(userId)) { + installRequest.setError(PackageManagerException.ofInternalError( + "User " + userId + " doesn't exist or has been removed", + PackageManagerException.INTERNAL_ERROR_MISSING_USER)); + return; + } + synchronized (mPm.mLock) { + // For system-bundled packages, we assume that installing an upgraded version + // of the package implies that the user actually wants to run that new code, + // so we enable the package. + final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName); + if (ps != null) { + if (ps.isSystem()) { + if (DEBUG_INSTALL) { + Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName); + } + // Enable system package for requested users + if (installedForUsers != null + && !installRequest.isApplicationEnabledSettingPersistent()) { + for (int origUserId : installedForUsers) { + if (userId == UserHandle.USER_ALL || userId == origUserId) { + ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, + origUserId, installerPackageName); + } + } + } + // Also convey the prior install/uninstall state + if (allUsers != null && installedForUsers != null) { + for (int currentUserId : allUsers) { + final boolean installed = ArrayUtils.contains( + installedForUsers, currentUserId); + if (DEBUG_INSTALL) { + Slog.d(TAG, " user " + currentUserId + " => " + installed); + } + ps.setInstalled(installed, currentUserId); + } + // these install state changes will be persisted in the + // upcoming call to mSettings.writeLPr(). + } + + if (allUsers != null) { + for (int currentUserId : allUsers) { + ps.resetOverrideComponentLabelIcon(currentUserId); + } + } + } + + // Retrieve the overlays for shared libraries of the package. + if (!ps.getPkgState().getUsesLibraryInfos().isEmpty()) { + for (SharedLibraryWrapper sharedLib : ps.getPkgState().getUsesLibraryInfos()) { + for (int currentUserId : allUsers) { + if (sharedLib.getType() != SharedLibraryInfo.TYPE_DYNAMIC) { + // TODO(146804378): Support overlaying static shared libraries + continue; + } + final PackageSetting libPs = mPm.mSettings.getPackageLPr( + sharedLib.getPackageName()); + if (libPs == null) { + continue; + } + ps.setOverlayPathsForLibrary(sharedLib.getName(), + libPs.getOverlayPaths(currentUserId), currentUserId); + } + } + } + + if (userId != UserHandle.USER_ALL) { + // It's implied that when a user requests installation, they want the app to + // be installed and enabled. The caller, however, can explicitly specify to + // keep the existing enabled state. + ps.setInstalled(true, userId); + if (!installRequest.isApplicationEnabledSettingPersistent()) { + ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, + installerPackageName); + } + // Clear any existing archive state. + mPm.mInstallerService.mPackageArchiver.clearArchiveState(ps, userId); + } else if (allUsers != null) { + // The caller explicitly specified INSTALL_ALL_USERS flag. + // Thus, updating the settings to install the app for all users. + for (int currentUserId : allUsers) { + // If the app is already installed for the currentUser, + // keep it as installed as we might be updating the app at this place. + // If not currently installed, check if the currentUser is restricted by + // DISALLOW_INSTALL_APPS or DISALLOW_DEBUGGING_FEATURES device policy. + // Install / update the app if the user isn't restricted. Skip otherwise. + final boolean installedForCurrentUser = ArrayUtils.contains( + installedForUsers, currentUserId); + final boolean restrictedByPolicy = + mPm.isUserRestricted(currentUserId, + UserManager.DISALLOW_INSTALL_APPS) + || mPm.isUserRestricted(currentUserId, + UserManager.DISALLOW_DEBUGGING_FEATURES); + if (installedForCurrentUser || !restrictedByPolicy) { + ps.setInstalled(true, currentUserId); + if (!installRequest.isApplicationEnabledSettingPersistent()) { + ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, currentUserId, + installerPackageName); + } + // Clear any existing archive state. + mPm.mInstallerService.mPackageArchiver.clearArchiveState(ps, + currentUserId); + } else { + ps.setInstalled(false, currentUserId); + } + } + } + + mPm.mSettings.addInstallerPackageNames(ps.getInstallSource()); + + // When replacing an existing package, preserve the original install reason for all + // users that had the package installed before. Similarly for uninstall reasons. + final Set previousUserIds = new ArraySet<>(); + if (installRequest.getRemovedInfo() != null + && installRequest.getRemovedInfo().mInstallReasons != null) { + final int installReasonCount = + installRequest.getRemovedInfo().mInstallReasons.size(); + for (int i = 0; i < installReasonCount; i++) { + final int previousUserId = + installRequest.getRemovedInfo().mInstallReasons.keyAt(i); + final int previousInstallReason = + installRequest.getRemovedInfo().mInstallReasons.valueAt(i); + ps.setInstallReason(previousInstallReason, previousUserId); + previousUserIds.add(previousUserId); + } + } + if (installRequest.getRemovedInfo() != null + && installRequest.getRemovedInfo().mUninstallReasons != null) { + for (int i = 0; i < installRequest.getRemovedInfo().mUninstallReasons.size(); + i++) { + final int previousUserId = + installRequest.getRemovedInfo().mUninstallReasons.keyAt(i); + final int previousReason = + installRequest.getRemovedInfo().mUninstallReasons.valueAt(i); + ps.setUninstallReason(previousReason, previousUserId); + } + } + + // Set install reason for users that are having the package newly installed. + if (userId == UserHandle.USER_ALL) { + for (int currentUserId : allUsers) { + if (!previousUserIds.contains(currentUserId) + && ps.getInstalled(currentUserId)) { + ps.setInstallReason(installReason, currentUserId); + } + } + } else if (!previousUserIds.contains(userId)) { + ps.setInstallReason(installReason, userId); + } + + // TODO(b/169721400): generalize Incremental States and create a Callback object + // that can be used for all the packages. + final String codePath = ps.getPathString(); + if (IncrementalManager.isIncrementalPath(codePath) + && mIncrementalManager != null) { + mIncrementalManager.registerLoadingProgressCallback(codePath, + new IncrementalProgressListener(ps.getPackageName(), mPm)); + } + + // Ensure that the uninstall reason is UNKNOWN for users with the package installed. + for (int currentUserId : allUsers) { + if (ps.getInstalled(currentUserId)) { + ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, currentUserId); + } + } + + mPm.mSettings.writeKernelMappingLPr(ps); + + final PermissionManagerServiceInternal.PackageInstalledParams.Builder + permissionParamsBuilder = + new PermissionManagerServiceInternal.PackageInstalledParams.Builder(); + final boolean grantRequestedPermissions = (installRequest.getInstallFlags() + & PackageManager.INSTALL_GRANT_ALL_REQUESTED_PERMISSIONS) != 0; + if (grantRequestedPermissions) { + var permissionStates = new ArrayMap(); + for (var permissionName : pkg.getRequestedPermissions()) { + permissionStates.put(permissionName, + PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED); + } + permissionParamsBuilder.setPermissionStates(permissionStates); + } else { + var permissionStates = installRequest.getPermissionStates(); + if (permissionStates != null) { + permissionParamsBuilder + .setPermissionStates(permissionStates); + } + } + final boolean allowlistAllRestrictedPermissions = + (installRequest.getInstallFlags() + & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0; + final List allowlistedRestrictedPermissions = + allowlistAllRestrictedPermissions + ? new ArrayList<>(pkg.getRequestedPermissions()) + : installRequest.getAllowlistedRestrictedPermissions(); + if (allowlistedRestrictedPermissions != null) { + permissionParamsBuilder.setAllowlistedRestrictedPermissions( + allowlistedRestrictedPermissions); + } + final int autoRevokePermissionsMode = installRequest.getAutoRevokePermissionsMode(); + permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode); + mPm.mPermissionManager.onPackageInstalled(pkg, installRequest.getPreviousAppId(), + permissionParamsBuilder.build(), userId); + } + installRequest.setName(pkgName); + installRequest.setAppId(pkg.getUid()); + installRequest.setPkg(pkg); + installRequest.setReturnCode(PackageManager.INSTALL_SUCCEEDED); + //to update install status + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings"); + mPm.writeSettingsLPrTEMP(); + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + private void enableRestrictedSettings(String pkgName, int appId, int userId) { + final AppOpsManager appOpsManager = mPm.mContext.getSystemService(AppOpsManager.class); + final int uid = UserHandle.getUid(userId, appId); + appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, + uid, + pkgName, + AppOpsManager.MODE_ERRORED); + } + + /** + * On successful install, executes remaining steps after commit completes and the package lock + * is released. These are typically more expensive or require calls to installd, which often + * locks on {@link com.android.server.pm.PackageManagerService.mLock}. + */ + @GuardedBy("mPm.mInstallLock") + private void executePostCommitStepsLIF(List reconciledPackages) { + final ArraySet incrementalStorages = new ArraySet<>(); + for (ReconciledPackage reconciledPkg : reconciledPackages) { + final InstallRequest installRequest = reconciledPkg.mInstallRequest; + final PackageSetting ps = installRequest.getScannedPackageSetting(); + final String packageName = ps.getPackageName(); + final String codePath = ps.getPathString(); + final AndroidPackage pkg = ps.getPkg(); + final boolean onIncremental = mIncrementalManager != null + && isIncrementalPath(codePath); + if (onIncremental) { + IncrementalStorage storage = mIncrementalManager.openStorage(codePath); + if (storage == null) { + throw new IllegalArgumentException( + "Install: null storage for incremental package " + packageName); + } + incrementalStorages.add(storage); + } + + // Hardcode previousAppId to 0 to disable any data migration (http://b/221088088) + mAppDataHelper.prepareAppDataPostCommitLIF(ps, 0, installRequest.getNewUsers()); + if (installRequest.isClearCodeCache()) { + mAppDataHelper.clearAppDataLIF(ps.getPkg(), UserHandle.USER_ALL, + FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL + | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); + } + if (installRequest.isInstallReplace() && pkg != null) { + mDexManager.notifyPackageUpdated(packageName, + pkg.getBaseApkPath(), pkg.getSplitCodePaths()); + } + + // ART Service handles this on demand instead. + if (!useArtService() && pkg != null) { + // Prepare the application profiles for the new code paths. + // This needs to be done before invoking dexopt so that any install-time profile + // can be used for optimizations. + try { + mArtManagerService.prepareAppProfiles(pkg, + mPm.resolveUserIds(installRequest.getUserId()), + /* updateReferenceProfileContent= */ true); + } catch (LegacyDexoptDisabledException e) { + throw new RuntimeException(e); + } + } + + // Construct the DexoptOptions early to see if we should skip running 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 = DexOptHelper.getDexoptOptionsByInstallRequest( + installRequest, mDexManager); + // Check whether we need to dexopt the app. + // + // NOTE: it is IMPORTANT to call dexopt: + // - after doRename which will sync the package data from AndroidPackage and + // its corresponding ApplicationInfo. + // - after installNewPackageLIF or replacePackageLIF which will update result with the + // uid of the application (pkg.applicationInfo.uid). + // This update happens in place! + // + // We only need to dexopt if the package meets ALL of the following conditions: + // 1) it is not an instant app or if it is then dexopt is enabled via gservices. + // 2) it is not debuggable. + // 3) it is not on Incremental File System. + // + // Note that we do not dexopt instant apps by default. dexopt can take some time to + // complete, so we skip this step during installation. Instead, we'll take extra time + // the first time the instant app starts. It's preferred to do it this way to provide + // continuous progress to the useur instead of mysteriously blocking somewhere in the + // middle of running an instant app. The default behaviour can be overridden + // via gservices. + // + // Furthermore, dexopt may be skipped, depending on the install scenario and current + // state of the device. + // + // TODO(b/174695087): instantApp and onIncremental should be removed and their install + // path moved to SCENARIO_FAST. + + final boolean performDexopt = DexOptHelper.shouldPerformDexopt(installRequest, + dexoptOptions, mContext); + if (performDexopt) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); + + // This mirrors logic from commitReconciledScanResultLocked, where the library files + // needed for dexopt are assigned. + PackageSetting realPkgSetting = installRequest.getRealPackageSetting(); + + // Unfortunately, the updated system app flag is only tracked on this PackageSetting + boolean isUpdatedSystemApp = + installRequest.getScannedPackageSetting().isUpdatedSystemApp(); + + realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp); + + if (useArtService()) { + DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService( + installRequest, dexoptOptions); + installRequest.onDexoptFinished(dexOptResult); + } else { + try { + mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting, + null /* instructionSets */, + mPm.getOrCreateCompilerPackageStats(pkg), + mDexManager.getPackageUseInfoOrDefault(packageName), dexoptOptions); + } catch (LegacyDexoptDisabledException e) { + throw new RuntimeException(e); + } + } + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + if (!useArtService()) { + // 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 denylist. + // ART Service currently doesn't support this and will retry packages in every + // background dexopt. + // TODO: Layering violation + try { + BackgroundDexOptService.getService().notifyPackageChanged(packageName); + } catch (LegacyDexoptDisabledException e) { + throw new RuntimeException(e); + } + } + } + PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental( + incrementalStorages); + } + + Pair verifyReplacingVersionCode(PackageInfoLite pkgLite, + long requiredInstalledVersionCode, int installFlags) { + if ((installFlags & PackageManager.INSTALL_APEX) != 0) { + return verifyReplacingVersionCodeForApex( + pkgLite, requiredInstalledVersionCode, installFlags); + } + + String packageName = pkgLite.packageName; + synchronized (mPm.mLock) { + // Package which currently owns the data that the new package will own if installed. + // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg + // will be null whereas dataOwnerPkg will contain information about the package + // which was uninstalled while keeping its data. + AndroidPackage dataOwnerPkg = mPm.mPackages.get(packageName); + PackageSetting dataOwnerPs = mPm.mSettings.getPackageLPr(packageName); + if (dataOwnerPkg == null) { + if (dataOwnerPs != null) { + dataOwnerPkg = dataOwnerPs.getPkg(); + } + } + + if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) { + if (dataOwnerPkg == null) { + String errorMsg = "Required installed version code was " + + requiredInstalledVersionCode + + " but package is not installed"; + Slog.w(TAG, errorMsg); + return Pair.create( + PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg); + } + + if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) { + String errorMsg = "Required installed version code was " + + requiredInstalledVersionCode + + " but actual installed version is " + + dataOwnerPkg.getLongVersionCode(); + Slog.w(TAG, errorMsg); + return Pair.create( + PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg); + } + } + + if (dataOwnerPkg != null && !dataOwnerPkg.isSdkLibrary()) { + if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, + dataOwnerPkg.isDebuggable())) { + // Downgrade is not permitted; a lower version of the app will not be allowed + try { + PackageManagerServiceUtils.checkDowngrade(dataOwnerPkg, pkgLite); + } catch (PackageManagerException e) { + String errorMsg = "Downgrade detected: " + e.getMessage(); + Slog.w(TAG, errorMsg); + return Pair.create( + PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg); + } + } else if (dataOwnerPs.isSystem()) { + // Downgrade is permitted, but system apps can't be downgraded below + // the version preloaded onto the system image + final PackageSetting disabledPs = mPm.mSettings.getDisabledSystemPkgLPr( + dataOwnerPs); + if (disabledPs != null) { + dataOwnerPkg = disabledPs.getPkg(); + } + if (!Build.IS_DEBUGGABLE && !dataOwnerPkg.isDebuggable()) { + // Only restrict non-debuggable builds and non-debuggable version of the app + try { + PackageManagerServiceUtils.checkDowngrade(dataOwnerPkg, pkgLite); + } catch (PackageManagerException e) { + String errorMsg = + "System app: " + packageName + " cannot be downgraded to" + + " older than its preloaded version on the system" + + " image. " + e.getMessage(); + Slog.w(TAG, errorMsg); + return Pair.create( + PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg); + } + } + } + } + } + return Pair.create(PackageManager.INSTALL_SUCCEEDED, null); + } + + private Pair verifyReplacingVersionCodeForApex(PackageInfoLite pkgLite, + long requiredInstalledVersionCode, int installFlags) { + String packageName = pkgLite.packageName; + + final PackageInfo activePackage = mPm.snapshotComputer().getPackageInfo( + packageName, PackageManager.MATCH_APEX, UserHandle.USER_SYSTEM); + if (activePackage == null) { + String errorMsg = "Attempting to install new APEX package " + packageName; + Slog.w(TAG, errorMsg); + return Pair.create(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED, errorMsg); + } + + final long activeVersion = activePackage.getLongVersionCode(); + if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST + && activeVersion != requiredInstalledVersionCode) { + String errorMsg = "Installed version of APEX package " + packageName + + " does not match required. Active version: " + activeVersion + + " required: " + requiredInstalledVersionCode; + Slog.w(TAG, errorMsg); + return Pair.create(PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg); + } + + final boolean isAppDebuggable = (activePackage.applicationInfo.flags + & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + final long newVersionCode = pkgLite.getLongVersionCode(); + if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, isAppDebuggable) + && newVersionCode < activeVersion) { + String errorMsg = "Downgrade of APEX package " + packageName + + " is not allowed. Active version: " + activeVersion + + " attempted: " + newVersionCode; + Slog.w(TAG, errorMsg); + return Pair.create(PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg); + } + + return Pair.create(PackageManager.INSTALL_SUCCEEDED, null); + } + + int getUidForVerifier(VerifierInfo verifierInfo) { + synchronized (mPm.mLock) { + final AndroidPackage pkg = mPm.mPackages.get(verifierInfo.packageName); + if (pkg == null) { + return -1; + } else if (pkg.getSigningDetails().getSignatures().length != 1) { + Slog.i(TAG, "Verifier package " + verifierInfo.packageName + + " has more than one signature; ignoring"); + return -1; + } + + /* + * If the public key of the package's signature does not match + * our expected public key, then this is a different package and + * we should skip. + */ + + final byte[] expectedPublicKey; + try { + final Signature verifierSig = pkg.getSigningDetails().getSignatures()[0]; + final PublicKey publicKey = verifierSig.getPublicKey(); + expectedPublicKey = publicKey.getEncoded(); + } catch (CertificateException e) { + return -1; + } + + final byte[] actualPublicKey = verifierInfo.publicKey.getEncoded(); + + if (!Arrays.equals(actualPublicKey, expectedPublicKey)) { + Slog.i(TAG, "Verifier package " + verifierInfo.packageName + + " does not have the expected public key; ignoring"); + return -1; + } + + return pkg.getUid(); + } + } + + public void sendPendingBroadcasts() { + String[] packages; + ArrayList[] components; + int numBroadcasts = 0, numUsers; + int[] uids; + + synchronized (mPm.mLock) { + final SparseArray>> userIdToPackagesToComponents = + mPm.mPendingBroadcasts.copiedMap(); + numUsers = userIdToPackagesToComponents.size(); + for (int n = 0; n < numUsers; n++) { + numBroadcasts += userIdToPackagesToComponents.valueAt(n).size(); + } + if (numBroadcasts == 0) { + // Nothing to be done. Just return + return; + } + packages = new String[numBroadcasts]; + components = new ArrayList[numBroadcasts]; + uids = new int[numBroadcasts]; + int i = 0; // filling out the above arrays + + for (int n = 0; n < numUsers; n++) { + final int packageUserId = userIdToPackagesToComponents.keyAt(n); + final ArrayMap> componentsToBroadcast = + userIdToPackagesToComponents.valueAt(n); + final int numComponents = CollectionUtils.size(componentsToBroadcast); + for (int index = 0; index < numComponents; index++) { + packages[i] = componentsToBroadcast.keyAt(index); + components[i] = componentsToBroadcast.valueAt(index); + final PackageSetting ps = mPm.mSettings.getPackageLPr(packages[i]); + uids[i] = (ps != null) + ? UserHandle.getUid(packageUserId, ps.getAppId()) + : -1; + i++; + } + } + numBroadcasts = i; + mPm.mPendingBroadcasts.clear(); + } + final Computer snapshot = mPm.snapshotComputer(); + // Send broadcasts + for (int i = 0; i < numBroadcasts; i++) { + mBroadcastHelper.sendPackageChangedBroadcast(snapshot, packages[i], + true /* dontKillApp */, components[i], uids[i], null /* reason */); + } + } + + void handlePackagePostInstall(InstallRequest request, boolean launchedForRestore) { + final boolean killApp = + (request.getInstallFlags() & PackageManager.INSTALL_DONT_KILL_APP) == 0; + final boolean succeeded = request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED; + final boolean update = request.isUpdate(); + final boolean archived = request.isArchived(); + final String packageName = request.getName(); + final Computer snapshot = mPm.snapshotComputer(); + final PackageStateInternal pkgSetting = + succeeded ? snapshot.getPackageStateInternal(packageName) : null; + final boolean removedBeforeUpdate = (pkgSetting == null) + || (pkgSetting.isSystem() && !pkgSetting.getPath().getPath().equals( + request.getPkg().getPath())); + if (succeeded && removedBeforeUpdate) { + Slog.e(TAG, packageName + " was removed before handlePackagePostInstall " + + "could be executed"); + request.setReturnCode(INSTALL_FAILED_PACKAGE_CHANGED); + request.setReturnMessage("Package was removed before install could complete."); + + // Remove the update failed package's older resources safely now + mRemovePackageHelper.cleanUpResources(packageName, request.getOldCodeFile(), + request.getOldInstructionSet()); + mPm.notifyInstallObserver(request); + return; + } + + if (succeeded) { + // Clear the uid cache after we installed a new package. + mPm.mPerUidReadTimeoutsCache = null; + + mPm.notifyInstantAppPackageInstalled(request.getPkg().getPackageName(), + request.getNewUsers()); + + final int[] firstUserIds = request.getFirstTimeBroadcastUserIds(); + + if (request.getPkg().getStaticSharedLibraryName() == null) { + mPm.mProcessLoggingHandler.invalidateBaseApkHash(request.getPkg().getBaseApkPath()); + } + + mBroadcastHelper.sendPostInstallBroadcasts(mPm.snapshotComputer(), request, packageName, + mPm.mRequiredPermissionControllerPackage, mPm.mRequiredVerifierPackages, + mPm.mRequiredInstallerPackage, + /* packageSender= */ mPm, launchedForRestore, killApp, update, archived); + + if (request.isAllNewUsers() && !update) { + mPm.notifyPackageAdded(packageName, request.getAppId()); + } else { + mPm.notifyPackageChanged(packageName, request.getAppId()); + } + + // Apply restricted settings on potentially dangerous packages. Needs to happen + // after appOpsManager is notified of the new package + if (request.getPackageSource() == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE + || request.getPackageSource() + == PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE) { + final int appId = request.getAppId(); + mPm.mHandler.post(() -> { + for (int userId : firstUserIds) { + enableRestrictedSettings(packageName, appId, userId); + } + }); + } + + // Log current value of "unknown sources" setting + EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED, + getUnknownSourcesSettings()); + + // Remove the replaced package's older resources safely now + CleanUpArgs args = request.getRemovedInfo() != null + ? request.getRemovedInfo().mArgs : null; + if (args != null) { + if (!killApp) { + // If we didn't kill the app, defer the deletion of code/resource files, + // since the old code/resource files may still be in use by the running + // application. This mitigates problems and cases where resources or + // code is loaded by a new Activity before ApplicationInfo changes have + // propagated to all application threads. + mPm.scheduleDeferredNoKillPostDelete(args); + if (Flags.improveInstallDontKill()) { + synchronized (mPm.mInstallLock) { + PackageManagerServiceUtils.linkFilesToOldDirs(mPm.mInstaller, + packageName, pkgSetting.getPath(), pkgSetting.getOldPaths()); + } + } + } else { + mRemovePackageHelper.cleanUpResources(packageName, args.getCodeFile(), + args.getInstructionSets()); + } + } 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(); + } + + if (!archived) { + // Notify DexManager that the package was installed for new users. + // The updated users should already be indexed and the package code paths + // should not change. + // Don't notify the manager for ephemeral apps as they are not expected to + // survive long enough to benefit of background optimizations. + for (int userId : firstUserIds) { + PackageInfo info = snapshot.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); + } + } + } else { + // Now send PACKAGE_REMOVED + EXTRA_REPLACING broadcast. + final PackageRemovedInfo info = new PackageRemovedInfo(); + info.mRemovedPackage = packageName; + info.mInstallerPackageName = request.getInstallerPackageName(); + info.mRemovedUsers = firstUserIds; + info.mBroadcastUsers = firstUserIds; + info.mUid = request.getAppId(); + info.mIsAppIdRemoved = true; + info.mRemovedPackageVersionCode = request.getPkg().getLongVersionCode(); + info.mRemovedForAllUsers = true; + + mBroadcastHelper.sendPackageRemovedBroadcasts(info, mPm, + false /*killApp*/, false /*removedBySystem*/, true /*isArchived*/); + } + } + + final boolean deferInstallObserver = succeeded && update; + if (deferInstallObserver) { + if (killApp) { + mPm.scheduleDeferredPendingKillInstallObserver(request); + } else { + mPm.scheduleDeferredNoKillInstallObserver(request); + } + } else { + mPm.notifyInstallObserver(request); + } + + // Prune unused static shared libraries which have been cached a period of time + mPm.schedulePruneUnusedStaticSharedLibraries(true /* delay */); + + // Log tracing if needed + if (request.getTraceMethod() != null) { + Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, request.getTraceMethod(), + request.getTraceCookie()); + } + } + + /** + * Get the "allow unknown sources" setting. + * + * @return the current "allow unknown sources" setting + */ + private int getUnknownSourcesSettings() { + return android.provider.Settings.Secure.getIntForUser(mContext.getContentResolver(), + android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS, + -1, UserHandle.USER_SYSTEM); + } + + /** + * Uncompress and install stub applications. + *

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. + *

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. + *

In order to forcefully attempt an installation of a full application, go to app + * settings and enable the application. + */ + @GuardedBy({"mPm.mLock", "mPm.mInstallLock"}) + void installSystemStubPackages(@NonNull List systemStubPackageNames, + @PackageManagerService.ScanFlags int scanFlags) { + for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) { + final String packageName = systemStubPackageNames.get(i); + // skip if the system package is already disabled + if (mPm.mSettings.isDisabledSystemPackageLPr(packageName)) { + systemStubPackageNames.remove(i); + continue; + } + // skip if the package isn't installed (?!); this should never happen + final AndroidPackage pkg = mPm.mPackages.get(packageName); + if (pkg == null) { + systemStubPackageNames.remove(i); + continue; + } + // skip if the package has been disabled by the user + final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); + if (ps != null) { + final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM); + if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { + systemStubPackageNames.remove(i); + continue; + } + } + + // install the package to replace the stub on /system + try { + installStubPackageLI(pkg, 0, scanFlags); + ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, + UserHandle.USER_SYSTEM, "android"); + systemStubPackageNames.remove(i); + } catch (PackageManagerException e) { + Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage()); + } + + // any failed attempt to install the package will be cleaned up later + } + + // disable any stub still left; these failed to install the full application + for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) { + final String pkgName = systemStubPackageNames.get(i); + final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName); + ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + UserHandle.USER_SYSTEM, "android"); + logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName); + } + } + + /** + * Extract, install and enable a stub package. + *

If the compressed file can not be extracted / installed for any reason, the stub + * APK will be installed and the package will be disabled. To recover from this situation, + * the user will need to go into system settings and re-enable the package. + */ + boolean enableCompressedPackage(AndroidPackage stubPkg, + @NonNull PackageSetting stubPkgSetting) { + final int parseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY + | ParsingPackageUtils.PARSE_ENFORCE_CODE; + synchronized (mPm.mInstallLock) { + final AndroidPackage pkg; + try (PackageFreezer freezer = + mPm.freezePackage(stubPkg.getPackageName(), UserHandle.USER_ALL, + "setEnabledSetting", + ApplicationExitInfo.REASON_PACKAGE_UPDATED, null /* request */)) { + pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/); + mAppDataHelper.prepareAppDataAfterInstallLIF(pkg); + synchronized (mPm.mLock) { + try { + mSharedLibraries.updateSharedLibraries( + pkg, stubPkgSetting, null, null, + Collections.unmodifiableMap(mPm.mPackages)); + } catch (PackageManagerException e) { + Slog.w(TAG, "updateAllSharedLibrariesLPw failed: ", e); + } + mPm.mPermissionManager.onPackageInstalled(pkg, + Process.INVALID_UID /* previousAppId */, + PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT, + UserHandle.USER_ALL); + mPm.writeSettingsLPrTEMP(); + // Since compressed package can be system app only, we do not need to + // set restricted settings on it. + } + } catch (PackageManagerException e) { + // Whoops! Something went very wrong; roll back to the stub and disable the package + try (PackageFreezer freezer = + mPm.freezePackage(stubPkg.getPackageName(), UserHandle.USER_ALL, + "setEnabledSetting", + ApplicationExitInfo.REASON_PACKAGE_UPDATED, + null /* request */)) { + synchronized (mPm.mLock) { + // NOTE: Ensure the system package is enabled; even for a compressed stub. + // If we don't, installing the system package fails during scan + mPm.mSettings.enableSystemPackageLPw(stubPkg.getPackageName()); + } + installPackageFromSystemLIF(stubPkg.getPath(), + mPm.mUserManager.getUserIds() /*allUserHandles*/, + null /*origUserHandles*/, + true /*writeSettings*/); + } catch (PackageManagerException pme) { + // Serious WTF; we have to be able to install the stub + Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(), + pme); + } finally { + // Disable the package; the stub by itself is not runnable + synchronized (mPm.mLock) { + final PackageSetting stubPs = mPm.mSettings.getPackageLPr( + stubPkg.getPackageName()); + if (stubPs != null) { + stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, + UserHandle.USER_SYSTEM, "android"); + } + mPm.writeSettingsLPrTEMP(); + } + } + return false; + } + mAppDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL, + FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL + | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); + mDexManager.notifyPackageUpdated(pkg.getPackageName(), + pkg.getBaseApkPath(), pkg.getSplitCodePaths()); + } + return true; + } + + @GuardedBy("mPm.mInstallLock") + private AndroidPackage installStubPackageLI(AndroidPackage stubPkg, + @ParsingPackageUtils.ParseFlags int parseFlags, + @PackageManagerService.ScanFlags int scanFlags) + throws PackageManagerException { + if (DEBUG_COMPRESSION) { + Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.getPackageName()); + } + // uncompress the binary to its eventual destination on /data + final File scanFile = decompressPackage(stubPkg.getPackageName(), stubPkg.getPath()); + if (scanFile == null) { + throw PackageManagerException.ofInternalError( + "Unable to decompress stub at " + stubPkg.getPath(), + PackageManagerException.INTERNAL_ERROR_DECOMPRESS_STUB); + } + synchronized (mPm.mLock) { + mPm.mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/); + } + mRemovePackageHelper.removePackage(stubPkg, true /*chatty*/); + try { + return initPackageTracedLI(scanFile, parseFlags, scanFlags); + } catch (PackageManagerException e) { + Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(), + e); + // Remove the failed install + mRemovePackageHelper.removeCodePath(scanFile); + throw e; + } + } + + /** + * Decompresses the given package on the system image onto + * the /data partition. + * @return The directory the package was decompressed into. Otherwise, {@code null}. + */ + @GuardedBy("mPm.mInstallLock") + private File decompressPackage(String packageName, String codePath) { + if (!compressedFileExists(codePath)) { + if (DEBUG_COMPRESSION) { + Slog.i(TAG, "No files to decompress at: " + codePath); + } + return null; + } + final File dstCodePath = + PackageManagerServiceUtils.getNextCodePath(Environment.getDataAppDirectory(null), + packageName); + int ret = PackageManagerServiceUtils.decompressFiles(codePath, dstCodePath, packageName); + if (ret == PackageManager.INSTALL_SUCCEEDED) { + ret = PackageManagerServiceUtils.extractNativeBinaries(dstCodePath, packageName); + } + if (ret == PackageManager.INSTALL_SUCCEEDED) { + // NOTE: During boot, we have to delay releasing cblocks for no other reason than + // we cannot retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}. + // When we no longer need to read that setting, cblock release can occur always + // occur here directly + if (!mPm.isSystemReady()) { + if (mPm.mReleaseOnSystemReady == null) { + mPm.mReleaseOnSystemReady = new ArrayList<>(); + } + mPm.mReleaseOnSystemReady.add(dstCodePath); + } else { + final ContentResolver resolver = mContext.getContentResolver(); + F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath); + } + } else { + if (!dstCodePath.exists()) { + return null; + } + mRemovePackageHelper.removeCodePath(dstCodePath); + return null; + } + + return dstCodePath; + } + + /** + * Tries to restore the disabled system package after an update has been deleted. + */ + public void restoreDisabledSystemPackageLIF(DeletePackageAction action, + @NonNull int[] allUserHandles, boolean writeSettings) throws SystemDeleteException { + final PackageSetting deletedPs = action.mDeletingPs; + final PackageRemovedInfo outInfo = action.mRemovedInfo; + final PackageSetting disabledPs = action.mDisabledPs; + + synchronized (mPm.mLock) { + // NOTE: The system package always needs to be enabled; even if it's for + // a compressed stub. If we don't, installing the system package fails + // during scan [scanning checks the disabled packages]. We will reverse + // this later, after we've "installed" the stub. + // Reinstate the old system package + mPm.mSettings.enableSystemPackageLPw(disabledPs.getPkg().getPackageName()); + // Remove any native libraries from the upgraded package. + PackageManagerServiceUtils.removeNativeBinariesLI(deletedPs); + } + // Install the system package + if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs); + try { + synchronized (mPm.mInstallLock) { + final int[] origUsers = outInfo == null ? null : outInfo.mOrigUsers; + installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles, + origUsers, writeSettings); + } + } catch (PackageManagerException e) { + Slog.w(TAG, "Failed to restore system package:" + deletedPs.getPackageName() + ": " + + e.getMessage()); + // TODO(b/194319951): can we avoid this; throw would come from scan... + throw new SystemDeleteException(e); + } finally { + if (disabledPs.getPkg().isStub()) { + // We've re-installed the stub; make sure it's disabled here. If package was + // originally enabled, we'll install the compressed version of the application + // and re-enable it afterward. + synchronized (mPm.mLock) { + disableStubPackage(action, deletedPs, allUserHandles); + } + } + } + } + + @GuardedBy("mPm.mLock") + private void disableStubPackage(DeletePackageAction action, PackageSetting deletedPs, + @NonNull int[] allUserHandles) { + final PackageSetting stubPs = mPm.mSettings.getPackageLPr( + deletedPs.getPackageName()); + if (stubPs != null) { + int userId = action.mUser == null + ? UserHandle.USER_ALL : action.mUser.getIdentifier(); + if (userId == UserHandle.USER_ALL) { + for (int aUserId : allUserHandles) { + stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android"); + } + } else if (userId >= UserHandle.USER_SYSTEM) { + stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android"); + } + } + } + + /** + * Installs a package that's already on the system partition. + */ + @GuardedBy("mPm.mInstallLock") + private void installPackageFromSystemLIF(@NonNull String codePathString, + @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, + boolean writeSettings) + throws PackageManagerException { + final File codePath = new File(codePathString); + @ParsingPackageUtils.ParseFlags int parseFlags = + mPm.getDefParseFlags() + | ParsingPackageUtils.PARSE_MUST_BE_APK + | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR; + @PackageManagerService.ScanFlags int scanFlags = mPm.getSystemPackageScanFlags(codePath); + final AndroidPackage pkg = initPackageTracedLI(codePath, parseFlags, scanFlags); + + synchronized (mPm.mLock) { + PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName()); + try { + // update shared libraries for the newly re-installed system package + mSharedLibraries.updateSharedLibraries(pkg, pkgSetting, null, null, + Collections.unmodifiableMap(mPm.mPackages)); + } catch (PackageManagerException e) { + Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage()); + } + } + setPackageInstalledForSystemPackage(pkg, allUserHandles, origUserHandles, writeSettings); + + mAppDataHelper.prepareAppDataAfterInstallLIF(pkg); + } + + private void setPackageInstalledForSystemPackage(@NonNull AndroidPackage pkg, + @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, + boolean writeSettings) { + // writer + synchronized (mPm.mLock) { + PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName()); + + final boolean applyUserRestrictions = origUserHandles != null; + if (applyUserRestrictions) { + boolean installedStateChanged = false; + if (DEBUG_REMOVE) { + Slog.d(TAG, "Propagating install state across reinstall"); + } + for (int userId : allUserHandles) { + final boolean installed = ArrayUtils.contains(origUserHandles, userId); + if (DEBUG_REMOVE) { + Slog.d(TAG, " user " + userId + " => " + installed); + } + if (installed != ps.getInstalled(userId)) { + installedStateChanged = true; + } + ps.setInstalled(installed, userId); + if (installed) { + ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId); + } + } + // Regardless of writeSettings we need to ensure that this restriction + // state propagation is persisted + mPm.mSettings.writeAllUsersPackageRestrictionsLPr(); + if (installedStateChanged) { + mPm.mSettings.writeKernelMappingLPr(ps); + } + } + + // The method below will take care of removing obsolete permissions and granting + // install permissions. + mPm.mPermissionManager.onPackageInstalled(pkg, Process.INVALID_UID, + PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT, + UserHandle.USER_ALL); + for (final int userId : allUserHandles) { + if (applyUserRestrictions) { + mPm.mSettings.writePermissionStateForUserLPr(userId, false); + } + } + + // can downgrade to reader here + if (writeSettings) { + mPm.writeSettingsLPrTEMP(); + } + } + } + + @GuardedBy("mPm.mLock") + public void prepareSystemPackageCleanUp( + WatchedArrayMap packageSettings, + List possiblyDeletedUpdatedSystemApps, + ArrayMap expectingBetter, int[] userIds) { + // Iterates PackageSettings in reversed order because the item could be removed + // during the iteration. + for (int index = packageSettings.size() - 1; index >= 0; index--) { + final PackageSetting ps = packageSettings.valueAt(index); + final String packageName = ps.getPackageName(); + /* + * If this is not a system app, it can't be a + * disable system app. + */ + if (!ps.isSystem()) { + continue; + } + + /* + * If the package is scanned, it's not erased. + */ + final AndroidPackage scannedPkg = mPm.mPackages.get(packageName); + final PackageSetting disabledPs = + mPm.mSettings.getDisabledSystemPkgLPr(packageName); + if (scannedPkg != null) { + if (scannedPkg.isApex()) { + // APEX on /data has been scanned. No need to expect better. + continue; + } + /* + * If the system app is both scanned and in the + * disabled packages list, then it must have been + * added via OTA. Remove it from the currently + * scanned package so the previously user-installed + * application can be scanned. + */ + if (disabledPs != null) { + logCriticalInfo(Log.WARN, + "Expecting better updated system app for " + + packageName + + "; removing system app. Last known" + + " codePath=" + ps.getPathString() + + ", versionCode=" + ps.getVersionCode() + + "; scanned versionCode=" + + scannedPkg.getLongVersionCode()); + mRemovePackageHelper.removePackage(scannedPkg, true); + expectingBetter.put(ps.getPackageName(), ps.getPath()); + } + + continue; + } + + if (disabledPs == null) { + logCriticalInfo(Log.WARN, "System package " + packageName + + " no longer exists; its data will be wiped"); + mRemovePackageHelper.removePackageData(ps, userIds); + } else { + // we still have a disabled system package, but, it still might have + // been removed. check the code path still exists and check there's + // still a package. the latter can happen if an OTA keeps the same + // code path, but, changes the package name. + if (disabledPs.getPath() == null || !disabledPs.getPath().exists() + || disabledPs.getPkg() == null) { + possiblyDeletedUpdatedSystemApps.add(packageName); + } else { + // We're expecting that the system app should remain disabled, but add + // it to expecting better to recover in case the data version cannot + // be scanned. + expectingBetter.put(disabledPs.getPackageName(), disabledPs.getPath()); + } + } + } + } + + @GuardedBy("mPm.mLock") + // 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. + public void cleanupDisabledPackageSettings(List possiblyDeletedUpdatedSystemApps, + int[] userIds, int scanFlags) { + for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) { + final String packageName = possiblyDeletedUpdatedSystemApps.get(i); + final AndroidPackage pkg = mPm.mPackages.get(packageName); + final String msg; + + // remove from the disabled system list; do this first so any future + // scans of this package are performed without this state + mPm.mSettings.removeDisabledSystemPackageLPw(packageName); + + if (pkg == null) { + // should have found an update, but, we didn't; remove everything + msg = "Updated system package " + packageName + + " no longer exists; removing its data"; + // Actual deletion of code and data will be handled by later + // reconciliation step + } else { + // found an update; revoke system privileges + msg = "Updated system package " + packageName + + " no longer exists; rescanning package on data"; + + // NOTE: We don't do anything special if a stub is removed from the + // system image. But, if we were [like removing the uncompressed + // version from the /data partition], this is where it'd be done. + + // remove the package from the system and re-scan it without any + // special privileges + mRemovePackageHelper.removePackage(pkg, true); + PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); + if (ps != null) { + ps.getPkgState().setUpdatedSystemApp(false); + } + + try { + final File codePath = new File(pkg.getPath()); + synchronized (mPm.mInstallLock) { + initPackageTracedLI(codePath, 0, scanFlags); + } + } catch (PackageManagerException e) { + Slog.e(TAG, "Failed to parse updated, ex-system package: " + + e.getMessage()); + } + } + + // one final check. if we still have a package setting [ie. it was + // previously scanned and known to the system], but, we don't have + // a package [ie. there was an error scanning it from the /data + // partition], completely remove the package data. + final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); + if (ps != null && mPm.mPackages.get(packageName) == null) { + mRemovePackageHelper.removePackageData(ps, userIds); + } + logCriticalInfo(Log.WARN, msg); + } + } + + /** + * Scans APEX packages and registers them with the system. + * + * apexd has its own policy to decide which APEX to activate and which not. The policy might + * conflicts that of PMS. The APEX package info stored in PMS is a mirror of that managed by + * apexd. To keep things simple and keep activation status in sync for both apexd and PMS, we + * don't persist APEX in settings and always scan APEX from scratch during boot. However, some + * data like lastUpdateTime will be lost if PackageSetting is not persisted for APEX. + * + * TODO(b/225756739): Read lastUpdateTime from ApexInfoList to populate PackageSetting correctly + */ + @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) + public List scanApexPackages(ApexInfo[] allPackages, int parseFlags, + int scanFlags, PackageParser2 packageParser, ExecutorService executorService) { + if (allPackages == null) { + return Collections.EMPTY_LIST; + } + + ParallelPackageParser parallelPackageParser = + new ParallelPackageParser(packageParser, executorService); + + // Submit files for parsing in parallel + ArrayMap parsingApexInfo = new ArrayMap<>(); + for (ApexInfo ai : allPackages) { + File apexFile = new File(ai.modulePath); + parallelPackageParser.submit(apexFile, parseFlags); + parsingApexInfo.put(apexFile, ai); + } + + List parseResults = + new ArrayList<>(parsingApexInfo.size()); + for (int i = 0; i < parsingApexInfo.size(); i++) { + ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take(); + parseResults.add(parseResult); + } + // Sort the list to ensure we always process factory packages first + Collections.sort(parseResults, (a, b) -> { + ApexInfo i1 = parsingApexInfo.get(a.scanFile); + ApexInfo i2 = parsingApexInfo.get(b.scanFile); + return Boolean.compare(i2.isFactory, i1.isFactory); + }); + + + // Process results one by one + List results = new ArrayList<>(parsingApexInfo.size()); + for (int i = 0; i < parseResults.size(); i++) { + ParallelPackageParser.ParseResult parseResult = parseResults.get(i); + Throwable throwable = parseResult.throwable; + ApexInfo ai = parsingApexInfo.get(parseResult.scanFile); + int newParseFlags = parseFlags; + int newScanFlags = scanFlags | SCAN_AS_APEX + | mPm.getSystemPackageScanFlags(parseResult.scanFile); + if (!ai.isFactory) { + newParseFlags &= ~ParsingPackageUtils.PARSE_IS_SYSTEM_DIR; + newScanFlags |= SCAN_NEW_INSTALL; + } + + if (throwable == null) { + try { + addForInitLI(parseResult.parsedPackage, newParseFlags, newScanFlags, null, + new ApexManager.ActiveApexInfo(ai)); + AndroidPackage pkg = parseResult.parsedPackage.hideAsFinal(); + if (ai.isFactory && !ai.isActive) { + disableSystemPackageLPw(pkg); + } + results.add(new ApexManager.ScanResult(ai, pkg, pkg.getPackageName())); + } catch (PackageManagerException e) { + throw new IllegalStateException("Failed to scan: " + ai.modulePath, e); + } + } else if (throwable instanceof PackageManagerException) { + throw new IllegalStateException("Unable to parse: " + ai.modulePath, throwable); + } else { + throw new IllegalStateException("Unexpected exception occurred while parsing " + + ai.modulePath, throwable); + } + } + + return results; + } + + @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) + public void installPackagesFromDir(File scanDir, int parseFlags, + int scanFlags, PackageParser2 packageParser, ExecutorService executorService, + @Nullable ApexManager.ActiveApexInfo apexInfo) { + final File[] files = scanDir.listFiles(); + if (ArrayUtils.isEmpty(files)) { + Log.d(TAG, "No files in app dir " + scanDir); + return; + } + + if (DEBUG_PACKAGE_SCANNING) { + Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags + + " flags=0x" + Integer.toHexString(parseFlags)); + } + ParallelPackageParser parallelPackageParser = + new ParallelPackageParser(packageParser, executorService); + + // Submit files for parsing in parallel + int fileCount = 0; + for (File file : files) { + final boolean isPackage = (isApkFile(file) || file.isDirectory()) + && !PackageInstallerService.isStageName(file.getName()); + if (!isPackage) { + // Ignore entries which are not packages + continue; + } + if ((scanFlags & SCAN_DROP_CACHE) != 0) { + final PackageCacher cacher = new PackageCacher(mPm.getCacheDir(), + mPm.mPackageParserCallback); + Log.w(TAG, "Dropping cache of " + file.getAbsolutePath()); + cacher.cleanCachedResult(file); + } + 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; + String errorMsg = null; + + if (throwable == null) { + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "addForInitLI"); + addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags, + new UserHandle(UserHandle.USER_SYSTEM), apexInfo); + } catch (PackageManagerException e) { + errorCode = e.error; + errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage(); + Slog.w(TAG, errorMsg); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } else if (throwable instanceof PackageManagerException) { + PackageManagerException e = (PackageManagerException) throwable; + errorCode = e.error; + errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage(); + Slog.w(TAG, errorMsg); + } else { + throw new IllegalStateException("Unexpected exception occurred while parsing " + + parseResult.scanFile, throwable); + } + + if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) { + mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg); + } + + // Delete invalid userdata apps + if ((scanFlags & SCAN_AS_SYSTEM) == 0 + && errorCode != PackageManager.INSTALL_SUCCEEDED) { + logCriticalInfo(Log.WARN, + "Deleting invalid package at " + parseResult.scanFile); + mRemovePackageHelper.removeCodePath(parseResult.scanFile); + } + } + } + + /** + * 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. + */ + @GuardedBy("mPm.mLock") + public void checkExistingBetterPackages(ArrayMap expectingBetterPackages, + List stubSystemApps, int systemScanFlags, int systemParseFlags) { + for (int i = 0; i < expectingBetterPackages.size(); i++) { + final String packageName = expectingBetterPackages.keyAt(i); + if (mPm.mPackages.containsKey(packageName)) { + continue; + } + final File scanFile = expectingBetterPackages.valueAt(i); + + logCriticalInfo(Log.WARN, "Expected better " + packageName + + " but never showed up; reverting to system"); + + final Pair rescanAndReparseFlags = + mPm.getSystemPackageRescanFlagsAndReparseFlags(scanFile, + systemScanFlags, systemParseFlags); + @PackageManagerService.ScanFlags int rescanFlags = rescanAndReparseFlags.first; + @ParsingPackageUtils.ParseFlags int reparseFlags = rescanAndReparseFlags.second; + + if (rescanFlags == 0) { + Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile); + continue; + } + mPm.mSettings.enableSystemPackageLPw(packageName); + + try { + synchronized (mPm.mInstallLock) { + final AndroidPackage newPkg = initPackageTracedLI( + scanFile, reparseFlags, rescanFlags); + // We rescanned a stub, add it to the list of stubbed system packages + if (newPkg.isStub()) { + stubSystemApps.add(packageName); + } + } + } catch (PackageManagerException e) { + Slog.e(TAG, "Failed to parse original system package: " + + e.getMessage()); + } + } + } + + /** + * Traces a package scan and registers it with the system. + * @see #initPackageLI(File, int, int) + */ + @GuardedBy("mPm.mInstallLock") + public AndroidPackage initPackageTracedLI(File scanFile, final int parseFlags, int scanFlags) + throws PackageManagerException { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]"); + try { + return initPackageLI(scanFile, parseFlags, scanFlags); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + /** + * Scans a package, registers it with the system and returns the newly parsed package. + * Returns {@code null} in case of errors and the error code is stored in mLastScanError + */ + @GuardedBy("mPm.mInstallLock") + private AndroidPackage initPackageLI(File scanFile, int parseFlags, int scanFlags) + throws PackageManagerException { + if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); + + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); + final ParsedPackage parsedPackage; + try (PackageParser2 pp = mPm.mInjector.getScanningPackageParser()) { + parsedPackage = pp.parsePackage(scanFile, parseFlags, false); + } catch (PackageParserException e) { + throw new PackageManagerException(e.error, e.getMessage(), e); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + return addForInitLI(parsedPackage, parseFlags, scanFlags, + new UserHandle(UserHandle.USER_SYSTEM), null); + } + + /** + * Adds a new package to the internal data structures during platform initialization. + *

After adding, the package is known to the system and available for querying. + *

For packages located on the device ROM [eg. packages located in /system, /vendor, + * etc...], additional checks are performed. Basic verification [such as ensuring + * matching signatures, checking version codes, etc...] occurs if the package is + * identical to a previously known package. If the package fails a signature check, + * the version installed on /data will be removed. If the version of the new package + * is less than or equal than the version on /data, it will be ignored. + *

Regardless of the package location, the results are applied to the internal + * structures and the package is made available to the rest of the system. + *

NOTE: The return value should be removed. It's the passed in package object. + */ + @GuardedBy("mPm.mInstallLock") + private AndroidPackage addForInitLI(ParsedPackage parsedPackage, + @ParsingPackageUtils.ParseFlags int parseFlags, + @PackageManagerService.ScanFlags int scanFlags, + @Nullable UserHandle user, @Nullable ApexManager.ActiveApexInfo activeApexInfo) + throws PackageManagerException { + PackageSetting disabledPkgSetting; + synchronized (mPm.mLock) { + // Static shared libraries have synthetic package names + if (activeApexInfo == null && parsedPackage.isStaticSharedLibrary()) { + PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage); + } + disabledPkgSetting = + mPm.mSettings.getDisabledSystemPkgLPr(parsedPackage.getPackageName()); + if (activeApexInfo != null && disabledPkgSetting != null) { + // When a disabled system package is scanned, its final PackageSetting is actually + // skipped and not added to any data structures, instead relying on the disabled + // setting read from the persisted Settings XML file. This persistence does not + // include the APEX module name, so here, re-set it from the active APEX info. + // + // This also has the (beneficial) side effect where if a package disappears from an + // APEX, leaving only a /data copy, it will lose its apexModuleName. + // + // This must be done before scanSystemPackageLI as that will throw in the case of a + // system -> data package. + disabledPkgSetting.setApexModuleName(activeApexInfo.apexModuleName); + } + } + + final Pair scanResultPair = scanSystemPackageLI( + parsedPackage, parseFlags, scanFlags, user); + final ScanResult scanResult = scanResultPair.first; + boolean shouldHideSystemApp = scanResultPair.second; + final InstallRequest installRequest = new InstallRequest( + parsedPackage, parseFlags, scanFlags, user, scanResult, disabledPkgSetting); + + String existingApexModuleName = null; + synchronized (mPm.mLock) { + var existingPkgSetting = mPm.mSettings.getPackageLPr(parsedPackage.getPackageName()); + if (existingPkgSetting != null) { + existingApexModuleName = existingPkgSetting.getApexModuleName(); + } + } + + if (activeApexInfo != null) { + installRequest.setApexModuleName(activeApexInfo.apexModuleName); + } else { + if (disabledPkgSetting != null) { + installRequest.setApexModuleName(disabledPkgSetting.getApexModuleName()); + } else if (existingApexModuleName != null) { + installRequest.setApexModuleName(existingApexModuleName); + } + } + + synchronized (mPm.mLock) { + boolean appIdCreated = false; + try { + final String pkgName = scanResult.mPkgSetting.getPackageName(); + final List reconcileResult = + ReconcilePackageUtils.reconcilePackages( + Collections.singletonList(installRequest), + mPm.mPackages, Collections.singletonMap(pkgName, + mPm.getSettingsVersionForPackage(parsedPackage)), + mSharedLibraries, mPm.mSettings.getKeySetManagerService(), + mPm.mSettings, mPm.mInjector.getSystemConfig()); + if ((scanFlags & SCAN_AS_APEX) == 0) { + appIdCreated = optimisticallyRegisterAppId(installRequest); + } else { + installRequest.setScannedPackageSettingAppId(Process.INVALID_UID); + } + commitReconciledScanResultLocked(reconcileResult.get(0), + mPm.mUserManager.getUserIds()); + } catch (PackageManagerException e) { + if (appIdCreated) { + cleanUpAppIdCreation(installRequest); + } + throw e; + } + } + + if (shouldHideSystemApp) { + synchronized (mPm.mLock) { + mPm.mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true); + } + } + + if (mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) { + if (scanResult.mPkgSetting != null && scanResult.mPkgSetting.isLoading()) { + // Continue monitoring loading progress of active incremental packages + mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(), + new IncrementalProgressListener(parsedPackage.getPackageName(), mPm)); + } + } + return scanResult.mPkgSetting.getPkg(); + } + + /** + * Prepares the system to commit a {@link ScanResult} in a way that will not fail by registering + * the app ID required for reconcile. + * @return {@code true} if a new app ID was registered and will need to be cleaned up on + * failure. + */ + private boolean optimisticallyRegisterAppId(@NonNull InstallRequest installRequest) + throws PackageManagerException { + if (!installRequest.isExistingSettingCopied() || installRequest.needsNewAppId()) { + synchronized (mPm.mLock) { + // THROWS: when we can't allocate a user id. add call to check if there's + // enough space to ensure we won't throw; otherwise, don't modify state + return mPm.mSettings.registerAppIdLPw(installRequest.getScannedPackageSetting(), + installRequest.needsNewAppId()); + } + } + return false; + } + + /** + * Reverts any app ID creation that were made by + * {@link #optimisticallyRegisterAppId(InstallRequest)}. Note: this is only necessary if the + * referenced method returned true. + */ + private void cleanUpAppIdCreation(@NonNull InstallRequest installRequest) { + // iff we've acquired an app ID for a new package setting, remove it so that it can be + // acquired by another request. + if (installRequest.getScannedPackageSetting() != null + && installRequest.getScannedPackageSetting().getAppId() > 0) { + synchronized (mPm.mLock) { + mPm.mSettings.removeAppIdLPw(installRequest.getScannedPackageSetting().getAppId()); + } + } + } + + @GuardedBy("mPm.mInstallLock") + private ScanResult scanPackageTracedLI(ParsedPackage parsedPackage, + final @ParsingPackageUtils.ParseFlags int parseFlags, + @PackageManagerService.ScanFlags int scanFlags, long currentTime, + @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage"); + try { + return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime, user, + cpuAbiOverride); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + private ScanRequest prepareInitialScanRequest(@NonNull ParsedPackage parsedPackage, + @ParsingPackageUtils.ParseFlags int parseFlags, + @PackageManagerService.ScanFlags int scanFlags, + @Nullable UserHandle user, String cpuAbiOverride) + throws PackageManagerException { + final AndroidPackage platformPackage; + final String realPkgName; + final PackageSetting disabledPkgSetting; + final PackageSetting installedPkgSetting; + final PackageSetting originalPkgSetting; + final SharedUserSetting sharedUserSetting; + SharedUserSetting oldSharedUserSetting = null; + + synchronized (mPm.mLock) { + platformPackage = mPm.getPlatformPackage(); + var isSystemApp = AndroidPackageLegacyUtils.isSystem(parsedPackage); + final String renamedPkgName = mPm.mSettings.getRenamedPackageLPr( + AndroidPackageUtils.getRealPackageOrNull(parsedPackage, isSystemApp)); + realPkgName = ScanPackageUtils.getRealPackageName(parsedPackage, renamedPkgName, + isSystemApp); + if (realPkgName != null) { + ScanPackageUtils.ensurePackageRenamed(parsedPackage, renamedPkgName); + } + originalPkgSetting = getOriginalPackageLocked(parsedPackage, renamedPkgName); + installedPkgSetting = mPm.mSettings.getPackageLPr(parsedPackage.getPackageName()); + if (mPm.mTransferredPackages.contains(parsedPackage.getPackageName())) { + Slog.w(TAG, "Package " + parsedPackage.getPackageName() + + " was transferred to another, but its .apk remains"); + } + disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr( + parsedPackage.getPackageName()); + + boolean ignoreSharedUserId = false; + if (installedPkgSetting == null || !installedPkgSetting.hasSharedUser()) { + // Directly ignore sharedUserSetting for new installs, or if the app has + // already left shared UID + ignoreSharedUserId = parsedPackage.isLeavingSharedUser(); + } + + if (!ignoreSharedUserId && parsedPackage.getSharedUserId() != null) { + sharedUserSetting = mPm.mSettings.getSharedUserLPw( + parsedPackage.getSharedUserId(), + 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/); + } else { + sharedUserSetting = null; + } + if (DEBUG_PACKAGE_SCANNING + && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0 + && sharedUserSetting != null) { + Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId() + + " (uid=" + sharedUserSetting.mAppId + "):" + + " packages=" + sharedUserSetting.getPackageStates()); + } + if (installedPkgSetting != null) { + oldSharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(installedPkgSetting); + } + } + + final boolean isPlatformPackage = platformPackage != null + && platformPackage.getPackageName().equals(parsedPackage.getPackageName()); + + return new ScanRequest(parsedPackage, oldSharedUserSetting, + installedPkgSetting == null ? null : installedPkgSetting.getPkg() /* oldPkg */, + installedPkgSetting /* packageSetting */, + sharedUserSetting, + disabledPkgSetting /* disabledPackageSetting */, + originalPkgSetting /* originalPkgSetting */, + realPkgName, parseFlags, scanFlags, isPlatformPackage, user, cpuAbiOverride); + } + + @GuardedBy("mPm.mInstallLock") + private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage, + final @ParsingPackageUtils.ParseFlags int parseFlags, + @PackageManagerService.ScanFlags int scanFlags, long currentTime, + @Nullable UserHandle user, String cpuAbiOverride) + throws PackageManagerException { + final ScanRequest initialScanRequest = prepareInitialScanRequest(parsedPackage, parseFlags, + scanFlags, user, cpuAbiOverride); + final PackageSetting installedPkgSetting = initialScanRequest.mPkgSetting; + final PackageSetting disabledPkgSetting = initialScanRequest.mDisabledPkgSetting; + + boolean isUpdatedSystemApp; + if (installedPkgSetting != null) { + isUpdatedSystemApp = installedPkgSetting.isUpdatedSystemApp(); + } else { + isUpdatedSystemApp = disabledPkgSetting != null; + } + + final int newScanFlags = adjustScanFlags(scanFlags, installedPkgSetting, disabledPkgSetting, + user, parsedPackage); + ScanPackageUtils.applyPolicy(parsedPackage, newScanFlags, + mPm.getPlatformPackage(), isUpdatedSystemApp); + + synchronized (mPm.mLock) { + assertPackageIsValid(parsedPackage, parseFlags, newScanFlags); + final ScanRequest request = new ScanRequest(parsedPackage, + initialScanRequest.mOldSharedUserSetting, + initialScanRequest.mOldPkg, installedPkgSetting, + initialScanRequest.mSharedUserSetting, disabledPkgSetting, + initialScanRequest.mOriginalPkgSetting, initialScanRequest.mRealPkgName, + parseFlags, scanFlags, initialScanRequest.mIsPlatformPackage, user, + cpuAbiOverride); + return ScanPackageUtils.scanPackageOnlyLI(request, mPm.mInjector, mPm.mFactoryTest, + currentTime); + } + } + + /* + * Returns if scanFlags is SCAN_FIRST_BOOT_OR_UPGRADE or SCAN_FAST_COMMAND, + * can be skipped apk verity + */ + private boolean isFirstBootOrUpdateOrScanFast(int scanFlags) { + return ((scanFlags & PackageManagerService.SCAN_FAST_COMMAND) != 0) || + ((scanFlags & PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE) != 0); + } + + private Pair scanSystemPackageLI(ParsedPackage parsedPackage, + @ParsingPackageUtils.ParseFlags int parseFlags, + @PackageManagerService.ScanFlags int scanFlags, + @Nullable UserHandle user) throws PackageManagerException { + final boolean scanSystemPartition = + ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) || isFirstBootOrUpdateOrScanFast(scanFlags); + final ScanRequest initialScanRequest = prepareInitialScanRequest(parsedPackage, parseFlags, + scanFlags, user, null); + final PackageSetting installedPkgSetting = initialScanRequest.mPkgSetting; + final PackageSetting originalPkgSetting = initialScanRequest.mOriginalPkgSetting; + final PackageSetting pkgSetting = + originalPkgSetting == null ? installedPkgSetting : originalPkgSetting; + final boolean pkgAlreadyExists = pkgSetting != null; + final String disabledPkgName = pkgAlreadyExists + ? pkgSetting.getPackageName() : parsedPackage.getPackageName(); + final boolean isSystemPkgUpdated; + final PackageSetting disabledPkgSetting; + final boolean isUpgrade; + synchronized (mPm.mLock) { + isUpgrade = mPm.isDeviceUpgrading(); + if (scanSystemPartition && !pkgAlreadyExists + && mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) { + // The updated-package data for /system apk remains inconsistently + // after the package data for /data apk is lost accidentally. + // To recover it, enable /system apk and install it as non-updated system app. + Slog.w(TAG, "Inconsistent package setting of updated system app for " + + disabledPkgName + ". To recover it, enable the system app " + + "and install it as non-updated system app."); + mPm.mSettings.removeDisabledSystemPackageLPw(disabledPkgName); + } + disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName); + isSystemPkgUpdated = disabledPkgSetting != null; + + if (DEBUG_INSTALL && isSystemPkgUpdated) { + Slog.d(TAG, "updatedPkg = " + disabledPkgSetting); + } + + if (scanSystemPartition && isSystemPkgUpdated) { + // we're updating the disabled package, so, scan it as the package setting + final ScanRequest request = new ScanRequest(parsedPackage, + mPm.mSettings.getSharedUserSettingLPr(disabledPkgSetting), + null, disabledPkgSetting /* pkgSetting */, + initialScanRequest.mSharedUserSetting, + null /* disabledPkgSetting */, null /* originalPkgSetting */, + null, parseFlags, scanFlags, + initialScanRequest.mIsPlatformPackage, user, null); + ScanPackageUtils.applyPolicy(parsedPackage, scanFlags, + mPm.getPlatformPackage(), true); + final ScanResult scanResult = + ScanPackageUtils.scanPackageOnlyLI(request, mPm.mInjector, + mPm.mFactoryTest, -1L); + if (scanResult.mExistingSettingCopied + && scanResult.mRequest.mPkgSetting != null) { + scanResult.mRequest.mPkgSetting.updateFrom(scanResult.mPkgSetting); + } + } + } // End of mLock + + final boolean newPkgChangedPaths = pkgAlreadyExists + && !pkgSetting.getPathString().equals(parsedPackage.getPath()); + final boolean newPkgVersionGreater = pkgAlreadyExists + && parsedPackage.getLongVersionCode() > pkgSetting.getVersionCode(); + final boolean newSharedUserSetting = pkgAlreadyExists + && (initialScanRequest.mOldSharedUserSetting + != initialScanRequest.mSharedUserSetting); + final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated + && newPkgChangedPaths && (newPkgVersionGreater || newSharedUserSetting); + if (isSystemPkgBetter) { + // The version of the application on /system is greater than the version on + // /data. Switch back to the application on /system. + // It's safe to assume the application on /system will correctly scan. If not, + // there won't be a working copy of the application. + // Also, if the sharedUserSetting of the application on /system is different + // from the sharedUserSetting on /data, switch back to the application on /system. + // We should trust the sharedUserSetting on /system, even if the application + // version on /system is smaller than the version on /data. + synchronized (mPm.mLock) { + // just remove the loaded entries from package lists + mPm.mPackages.remove(pkgSetting.getPackageName()); + } + + logCriticalInfo(Log.WARN, + "System package updated;" + + " name: " + pkgSetting.getPackageName() + + "; " + pkgSetting.getVersionCode() + " --> " + + parsedPackage.getLongVersionCode() + + "; " + pkgSetting.getPathString() + + " --> " + parsedPackage.getPath()); + + mRemovePackageHelper.cleanUpResources(pkgSetting.getPackageName(), + new File(pkgSetting.getPathString()), + getAppDexInstructionSets(pkgSetting.getPrimaryCpuAbiLegacy(), + pkgSetting.getSecondaryCpuAbiLegacy())); + synchronized (mPm.mLock) { + mPm.mSettings.enableSystemPackageLPw(pkgSetting.getPackageName()); + } + } + + // The version of the application on the /system partition is less than or + // equal to the version on the /data partition. Throw an exception and use + // the application already installed on the /data partition. + if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) { + // For some updated system packages, during addForInit we want to ensure the + // PackageSetting has the correct SigningDetails compares to the original version on + // the system partition. For the check to happen later during the /data scan, update + // the disabled package setting per the original APK on a system partition so that it + // can be trusted during reconcile. + if (needSignatureMatchToSystem(parsedPackage.getPackageName())) { + final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); + final ParseResult result = + ParsingPackageUtils.getSigningDetails(input, parsedPackage, + false /*skipVerify*/); + if (result.isError()) { + throw new PrepareFailure("Failed collect during scanSystemPackageLI", + result.getException()); + } + disabledPkgSetting.setSigningDetails(result.getResult()); + } + + // In the case of a skipped package, commitReconciledScanResultLocked is not called to + // add the object to the "live" data structures, so this is the final mutation step + // for the package. Which means it needs to be finalized here to cache derived fields. + // This is relevant for cases where the disabled system package is used for flags or + // other metadata. + parsedPackage.hideAsFinal(); + throw PackageManagerException.ofInternalError( + "Package " + parsedPackage.getPackageName() + + " at " + parsedPackage.getPath() + " ignored: updated version " + + (pkgAlreadyExists ? String.valueOf(pkgSetting.getVersionCode()) : "unknown") + + " better than this " + parsedPackage.getLongVersionCode(), + PackageManagerException.INTERNAL_ERROR_UPDATED_VERSION_BETTER_THAN_SYSTEM); + } + + // Verify certificates against what was last scanned. Force re-collecting certificate in two + // special cases: + // 1) when scanning system, force re-collect only if system is upgrading. + // 2) when scanning /data, force re-collect only if the package name is allowlisted. + final boolean forceCollect = scanSystemPartition ? isUpgrade + : pkgAlreadyExists && needSignatureMatchToSystem(pkgSetting.getPackageName()); + if (DEBUG_VERIFY && forceCollect) { + Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName()); + } + + // APK verification can be skipped during certificate collection, only if the file is in a + // verified partition. + final boolean skipVerify = scanSystemPartition; + ScanPackageUtils.collectCertificatesLI(pkgSetting, parsedPackage, + mPm.getSettingsVersionForPackage(parsedPackage), forceCollect, skipVerify, + mPm.isPreNMR1Upgrade()); + + // Reset profile if the application version is changed + maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage); + + /* + * A new system app appeared, but we already had a non-system one of the + * same name installed earlier. + */ + boolean shouldHideSystemApp = false; + // A new application appeared on /system, but, we already have a copy of + // the application installed on /data. + if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists + && !pkgSetting.isSystem()) { + + if (!parsedPackage.getSigningDetails() + .checkCapability(pkgSetting.getSigningDetails(), + SigningDetails.CertCapabilities.INSTALLED_DATA) + && !pkgSetting.getSigningDetails().checkCapability( + parsedPackage.getSigningDetails(), + SigningDetails.CertCapabilities.ROLLBACK)) { + logCriticalInfo(Log.WARN, + "System package signature mismatch;" + + " name: " + pkgSetting.getPackageName()); + try (@SuppressWarnings("unused") PackageFreezer freezer = mPm.freezePackage( + parsedPackage.getPackageName(), UserHandle.USER_ALL, + "scanPackageInternalLI", ApplicationExitInfo.REASON_OTHER, + null /* request */)) { + mDeletePackageHelper.deletePackageLIF( + parsedPackage.getPackageName(), null, true, + mPm.mUserManager.getUserIds(), 0, new PackageRemovedInfo(), false); + } + } else if (newPkgVersionGreater || newSharedUserSetting) { + // The application on /system is newer than the application on /data. + // Simply remove the application on /data [keeping application data] + // and replace it with the version on /system. + // Also, if the sharedUserSetting of the application on /system is different + // from the sharedUserSetting on data, we should trust the sharedUserSetting + // on /system, even if the application version on /system is smaller than + // the version on /data. + logCriticalInfo(Log.WARN, + "System package enabled;" + + " name: " + pkgSetting.getPackageName() + + "; " + pkgSetting.getVersionCode() + " --> " + + parsedPackage.getLongVersionCode() + + "; " + pkgSetting.getPathString() + " --> " + + parsedPackage.getPath()); + mRemovePackageHelper.cleanUpResources(pkgSetting.getPackageName(), + new File(pkgSetting.getPathString()), + getAppDexInstructionSets( + pkgSetting.getPrimaryCpuAbiLegacy(), pkgSetting.getSecondaryCpuAbiLegacy())); + } else { + // The application on /system is older than the application on /data. Hide + // the application on /system and the version on /data will be scanned later + // and re-added like an update. + shouldHideSystemApp = true; + logCriticalInfo(Log.INFO, + "System package disabled;" + + " name: " + pkgSetting.getPackageName() + + "; old: " + pkgSetting.getPathString() + " @ " + + pkgSetting.getVersionCode() + + "; new: " + parsedPackage.getPath() + " @ " + + parsedPackage.getLongVersionCode()); + } + } + + // A new application appeared on /system, and we are seeing it for the first time. + // Its also not updated as we don't have a copy of it on /data. So, scan it in a + // STOPPED state. + // We'll skip this step under the following conditions: + // - It's "android" + // - It's an APEX or overlay package since stopped state does not affect them. + // - It is enumerated with a tag having the stopped attribute + // set to false + // - It doesn't have an enabled and exported launcher activity, which means the user + // wouldn't have a way to un-stop it + final boolean isApexPkg = (scanFlags & SCAN_AS_APEX) != 0; + if (mPm.mShouldStopSystemPackagesByDefault + && scanSystemPartition + && !pkgAlreadyExists + && !isApexPkg + && !parsedPackage.isOverlayIsStatic() + ) { + String packageName = parsedPackage.getPackageName(); + if (!"android".contentEquals(packageName) + && !mPm.mInitialNonStoppedSystemPackages.contains(packageName) + && hasLauncherEntry(parsedPackage)) { + scanFlags |= SCAN_AS_STOPPED_SYSTEM_APP; + } + } + + final long firstInstallTime = Flags.fixSystemAppsFirstInstallTime() + ? System.currentTimeMillis() : 0; + final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, + scanFlags | SCAN_UPDATE_SIGNATURE, firstInstallTime, user, null); + return new Pair<>(scanResult, shouldHideSystemApp); + } + + private static boolean hasLauncherEntry(ParsedPackage parsedPackage) { + final HashSet categories = new HashSet<>(); + categories.add(Intent.CATEGORY_LAUNCHER); + final List activities = parsedPackage.getActivities(); + for (int indexActivity = 0; indexActivity < activities.size(); indexActivity++) { + final ParsedActivity activity = activities.get(indexActivity); + if (!activity.isEnabled() || !activity.isExported()) { + continue; + } + final List intents = activity.getIntents(); + for (int indexIntent = 0; indexIntent < intents.size(); indexIntent++) { + final IntentFilter intentFilter = intents.get(indexIntent).getIntentFilter(); + if (intentFilter != null && intentFilter.matchCategories(categories) == null) { + return true; + } + } + } + return false; + } + + /** + * Returns whether the package needs a signature verification against the pre-installed version + * at boot. + */ + private boolean needSignatureMatchToSystem(String packageName) { + if (!android.security.Flags.extendVbChainToUpdatedApk()) { + return false; + } + return mPm.mInjector.getSystemConfig().getPreinstallPackagesWithStrictSignatureCheck() + .contains(packageName); + } + + /** + * Clear the package profile if this was an upgrade and the package + * version was updated. + */ + private void maybeClearProfilesForUpgradesLI( + @Nullable PackageSetting originalPkgSetting, + @NonNull AndroidPackage pkg) { + if (originalPkgSetting == null || !mPm.isDeviceUpgrading()) { + return; + } + if (originalPkgSetting.getVersionCode() == pkg.getLongVersionCode()) { + return; + } + + mAppDataHelper.clearAppProfilesLIF(pkg); + if (DEBUG_INSTALL) { + Slog.d(TAG, originalPkgSetting.getPackageName() + + " clear profile due to version change " + + originalPkgSetting.getVersionCode() + " != " + + pkg.getLongVersionCode()); + } + } + + /** + * Returns the original package setting. + *

A package can migrate its name during an update. In this scenario, a package + * designates a set of names that it considers as one of its original names. + *

An original package must be signed identically and it must have the same + * shared user [if any]. + */ + @GuardedBy("mPm.mLock") + @Nullable + private PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg, + @Nullable String renamedPkgName) { + if (ScanPackageUtils.isPackageRenamed(pkg, renamedPkgName)) { + return null; + } + for (int i = ArrayUtils.size(pkg.getOriginalPackages()) - 1; i >= 0; --i) { + final PackageSetting originalPs = + mPm.mSettings.getPackageLPr(pkg.getOriginalPackages().get(i)); + if (originalPs != null) { + // the package is already installed under its original name... + // but, should we use it? + if (!verifyPackageUpdateLPr(originalPs, pkg)) { + // the new package is incompatible with the original + continue; + } else if (mPm.mSettings.getSharedUserSettingLPr(originalPs) != null) { + final String sharedUserSettingsName = + mPm.mSettings.getSharedUserSettingLPr(originalPs).name; + if (!sharedUserSettingsName.equals(pkg.getSharedUserId())) { + // the shared user id is incompatible with the original + Slog.w(TAG, "Unable to migrate data from " + originalPs.getPackageName() + + " to " + pkg.getPackageName() + ": old shared user settings name " + + sharedUserSettingsName + + " differs from " + pkg.getSharedUserId()); + continue; + } + // TODO: Add case when shared user id is added [b/28144775] + } else { + if (DEBUG_UPGRADE) { + Log.v(TAG, "Renaming new package " + + pkg.getPackageName() + " to old name " + + originalPs.getPackageName()); + } + } + return originalPs; + } + } + return null; + } + + @GuardedBy("mPm.mLock") + private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) { + if ((oldPkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) == 0) { + Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName() + + " to " + newPkg.getPackageName() + + ": old package not in system partition"); + return false; + } else if (mPm.mPackages.get(oldPkg.getPackageName()) != null) { + Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName() + + " to " + newPkg.getPackageName() + + ": old package still exists"); + return false; + } + return true; + } + + /** + * Asserts the parsed package is valid according to the given policy. If the + * package is invalid, for whatever reason, throws {@link PackageManagerException}. + *

+ * Implementation detail: This method must NOT have any side effects. It would + * ideally be static, but, it requires locks to read system state. + * + * @throws PackageManagerException If the package fails any of the validation checks + */ + private void assertPackageIsValid(AndroidPackage pkg, + final @ParsingPackageUtils.ParseFlags int parseFlags, + final @PackageManagerService.ScanFlags int scanFlags) + throws PackageManagerException { + if ((parseFlags & ParsingPackageUtils.PARSE_ENFORCE_CODE) != 0) { + ScanPackageUtils.assertCodePolicy(pkg); + } + + if (pkg.getPath() == null) { + // Bail out. The resource and code paths haven't been set. + throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, + "Code and resource paths haven't been set correctly"); + } + + // Check that there is an APEX package with the same name only during install/first boot + // after OTA. + final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0; + final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0; + // It is allowed to install a new APEX with the same name. But there shouldn't be + // conflicting names between APK and APEX. + final boolean installApex = (scanFlags & SCAN_AS_APEX) != 0; + if ((isUserInstall || isFirstBootOrUpgrade) + && mPm.snapshotComputer().isApexPackage(pkg.getPackageName()) + && !installApex) { + throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE, + pkg.getPackageName() + + " is an APEX package and can't be installed as an APK."); + } + + // Make sure we're not adding any bogus keyset info + final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService(); + ksms.assertScannedPackageValid(pkg); + + synchronized (mPm.mLock) { + // The special "android" package can only be defined once + if (pkg.getPackageName().equals("android")) { + if (mPm.getCoreAndroidApplication() != null) { + Slog.w(TAG, "*************************************************"); + Slog.w(TAG, "Core android package being redefined. Skipping."); + Slog.w(TAG, " codePath=" + pkg.getPath()); + Slog.w(TAG, "*************************************************"); + throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE, + "Core android package being redefined. Skipping."); + } + } + + // A package name must be unique; don't allow duplicates + if ((scanFlags & SCAN_NEW_INSTALL) == 0 + && mPm.mPackages.containsKey(pkg.getPackageName())) { + throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE, + "Application package " + pkg.getPackageName() + + " already installed. Skipping duplicate."); + } + + if (pkg.isStaticSharedLibrary()) { + // Static libs have a synthetic package name containing the version + // but we still want the base name to be unique. + if ((scanFlags & SCAN_NEW_INSTALL) == 0 + && mPm.mPackages.containsKey(pkg.getManifestPackageName())) { + throw PackageManagerException.ofInternalError( + "Duplicate static shared lib provider package", + PackageManagerException.INTERNAL_ERROR_DUP_STATIC_SHARED_LIB_PROVIDER); + } + ScanPackageUtils.assertStaticSharedLibraryIsValid(pkg, scanFlags); + assertStaticSharedLibraryVersionCodeIsValid(pkg); + } + + // 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 (mPm.isExpectingBetter(pkg.getPackageName())) { + Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package " + + pkg.getPackageName()); + } else { + PackageSetting known = mPm.mSettings.getPackageLPr(pkg.getPackageName()); + if (known != null) { + if (DEBUG_PACKAGE_SCANNING) { + Log.d(TAG, "Examining " + pkg.getPath() + + " and requiring known path " + known.getPathString()); + } + if (!pkg.getPath().equals(known.getPathString())) { + throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED, + "Application package " + pkg.getPackageName() + + " found at " + pkg.getPath() + + " but expected at " + known.getPathString() + + "; ignoring."); + } + } else { + throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION, + "Application package " + pkg.getPackageName() + + " not found; ignoring."); + } + } + } + + // Verify that this new package doesn't have any content providers + // that conflict with existing packages. Only do this if the + // package isn't already installed, since we don't want to break + // things that are installed. + if ((scanFlags & SCAN_NEW_INSTALL) != 0) { + mPm.mComponentResolver.assertProvidersNotDefined(pkg); + } + + // If this package has defined explicit processes, then ensure that these are + // the only processes used by its components. + ScanPackageUtils.assertProcessesAreValid(pkg); + + // Verify that packages sharing a user with a privileged app are marked as privileged. + assertPackageWithSharedUserIdIsPrivileged(pkg); + + // Apply policies specific for runtime resource overlays (RROs). + if (pkg.getOverlayTarget() != null) { + assertOverlayIsValid(pkg, parseFlags, scanFlags); + } + + // Ensure the package is signed with at least the minimum signature scheme version + // required for its target SDK. + ScanPackageUtils.assertMinSignatureSchemeIsValid(pkg, parseFlags); + } + } + + private void assertStaticSharedLibraryVersionCodeIsValid(AndroidPackage pkg) + throws PackageManagerException { + // The version codes must be ordered as lib versions + long minVersionCode = Long.MIN_VALUE; + long maxVersionCode = Long.MAX_VALUE; + + WatchedLongSparseArray versionedLib = + mSharedLibraries.getSharedLibraryInfos(pkg.getStaticSharedLibraryName()); + if (versionedLib != null) { + final int versionCount = versionedLib.size(); + for (int i = 0; i < versionCount; i++) { + SharedLibraryInfo libInfo = versionedLib.valueAt(i); + final long libVersionCode = libInfo.getDeclaringPackage() + .getLongVersionCode(); + if (libInfo.getLongVersion() < pkg.getStaticSharedLibraryVersion()) { + minVersionCode = Math.max(minVersionCode, libVersionCode + 1); + } else if (libInfo.getLongVersion() + > pkg.getStaticSharedLibraryVersion()) { + maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1); + } else { + minVersionCode = maxVersionCode = libVersionCode; + break; + } + } + } + if (pkg.getLongVersionCode() < minVersionCode + || pkg.getLongVersionCode() > maxVersionCode) { + throw PackageManagerException.ofInternalError("Static shared" + + " lib version codes must be ordered as lib versions", + PackageManagerException.INTERNAL_ERROR_STATIC_SHARED_LIB_VERSION_CODES_ORDER); + } + } + + private void assertOverlayIsValid(AndroidPackage pkg, + @ParsingPackageUtils.ParseFlags int parseFlags, + @PackageManagerService.ScanFlags int scanFlags) throws PackageManagerException { + // System overlays have some restrictions on their use of the 'static' state. + if ((scanFlags & SCAN_AS_SYSTEM) != 0) { + // We are scanning a system overlay. This can be the first scan of the + // system/vendor/oem partition, or an update to the system overlay. + if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) { + // This must be an update to a system overlay. Immutable overlays cannot be + // upgraded. + if (!mPm.isOverlayMutable(pkg.getPackageName())) { + throw PackageManagerException.ofInternalError("Overlay " + + pkg.getPackageName() + + " is static and cannot be upgraded.", + PackageManagerException.INTERNAL_ERROR_SYSTEM_OVERLAY_STATIC); + } + } else { + if ((scanFlags & SCAN_AS_VENDOR) != 0) { + if (pkg.getTargetSdkVersion() < ScanPackageUtils.getVendorPartitionVersion()) { + Slog.w(TAG, "System overlay " + pkg.getPackageName() + + " targets an SDK below the required SDK level of vendor" + + " overlays (" + + ScanPackageUtils.getVendorPartitionVersion() + + ")." + + " This will become an install error in a future release"); + } + } else if (pkg.getTargetSdkVersion() < Build.VERSION.SDK_INT) { + Slog.w(TAG, "System overlay " + pkg.getPackageName() + + " targets an SDK below the required SDK level of system" + + " overlays (" + Build.VERSION.SDK_INT + ")." + + " This will become an install error in a future release"); + } + } + } else { + // A non-preloaded overlay packages must have targetSdkVersion >= Q, or be + // signed with the platform certificate. Check this in increasing order of + // computational cost. + if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.Q) { + final PackageSetting platformPkgSetting; + synchronized (mPm.mLock) { + platformPkgSetting = mPm.mSettings.getPackageLPr("android"); + } + if (!comparePackageSignatures(platformPkgSetting, pkg.getSigningDetails())) { + throw PackageManagerException.ofInternalError("Overlay " + + pkg.getPackageName() + + " must target Q or later, " + + "or be signed with the platform certificate", + PackageManagerException.INTERNAL_ERROR_OVERLAY_LOW_TARGET_SDK); + } + } + + // A non-preloaded overlay package, without , will + // only be used if it is signed with the same certificate as its target OR if + // it is signed with the same certificate as a reference package declared + // in 'overlay-config-signature' tag of SystemConfig. + // If the target is already installed or 'overlay-config-signature' tag in + // SystemConfig is set, check this here to augment the last line of defense + // which is OMS. + if (pkg.getOverlayTargetOverlayableName() == null) { + final PackageSetting targetPkgSetting; + synchronized (mPm.mLock) { + targetPkgSetting = mPm.mSettings.getPackageLPr(pkg.getOverlayTarget()); + } + if (targetPkgSetting != null) { + if (!comparePackageSignatures(targetPkgSetting, pkg.getSigningDetails())) { + // check reference signature + if (mPm.mOverlayConfigSignaturePackage == null) { + throw PackageManagerException.ofInternalError("Overlay " + + pkg.getPackageName() + " and target " + + pkg.getOverlayTarget() + " signed with" + + " different certificates, and the overlay lacks" + + " ", + PackageManagerException.INTERNAL_ERROR_OVERLAY_SIGNATURE1); + } + final PackageSetting refPkgSetting; + synchronized (mPm.mLock) { + refPkgSetting = mPm.mSettings.getPackageLPr( + mPm.mOverlayConfigSignaturePackage); + } + if (!comparePackageSignatures(refPkgSetting, pkg.getSigningDetails())) { + throw PackageManagerException.ofInternalError("Overlay " + + pkg.getPackageName() + " signed with a different " + + "certificate than both the reference package and " + + "target " + pkg.getOverlayTarget() + ", and the " + + "overlay lacks ", + PackageManagerException.INTERNAL_ERROR_OVERLAY_SIGNATURE2); + } + } + } + } + } + } + + private void assertPackageWithSharedUserIdIsPrivileged(AndroidPackage pkg) + throws PackageManagerException { + if (!AndroidPackageLegacyUtils.isPrivileged(pkg) + && (pkg.getSharedUserId() != null) + && !pkg.isLeavingSharedUser()) { + SharedUserSetting sharedUserSetting = null; + try { + synchronized (mPm.mLock) { + sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(), + 0, 0, false); + } + } catch (PackageManagerException ignore) { + } + if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) { + // Exempt SharedUsers signed with the platform key. + final PackageSetting platformPkgSetting; + synchronized (mPm.mLock) { + platformPkgSetting = mPm.mSettings.getPackageLPr("android"); + } + if (!comparePackageSignatures(platformPkgSetting, pkg.getSigningDetails())) { + throw PackageManagerException.ofInternalError("Apps that share a user with a " + + "privileged app must themselves be marked as privileged. " + + pkg.getPackageName() + " shares privileged user " + + pkg.getSharedUserId() + ".", + PackageManagerException.INTERNAL_ERROR_NOT_PRIV_SHARED_USER); + } + } + } + } + + private @PackageManagerService.ScanFlags int adjustScanFlags( + @PackageManagerService.ScanFlags int scanFlags, + @Nullable PackageSetting existingPkgSetting, + @Nullable PackageSetting disabledPkgSetting, UserHandle user, + @NonNull AndroidPackage pkg) { + scanFlags = ScanPackageUtils.adjustScanFlagsWithPackageSetting(scanFlags, existingPkgSetting, + disabledPkgSetting, user); + + // Exception for privileged apps that share a user with a priv-app. + final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0) + && ScanPackageUtils.getVendorPartitionVersion() < 28; + if (((scanFlags & SCAN_AS_PRIVILEGED) == 0) + && !AndroidPackageLegacyUtils.isPrivileged(pkg) + && (pkg.getSharedUserId() != null) + && !skipVendorPrivilegeScan + && !pkg.isLeavingSharedUser()) { + SharedUserSetting sharedUserSetting = null; + synchronized (mPm.mLock) { + try { + sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(), 0, + 0, false); + } catch (PackageManagerException ignore) { + } + if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) { + // Exempt SharedUsers signed with the platform key. + // TODO(b/72378145) Fix this exemption. Force signature apps + // to allowlist their privileged permissions just like other + // priv-apps. + PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android"); + if ((compareSignatures(platformPkgSetting.getSigningDetails(), + pkg.getSigningDetails()) != PackageManager.SIGNATURE_MATCH)) { + scanFlags |= SCAN_AS_PRIVILEGED; + } + } + } + } + + return scanFlags; + } +} diff --git a/aosp/frameworks/base/services/core/java/com/android/server/pm/Installer.java b/aosp/frameworks/base/services/core/java/com/android/server/pm/Installer.java new file mode 100644 index 0000000000000000000000000000000000000000..5457539002bdbaf99a28b7d86c40d337b89d788c --- /dev/null +++ b/aosp/frameworks/base/services/core/java/com/android/server/pm/Installer.java @@ -0,0 +1,1269 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static com.android.server.pm.DexOptHelper.useArtService; + +import android.annotation.AppIdInt; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.Context; +import android.content.pm.PackageStats; +import android.os.Binder; +import android.os.Build; +import android.os.CreateAppDataArgs; +import android.os.CreateAppDataResult; +import android.os.IBinder; +import android.os.IInstalld; +import android.os.ParcelFileDescriptor; +import android.os.ReconcileSdkDataArgs; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.storage.CrateMetadata; +import android.text.format.DateUtils; +import android.util.EventLog; +import android.util.Slog; + +import com.android.internal.os.BackgroundThread; +import com.android.server.EventLogTags; +import com.android.server.SystemService; + +import dalvik.system.BlockGuard; +import dalvik.system.VMRuntime; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class Installer extends SystemService { + private static final String TAG = "Installer"; + + /* *************************************************************************** + * IMPORTANT: These values are passed to native code. Keep them in sync with + * frameworks/native/cmds/installd/installd_constants.h + * **************************************************************************/ + /** Application should be visible to everyone */ + public static final int DEXOPT_PUBLIC = 1 << 1; + /** Application wants to allow debugging of its code */ + public static final int DEXOPT_DEBUGGABLE = 1 << 2; + /** The system boot has finished */ + public static final int DEXOPT_BOOTCOMPLETE = 1 << 3; + /** Hint that the dexopt type is profile-guided. */ + public static final int DEXOPT_PROFILE_GUIDED = 1 << 4; + /** The compilation is for a secondary dex file. */ + public static final int DEXOPT_SECONDARY_DEX = 1 << 5; + /** Ignore the result of dexoptNeeded and force compilation. */ + public static final int DEXOPT_FORCE = 1 << 6; + /** Indicates that the dex file passed to dexopt in on CE storage. */ + public static final int DEXOPT_STORAGE_CE = 1 << 7; + /** Indicates that the dex file passed to dexopt in on DE storage. */ + public static final int DEXOPT_STORAGE_DE = 1 << 8; + /** Indicates that dexopt is invoked from the background service. */ + public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9; + /** Indicates that dexopt should restrict access to private APIs. */ + public static final int DEXOPT_ENABLE_HIDDEN_API_CHECKS = 1 << 10; + /** Indicates that dexopt should convert to CompactDex. */ + public static final int DEXOPT_GENERATE_COMPACT_DEX = 1 << 11; + /** Indicates that dexopt should generate an app image */ + public static final int DEXOPT_GENERATE_APP_IMAGE = 1 << 12; + /** Indicates that dexopt may be run with different performance / priority tuned for restore */ + public static final int DEXOPT_FOR_RESTORE = 1 << 13; // TODO(b/135202722): remove + + /** The result of the profile analysis indicating that the app should be optimized. */ + public static final int PROFILE_ANALYSIS_OPTIMIZE = 1; + /** The result of the profile analysis indicating that the app should not be optimized. */ + public static final int PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA = 2; + /** + * The result of the profile analysis indicating that the app should not be optimized because + * the profiles are empty. + */ + public static final int PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES = 3; + + /** + * The results of {@code getOdexVisibility}. See + * {@link #getOdexVisibility(String, String, String)} for details. + */ + public static final int ODEX_NOT_FOUND = 0; + public static final int ODEX_IS_PUBLIC = 1; + public static final int ODEX_IS_PRIVATE = 2; + + + public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE; + public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE; + public static final int FLAG_STORAGE_EXTERNAL = IInstalld.FLAG_STORAGE_EXTERNAL; + public static final int FLAG_STORAGE_SDK = IInstalld.FLAG_STORAGE_SDK; + + public static final int FLAG_CLEAR_CACHE_ONLY = IInstalld.FLAG_CLEAR_CACHE_ONLY; + public static final int FLAG_CLEAR_CODE_CACHE_ONLY = IInstalld.FLAG_CLEAR_CODE_CACHE_ONLY; + + public static final int FLAG_FREE_CACHE_V2 = IInstalld.FLAG_FREE_CACHE_V2; + public static final int FLAG_FREE_CACHE_V2_DEFY_QUOTA = IInstalld.FLAG_FREE_CACHE_V2_DEFY_QUOTA; + public static final int FLAG_FREE_CACHE_NOOP = IInstalld.FLAG_FREE_CACHE_NOOP; + public static final int FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES = + IInstalld.FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES; + + public static final int FLAG_USE_QUOTA = IInstalld.FLAG_USE_QUOTA; + public static final int FLAG_FORCE = IInstalld.FLAG_FORCE; + + public static final int FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES = + IInstalld.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES; + + private static final long CONNECT_RETRY_DELAY_MS = DateUtils.SECOND_IN_MILLIS; + private static final long CONNECT_WAIT_MS = 10 * DateUtils.SECOND_IN_MILLIS; + + private final boolean mIsolated; + private volatile boolean mDeferSetFirstBoot; + private volatile IInstalld mInstalld = null; + private volatile CountDownLatch mInstalldLatch = new CountDownLatch(1); + private volatile Object mWarnIfHeld; + + public Installer(Context context) { + this(context, false); + } + + /** + * @param isolated Make the installer isolated. See {@link isIsolated}. + */ + public Installer(Context context, boolean isolated) { + super(context); + mIsolated = isolated; + } + + /** + * Yell loudly if someone tries making future calls while holding a lock on + * the given object. + */ + public void setWarnIfHeld(Object warnIfHeld) { + mWarnIfHeld = warnIfHeld; + } + + /** + * Returns true if the installer is isolated, i.e. if this object should not connect to + * the real {@code installd}. All remote calls will be ignored unless you extend this class and + * intercept them. + */ + public boolean isIsolated() { + return mIsolated; + } + + @Override + public void onStart() { + if (mIsolated) { + mInstalld = null; + mInstalldLatch.countDown(); + } else { + connect(); + } + } + + private void connect() { + IBinder binder = ServiceManager.getService("installd"); + if (binder != null) { + try { + binder.linkToDeath(() -> { + Slog.w(TAG, "installd died; reconnecting"); + mInstalldLatch = new CountDownLatch(1); + connect(); + }, 0); + } catch (RemoteException e) { + binder = null; + } + } + + if (binder != null) { + IInstalld installd = IInstalld.Stub.asInterface(binder); + mInstalld = installd; + mInstalldLatch.countDown(); + try { + invalidateMounts(); + executeDeferredActions(); + } catch (InstallerException ignored) { + } + } else { + Slog.w(TAG, "installd not found; trying again"); + BackgroundThread.getHandler().postDelayed(this::connect, CONNECT_RETRY_DELAY_MS); + } + } + + /** + * Perform any deferred actions on mInstalld while the connection could not be made. + */ + private void executeDeferredActions() throws InstallerException { + if (mDeferSetFirstBoot) { + setFirstBoot(); + } + } + + /** + * Do several pre-flight checks before making a remote call. + * + * @return if the remote call should continue. + */ + private boolean checkBeforeRemote() throws InstallerException { + if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) { + Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x" + + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable()); + } + if (mIsolated) { + Slog.i(TAG, "Ignoring request because this installer is isolated"); + return false; + } + + try { + if (!mInstalldLatch.await(CONNECT_WAIT_MS, TimeUnit.MILLISECONDS)) { + throw new InstallerException("time out waiting for the installer to be ready"); + } + } catch (InterruptedException e) { + // Do nothing. + } + + return true; + } + + // We explicitly do NOT set previousAppId because the default value should always be 0. + // Manually override previousAppId after building CreateAppDataArgs for specific behaviors. + static CreateAppDataArgs buildCreateAppDataArgs(String uuid, String packageName, + int userId, int flags, int appId, String seInfo, int targetSdkVersion, + boolean usesSdk) { + final CreateAppDataArgs args = new CreateAppDataArgs(); + args.uuid = uuid; + args.packageName = packageName; + args.userId = userId; + args.flags = flags; + if (usesSdk) { + args.flags |= FLAG_STORAGE_SDK; + } + args.appId = appId; + args.seInfo = seInfo; + args.targetSdkVersion = targetSdkVersion; + return args; + } + + private static CreateAppDataResult buildPlaceholderCreateAppDataResult() { + final CreateAppDataResult result = new CreateAppDataResult(); + result.ceDataInode = -1; + result.deDataInode = -1; + result.exceptionCode = 0; + result.exceptionMessage = null; + return result; + } + + static ReconcileSdkDataArgs buildReconcileSdkDataArgs(String uuid, String packageName, + List subDirNames, int userId, int appId, + String seInfo, int flags) { + final ReconcileSdkDataArgs args = new ReconcileSdkDataArgs(); + args.uuid = uuid; + args.packageName = packageName; + args.subDirNames = subDirNames; + args.userId = userId; + args.appId = appId; + args.previousAppId = 0; + args.seInfo = seInfo; + args.flags = flags; + return args; + } + + public @NonNull CreateAppDataResult createAppData(@NonNull CreateAppDataArgs args) + throws InstallerException { + if (!checkBeforeRemote()) { + return buildPlaceholderCreateAppDataResult(); + } + // Hardcode previousAppId to 0 to disable any data migration (http://b/221088088) + args.previousAppId = 0; + try { + return mInstalld.createAppData(args); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public @NonNull CreateAppDataResult[] createAppDataBatched(@NonNull CreateAppDataArgs[] args) + throws InstallerException { + if (!checkBeforeRemote()) { + final CreateAppDataResult[] results = new CreateAppDataResult[args.length]; + Arrays.fill(results, buildPlaceholderCreateAppDataResult()); + return results; + } + // Hardcode previousAppId to 0 to disable any data migration (http://b/221088088) + for (final CreateAppDataArgs arg : args) { + arg.previousAppId = 0; + } + try { + return mInstalld.createAppDataBatched(args); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + void reconcileSdkData(@NonNull ReconcileSdkDataArgs args) + throws InstallerException { + if (!checkBeforeRemote()) { + return; + } + try { + mInstalld.reconcileSdkData(args); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Sets in Installd that it is first boot after data wipe + */ + public void setFirstBoot() throws InstallerException { + if (!checkBeforeRemote()) { + return; + } + try { + // mInstalld might be null if the connection could not be established. + if (mInstalld != null) { + mInstalld.setFirstBoot(); + } else { + // if it is null while trying to set the first boot, set a flag to try and set the + // first boot when the connection is eventually established + mDeferSetFirstBoot = true; + } + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Class that collects multiple {@code installd} operations together in an + * attempt to more efficiently execute them in bulk. + *

+ * Instead of returning results immediately, {@link CompletableFuture} + * instances are returned which can be used to chain follow-up work for each + * request. + *

+ * The creator of this object must invoke {@link #execute()} + * exactly once to begin execution of all pending operations. Once execution + * has been kicked off, no additional events can be enqueued into this + * instance, but multiple instances can safely exist in parallel. + */ + public static class Batch { + private static final int CREATE_APP_DATA_BATCH_SIZE = 256; + + private boolean mExecuted; + + private final List mArgs = new ArrayList<>(); + private final List> mFutures = new ArrayList<>(); + + /** + * Enqueue the given {@code installd} operation to be executed in the + * future when {@link #execute(Installer)} is invoked. + *

+ * Callers of this method are not required to hold a monitor lock on an + * {@link Installer} object. + */ + @NonNull + public synchronized CompletableFuture createAppData( + CreateAppDataArgs args) { + if (mExecuted) { + throw new IllegalStateException(); + } + final CompletableFuture future = new CompletableFuture<>(); + mArgs.add(args); + mFutures.add(future); + return future; + } + + /** + * Execute all pending {@code installd} operations that have been + * collected by this batch in a blocking fashion. + *

+ * Callers of this method must hold a monitor lock on the given + * {@link Installer} object. + */ + public synchronized void execute(@NonNull Installer installer) throws InstallerException { + if (mExecuted) throw new IllegalStateException(); + mExecuted = true; + + final int size = mArgs.size(); + for (int i = 0; i < size; i += CREATE_APP_DATA_BATCH_SIZE) { + final CreateAppDataArgs[] args = new CreateAppDataArgs[Math.min(size - i, + CREATE_APP_DATA_BATCH_SIZE)]; + for (int j = 0; j < args.length; j++) { + args[j] = mArgs.get(i + j); + } + final CreateAppDataResult[] results = installer.createAppDataBatched(args); + for (int j = 0; j < results.length; j++) { + final CreateAppDataResult result = results[j]; + final CompletableFuture future = mFutures.get(i + j); + if (result.exceptionCode == 0) { + future.complete(result); + } else { + future.completeExceptionally( + new InstallerException(result.exceptionMessage)); + } + } + } + } + } + + public void restoreconAppData(String uuid, String packageName, int userId, int flags, int appId, + String seInfo) throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.restoreconAppData(uuid, packageName, userId, flags, appId, seInfo); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void migrateAppData(String uuid, String packageName, int userId, int flags) + throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.migrateAppData(uuid, packageName, userId, flags); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void clearAppData(String uuid, String packageName, int userId, int flags, + long ceDataInode) throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.clearAppData(uuid, packageName, userId, flags, ceDataInode); + + final StackTraceElement[] elements = Thread.currentThread().getStackTrace(); + String className; + String methodName; + String fileName; + int lineNumber; + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + EventLog.writeEvent(EventLogTags.INSTALLER_CLEAR_APP_DATA_CALLER, pid, uid, packageName, + flags); + // Skip the first two elements since they are always the same, ie + // Thread#getStackTrace() and VMStack#getThreadStackTrace() + for (int i = 2; i < elements.length; i++) { + className = elements[i].getClassName(); + methodName = elements[i].getMethodName(); + fileName = elements[i].getFileName(); + lineNumber = elements[i].getLineNumber(); + EventLog.writeEvent(EventLogTags.INSTALLER_CLEAR_APP_DATA_CALL_STACK, methodName, + className, fileName, lineNumber); + } + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void destroyAppData(String uuid, String packageName, int userId, int flags, + long ceDataInode) throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.destroyAppData(uuid, packageName, userId, flags, ceDataInode); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void fixupAppData(String uuid, int flags) throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.fixupAppData(uuid, flags); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Remove all invalid dirs under app data folder. + * All dirs are supposed to be valid file and package names. + */ + public void cleanupInvalidPackageDirs(String uuid, int userId, int flags) + throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.cleanupInvalidPackageDirs(uuid, userId, flags); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void moveCompleteApp(String fromUuid, String toUuid, String packageName, + int appId, String seInfo, int targetSdkVersion, + String fromCodePath) throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.moveCompleteApp(fromUuid, toUuid, packageName, appId, seInfo, + targetSdkVersion, fromCodePath); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void getAppSize(String uuid, String[] packageNames, int userId, int flags, int appId, + long[] ceDataInodes, String[] codePaths, PackageStats stats) + throws InstallerException { + if (!checkBeforeRemote()) return; + if (codePaths != null) { + for (String codePath : codePaths) { + BlockGuard.getVmPolicy().onPathAccess(codePath); + } + } + try { + final long[] res = mInstalld.getAppSize(uuid, packageNames, userId, flags, + appId, ceDataInodes, codePaths); + stats.codeSize += res[0]; + stats.dataSize += res[1]; + stats.cacheSize += res[2]; + stats.externalCodeSize += res[3]; + stats.externalDataSize += res[4]; + stats.externalCacheSize += res[5]; + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void getUserSize(String uuid, int userId, int flags, int[] appIds, PackageStats stats) + throws InstallerException { + if (!checkBeforeRemote()) return; + try { + final long[] res = mInstalld.getUserSize(uuid, userId, flags, appIds); + stats.codeSize += res[0]; + stats.dataSize += res[1]; + stats.cacheSize += res[2]; + stats.externalCodeSize += res[3]; + stats.externalDataSize += res[4]; + stats.externalCacheSize += res[5]; + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public long[] getExternalSize(String uuid, int userId, int flags, int[] appIds) + throws InstallerException { + if (!checkBeforeRemote()) return new long[6]; + try { + return mInstalld.getExternalSize(uuid, userId, flags, appIds); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * To get all of the CrateMetadata of the crates for the specified user app by the installd. + * + * @param uuid the UUID + * @param packageNames the application package names + * @param userId the user id + * @return the array of CrateMetadata + */ + @Nullable + public CrateMetadata[] getAppCrates(@NonNull String uuid, @NonNull String[] packageNames, + @UserIdInt int userId) throws InstallerException { + if (!checkBeforeRemote()) return null; + try { + return mInstalld.getAppCrates(uuid, packageNames, userId); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * To retrieve all of the CrateMetadata of the crate for the specified user app by the installd. + * + * @param uuid the UUID + * @param userId the user id + * @return the array of CrateMetadata + */ + @Nullable + public CrateMetadata[] getUserCrates(String uuid, @UserIdInt int userId) + throws InstallerException { + if (!checkBeforeRemote()) return null; + try { + return mInstalld.getUserCrates(uuid, userId); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void setAppQuota(String uuid, int userId, int appId, long cacheQuota) + throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.setAppQuota(uuid, userId, appId, cacheQuota); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Runs dex optimization. + * + * @param apkPath Path of target APK + * @param uid UID of the package + * @param pkgName Name of the package + * @param instructionSet Target instruction set to run dex optimization. + * @param dexoptNeeded Necessary dex optimization for this request. Check + * {@link dalvik.system.DexFile#NO_DEXOPT_NEEDED}, + * {@link dalvik.system.DexFile#DEX2OAT_FROM_SCRATCH}, + * {@link dalvik.system.DexFile#DEX2OAT_FOR_BOOT_IMAGE}, and + * {@link dalvik.system.DexFile#DEX2OAT_FOR_FILTER}. + * @param outputPath Output path of generated dex optimization. + * @param dexFlags Check {@code DEXOPT_*} for allowed flags. + * @param compilerFilter Compiler filter like "verify", "speed-profile". Check + * {@code art/libartbase/base/compiler_filter.cc} for full list. + * @param volumeUuid UUID of the volume where the package data is stored. {@code null} + * represents internal storage. + * @param classLoaderContext This encodes the class loader chain (class loader type + class + * path) in a format compatible to dex2oat. Check + * {@code DexoptUtils.processContextForDexLoad} for further details. + * @param seInfo Selinux context to set for generated outputs. + * @param downgrade If set, allows downgrading {@code compilerFilter}. If downgrading is not + * allowed and requested {@code compilerFilter} is considered as downgrade, + * the request will be ignored. + * @param targetSdkVersion Target SDK version of the package. + * @param profileName Name of reference profile file. + * @param dexMetadataPath Specifies the location of dex metadata file. + * @param compilationReason Specifies the reason for the compilation like "install". + * @return {@code true} if {@code dexopt} is completed. {@code false} if it was cancelled. + * + * @throws InstallerException if {@code dexopt} fails. + */ + public boolean dexopt(String apkPath, int uid, String pkgName, String instructionSet, + int dexoptNeeded, @Nullable String outputPath, int dexFlags, String compilerFilter, + @Nullable String volumeUuid, @Nullable String classLoaderContext, + @Nullable String seInfo, boolean downgrade, int targetSdkVersion, + @Nullable String profileName, @Nullable String dexMetadataPath, + @Nullable String compilationReason) + throws InstallerException, LegacyDexoptDisabledException { + checkLegacyDexoptDisabled(); + assertValidInstructionSet(instructionSet); + BlockGuard.getVmPolicy().onPathAccess(apkPath); + BlockGuard.getVmPolicy().onPathAccess(outputPath); + BlockGuard.getVmPolicy().onPathAccess(dexMetadataPath); + if (!checkBeforeRemote()) return false; + try { + return mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath, + dexFlags, compilerFilter, volumeUuid, classLoaderContext, seInfo, downgrade, + targetSdkVersion, profileName, dexMetadataPath, compilationReason); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Enables or disables dex optimization blocking. + * + *

Enabling blocking will also involve cancelling pending dexopt call and killing child + * processes forked from installd to run dexopt. The pending dexopt call will return false + * when it is cancelled. + * + * @param block set to true to enable blocking / false to disable blocking. + */ + public void controlDexOptBlocking(boolean block) throws LegacyDexoptDisabledException { + checkLegacyDexoptDisabled(); + try { + mInstalld.controlDexOptBlocking(block); + } catch (Exception e) { + Slog.w(TAG, "blockDexOpt failed", e); + } + } + + /** + * Analyzes the ART profiles of the given package, possibly merging the information + * into the reference profile. Returns whether or not we should optimize the package + * based on how much information is in the profile. + * + * @return one of {@link #PROFILE_ANALYSIS_OPTIMIZE}, + * {@link #PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA}, + * {@link #PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES} + */ + public int mergeProfiles(int uid, String packageName, String profileName) + throws InstallerException, LegacyDexoptDisabledException { + checkLegacyDexoptDisabled(); + if (!checkBeforeRemote()) return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; + try { + return mInstalld.mergeProfiles(uid, packageName, profileName); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Dumps profiles associated with a package in a human readable format. + */ + public boolean dumpProfiles(int uid, String packageName, String profileName, String codePath, + boolean dumpClassesAndMethods) + throws InstallerException, LegacyDexoptDisabledException { + checkLegacyDexoptDisabled(); + if (!checkBeforeRemote()) return false; + BlockGuard.getVmPolicy().onPathAccess(codePath); + try { + return mInstalld.dumpProfiles(uid, packageName, profileName, codePath, + dumpClassesAndMethods); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public boolean copySystemProfile(String systemProfile, int uid, String packageName, + String profileName) throws InstallerException, LegacyDexoptDisabledException { + checkLegacyDexoptDisabled(); + if (!checkBeforeRemote()) return false; + try { + return mInstalld.copySystemProfile(systemProfile, uid, packageName, profileName); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void rmdex(String codePath, String instructionSet) + throws InstallerException, LegacyDexoptDisabledException { + checkLegacyDexoptDisabled(); + assertValidInstructionSet(instructionSet); + if (!checkBeforeRemote()) return; + BlockGuard.getVmPolicy().onPathAccess(codePath); + try { + mInstalld.rmdex(codePath, instructionSet); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Remove a directory belonging to a package. + */ + public void rmPackageDir(String packageName, String packageDir) throws InstallerException { + if (!checkBeforeRemote()) return; + BlockGuard.getVmPolicy().onPathAccess(packageDir); + try { + mInstalld.rmPackageDir(packageName, packageDir); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void cleanAppLink(String packageName, int deleteFlags) throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.cleanAppLink(packageName, deleteFlags); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void clearAppProfiles(String packageName, String profileName) + throws InstallerException, LegacyDexoptDisabledException { + checkLegacyDexoptDisabled(); + if (!checkBeforeRemote()) return; + try { + mInstalld.clearAppProfiles(packageName, profileName); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void destroyAppProfiles(String packageName) + throws InstallerException, LegacyDexoptDisabledException { + checkLegacyDexoptDisabled(); + if (!checkBeforeRemote()) return; + try { + mInstalld.destroyAppProfiles(packageName); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Deletes the reference profile with the given name of the given package. + * @throws InstallerException if the deletion fails. + */ + public void deleteReferenceProfile(String packageName, String profileName) + throws InstallerException, LegacyDexoptDisabledException { + checkLegacyDexoptDisabled(); + if (!checkBeforeRemote()) return; + try { + mInstalld.deleteReferenceProfile(packageName, profileName); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void createUserData(String uuid, int userId, int userSerial, int flags) + throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.createUserData(uuid, userId, userSerial, flags); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void destroyUserData(String uuid, int userId, int flags) throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.destroyUserData(uuid, userId, flags); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Deletes cache from specified uuid until targetFreeBytes amount of space is free. + * flag denotes aggressive or non-aggresive mode where cache under quota is eligible or not + * respectively for clearing. + */ + public void freeCache(String uuid, long targetFreeBytes, int flags) throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.freeCache(uuid, targetFreeBytes, flags); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Links the 32 bit native library directory in an application's data + * directory to the real location for backward compatibility. Note that no + * such symlink is created for 64 bit shared libraries. + */ + public void linkNativeLibraryDirectory(String uuid, String packageName, String nativeLibPath32, + int userId) throws InstallerException { + if (!checkBeforeRemote()) return; + BlockGuard.getVmPolicy().onPathAccess(nativeLibPath32); + try { + mInstalld.linkNativeLibraryDirectory(uuid, packageName, nativeLibPath32, userId); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Creates an oat dir for given package and instruction set. + */ + public void createOatDir(String packageName, String oatDir, String dexInstructionSet) + throws InstallerException { + // This method should be allowed even if ART Service is enabled, because it's used for + // creating oat dirs before creating hard links for partial installation. + // TODO(b/274658735): Add an ART Service API to support hard linking. + if (!checkBeforeRemote()) return; + try { + mInstalld.createOatDir(packageName, oatDir, dexInstructionSet); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Creates a hardlink for a path. + */ + public void linkFile(String packageName, String relativePath, String fromBase, String toBase) + throws InstallerException { + if (!checkBeforeRemote()) return; + BlockGuard.getVmPolicy().onPathAccess(fromBase); + BlockGuard.getVmPolicy().onPathAccess(toBase); + try { + mInstalld.linkFile(packageName, relativePath, fromBase, toBase); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Moves oat/vdex/art from "B" set defined by ro.boot.slot_suffix to the default set. + */ + public void moveAb(String packageName, String apkPath, String instructionSet, String outputPath) + throws InstallerException { + if (!checkBeforeRemote()) return; + BlockGuard.getVmPolicy().onPathAccess(apkPath); + BlockGuard.getVmPolicy().onPathAccess(outputPath); + try { + mInstalld.moveAb(packageName, apkPath, instructionSet, outputPath); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Deletes the optimized artifacts generated by ART and returns the number + * of freed bytes. + */ + public long deleteOdex(String packageName, String apkPath, String instructionSet, + String outputPath) throws InstallerException, LegacyDexoptDisabledException { + checkLegacyDexoptDisabled(); + if (!checkBeforeRemote()) return -1; + BlockGuard.getVmPolicy().onPathAccess(apkPath); + BlockGuard.getVmPolicy().onPathAccess(outputPath); + try { + return mInstalld.deleteOdex(packageName, apkPath, instructionSet, outputPath); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public boolean reconcileSecondaryDexFile(String apkPath, String packageName, int uid, + String[] isas, @Nullable String volumeUuid, int flags) + throws InstallerException, LegacyDexoptDisabledException { + checkLegacyDexoptDisabled(); + for (int i = 0; i < isas.length; i++) { + assertValidInstructionSet(isas[i]); + } + if (!checkBeforeRemote()) return false; + BlockGuard.getVmPolicy().onPathAccess(apkPath); + try { + return mInstalld.reconcileSecondaryDexFile(apkPath, packageName, uid, isas, + volumeUuid, flags); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public byte[] hashSecondaryDexFile(String dexPath, String packageName, int uid, + @Nullable String volumeUuid, int flags) throws InstallerException { + if (!checkBeforeRemote()) return new byte[0]; + BlockGuard.getVmPolicy().onPathAccess(dexPath); + try { + return mInstalld.hashSecondaryDexFile(dexPath, packageName, uid, volumeUuid, flags); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public boolean createProfileSnapshot(int appId, String packageName, String profileName, + String classpath) throws InstallerException, LegacyDexoptDisabledException { + checkLegacyDexoptDisabled(); + if (!checkBeforeRemote()) return false; + try { + return mInstalld.createProfileSnapshot(appId, packageName, profileName, classpath); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void destroyProfileSnapshot(String packageName, String profileName) + throws InstallerException, LegacyDexoptDisabledException { + checkLegacyDexoptDisabled(); + if (!checkBeforeRemote()) return; + try { + mInstalld.destroyProfileSnapshot(packageName, profileName); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public void invalidateMounts() throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.invalidateMounts(); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public boolean isQuotaSupported(String volumeUuid) throws InstallerException { + if (!checkBeforeRemote()) return false; + try { + return mInstalld.isQuotaSupported(volumeUuid); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Bind mount private volume CE and DE mirror storage. + */ + public void tryMountDataMirror(String volumeUuid) throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.tryMountDataMirror(volumeUuid); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Unmount private volume CE and DE mirror storage. + */ + public void onPrivateVolumeRemoved(String volumeUuid) throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.onPrivateVolumeRemoved(volumeUuid); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Prepares the app profile for the package at the given path: + *

    + *
  • Creates the current profile for the given user ID, unless the user ID is + * {@code UserHandle.USER_NULL}.
  • + *
  • Merges the profile from the dex metadata file (if present) into the reference + * profile.
  • + *
+ */ + public boolean prepareAppProfile(String pkg, @UserIdInt int userId, @AppIdInt int appId, + String profileName, String codePath, String dexMetadataPath) + throws InstallerException, LegacyDexoptDisabledException { + checkLegacyDexoptDisabled(); + if (!checkBeforeRemote()) return false; + BlockGuard.getVmPolicy().onPathAccess(codePath); + BlockGuard.getVmPolicy().onPathAccess(dexMetadataPath); + try { + return mInstalld.prepareAppProfile(pkg, userId, appId, profileName, codePath, + dexMetadataPath); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Snapshots user data of the given package. + * + * @param pkg name of the package to snapshot user data for. + * @param userId id of the user whose data to snapshot. + * @param snapshotId id of this snapshot. + * @param storageFlags flags controlling which data (CE or DE) to snapshot. + * + * @return {@code true} if the snapshot was taken successfully, or {@code false} if a remote + * call shouldn't be continued. See {@link #checkBeforeRemote}. + * + * @throws InstallerException if failed to snapshot user data. + */ + public boolean snapshotAppData(String pkg, @UserIdInt int userId, int snapshotId, + int storageFlags) throws InstallerException { + if (!checkBeforeRemote()) return false; + + try { + mInstalld.snapshotAppData(null, pkg, userId, snapshotId, storageFlags); + return true; + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Restores user data snapshot of the given package. + * + * @param pkg name of the package to restore user data for. + * @param appId id of the package to restore user data for. + * @param userId id of the user whose data to restore. + * @param snapshotId id of the snapshot to restore. + * @param storageFlags flags controlling which data (CE or DE) to restore. + * + * @return {@code true} if user data restore was successful, or {@code false} if a remote call + * shouldn't be continued. See {@link #checkBeforeRemote}. + * + * @throws InstallerException if failed to restore user data. + */ + public boolean restoreAppDataSnapshot(String pkg, @AppIdInt int appId, String seInfo, + @UserIdInt int userId, int snapshotId, int storageFlags) throws InstallerException { + if (!checkBeforeRemote()) return false; + + try { + mInstalld.restoreAppDataSnapshot(null, pkg, appId, seInfo, userId, snapshotId, + storageFlags); + return true; + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Deletes user data snapshot of the given package. + * + * @param pkg name of the package to delete user data snapshot for. + * @param userId id of the user whose user data snapshot to delete. + * @param snapshotId id of the snapshot to delete. + * @param storageFlags flags controlling which user data snapshot (CE or DE) to delete. + * + * @return {@code true} if user data snapshot was successfully deleted, or {@code false} if a + * remote call shouldn't be continued. See {@link #checkBeforeRemote}. + * + * @throws InstallerException if failed to delete user data snapshot. + */ + public boolean destroyAppDataSnapshot(String pkg, @UserIdInt int userId, + int snapshotId, int storageFlags) throws InstallerException { + if (!checkBeforeRemote()) return false; + + try { + mInstalld.destroyAppDataSnapshot(null, pkg, userId, 0, snapshotId, storageFlags); + return true; + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Deletes all snapshots of credential encrypted user data, where the snapshot id is not + * included in {@code retainSnapshotIds}. + * + * @param userId id of the user whose user data snapshots to delete. + * @param retainSnapshotIds ids of the snapshots that should not be deleted. + * + * @return {@code true} if the operation was successful, or {@code false} if a remote call + * shouldn't be continued. See {@link #checkBeforeRemote}. + * + * @throws InstallerException if failed to delete user data snapshot. + */ + public boolean destroyCeSnapshotsNotSpecified(@UserIdInt int userId, + int[] retainSnapshotIds) throws InstallerException { + if (!checkBeforeRemote()) return false; + + try { + mInstalld.destroyCeSnapshotsNotSpecified(null, userId, retainSnapshotIds); + return true; + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Migrates obb data from its legacy location {@code /data/media/obb} to + * {@code /data/media/0/Android/obb}. This call is idempotent and a fast no-op if data has + * already been migrated. + * + * @throws InstallerException if an error occurs. + */ + public boolean migrateLegacyObbData() throws InstallerException { + if (!checkBeforeRemote()) return false; + + try { + mInstalld.migrateLegacyObbData(); + return true; + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + private static void assertValidInstructionSet(String instructionSet) + throws InstallerException { + for (String abi : Build.SUPPORTED_ABIS) { + if (VMRuntime.getInstructionSet(abi).equals(instructionSet)) { + return; + } + } + throw new InstallerException("Invalid instruction set: " + instructionSet); + } + + /** + * Returns the visibility of the optimized artifacts. + * + * @param packageName name of the package. + * @param apkPath path to the APK. + * @param instructionSet instruction set of the optimized artifacts. + * @param outputPath path to the directory that contains the optimized artifacts (i.e., the + * directory that {@link #dexopt} outputs to). + * + * @return {@link #ODEX_NOT_FOUND} if the optimized artifacts are not found, or + * {@link #ODEX_IS_PUBLIC} if the optimized artifacts are accessible by all apps, or + * {@link #ODEX_IS_PRIVATE} if the optimized artifacts are only accessible by this app. + * + * @throws InstallerException if failed to get the visibility of the optimized artifacts. + */ + public int getOdexVisibility(String packageName, String apkPath, String instructionSet, + String outputPath) throws InstallerException, LegacyDexoptDisabledException { + checkLegacyDexoptDisabled(); + if (!checkBeforeRemote()) return -1; + BlockGuard.getVmPolicy().onPathAccess(apkPath); + BlockGuard.getVmPolicy().onPathAccess(outputPath); + try { + return mInstalld.getOdexVisibility(packageName, apkPath, instructionSet, outputPath); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Returns an auth token for the provided writable FD. + * + * @param authFd a file descriptor to proof that the caller can write to the file. + * @param uid uid of the calling app. + * + * @return authToken, or null if a remote call shouldn't be continued. See {@link + * #checkBeforeRemote}. + * + * @throws InstallerException if the remote call failed. + */ + public IInstalld.IFsveritySetupAuthToken createFsveritySetupAuthToken( + ParcelFileDescriptor authFd, int uid) throws InstallerException { + if (!checkBeforeRemote()) { + return null; + } + try { + return mInstalld.createFsveritySetupAuthToken(authFd, uid); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + /** + * Enables fs-verity to the given app file. + * + * @param authToken a token previously returned from {@link #createFsveritySetupAuthToken}. + * @param filePath file path of the package to enable fs-verity. + * @param packageName name of the package. + * + * @return 0 if the operation was successful, otherwise {@code errno}. + * + * @throws InstallerException if the remote call failed (e.g. see {@link #checkBeforeRemote}). + */ + public int enableFsverity(IInstalld.IFsveritySetupAuthToken authToken, String filePath, + String packageName) throws InstallerException { + if (!checkBeforeRemote()) { + throw new InstallerException("fs-verity wasn't enabled with an isolated installer"); + } + BlockGuard.getVmPolicy().onPathAccess(filePath); + try { + return mInstalld.enableFsverity(authToken, filePath, packageName); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public static class InstallerException extends Exception { + public InstallerException(String detailMessage) { + super(detailMessage); + } + + public static InstallerException from(Exception e) throws InstallerException { + throw new InstallerException(e.toString()); + } + } + + /** + * A checked exception that is thrown in legacy dexopt code paths when ART Service should be + * used instead. + */ + public static class LegacyDexoptDisabledException extends Exception { + // TODO(b/260124949): Remove the legacy dexopt code paths, i.e. this exception and all code + // that may throw it. + public LegacyDexoptDisabledException() { + super("Invalid call to legacy dexopt method while ART Service is in use."); + } + } + + /** + * Throws LegacyDexoptDisabledException if ART Service should be used instead of the + * {@link android.os.IInstalld} method that follows this method call. + */ + public static void checkLegacyDexoptDisabled() throws LegacyDexoptDisabledException { + if (useArtService()) { + throw new LegacyDexoptDisabledException(); + } + } +} diff --git a/aosp/frameworks/base/services/core/java/com/android/server/pm/PackageManagerNative.java b/aosp/frameworks/base/services/core/java/com/android/server/pm/PackageManagerNative.java new file mode 100644 index 0000000000000000000000000000000000000000..6bf59244331c7738ed4c143a182b2d138207356e --- /dev/null +++ b/aosp/frameworks/base/services/core/java/com/android/server/pm/PackageManagerNative.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.content.pm.PackageManager.CERT_INPUT_SHA256; + +import static com.android.server.pm.PackageManagerService.TAG; + +import android.annotation.Nullable; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManagerNative; +import android.content.pm.IStagedApexObserver; +import android.content.pm.PackageInfo; +import android.content.pm.StagedApexInfo; +import android.os.Binder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Slog; + +import java.util.Arrays; + +final class PackageManagerNative extends IPackageManagerNative.Stub { + private final PackageManagerService mPm; + + PackageManagerNative(PackageManagerService pm) { + mPm = pm; + } + + @Override + public String[] getNamesForUids(int[] uids) throws RemoteException { + String[] names = null; + String[] results = null; + try { + if (uids == null || uids.length == 0) { + return null; + } + names = mPm.snapshotComputer().getNamesForUids(uids); + results = (names != null) ? names : new String[uids.length]; + // 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; + } catch (Throwable t) { + // STOPSHIP(186558987): revert addition of try/catch/log + Slog.e(TAG, "uids: " + Arrays.toString(uids)); + Slog.e(TAG, "names: " + Arrays.toString(names)); + Slog.e(TAG, "results: " + Arrays.toString(results)); + Slog.e(TAG, "throwing exception", t); + throw t; + } + } + + // NB: this differentiates between preloads and sideloads + @Override + public String getInstallerForPackage(String packageName) throws RemoteException { + final Computer snapshot = mPm.snapshotComputer(); + final int callingUser = UserHandle.getUserId(Binder.getCallingUid()); + final String installerName = snapshot.getInstallerPackageName(packageName, callingUser); + if (!TextUtils.isEmpty(installerName)) { + return installerName; + } + // differentiate between preload and sideload + ApplicationInfo appInfo = snapshot.getApplicationInfo(packageName, + /*flags*/ 0, + /*userId*/ callingUser); + if (appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + return "preload"; + } + return ""; + } + + @Override + public long getVersionCodeForPackage(String packageName) throws RemoteException { + try { + int callingUser = UserHandle.getUserId(Binder.getCallingUid()); + PackageInfo pInfo = mPm.snapshotComputer() + .getPackageInfo(packageName, 0, callingUser); + if (pInfo != null) { + return pInfo.getLongVersionCode(); + } + } catch (Exception e) { + } + return 0; + } + + @Override + public int getTargetSdkVersionForPackage(String packageName) throws RemoteException { + int targetSdk = mPm.snapshotComputer().getTargetSdkVersion(packageName); + if (targetSdk != -1) { + return targetSdk; + } + + throw new RemoteException("Couldn't get targetSdkVersion for package " + packageName); + } + + @Override + public boolean isPackageDebuggable(String packageName) throws RemoteException { + int callingUser = UserHandle.getCallingUserId(); + ApplicationInfo appInfo = mPm.snapshotComputer() + .getApplicationInfo(packageName, 0, callingUser); + if (appInfo != null) { + return (0 != (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE)); + } + + throw new RemoteException("Couldn't get debug flag for package " + packageName); + } + + @Override + public boolean[] isAudioPlaybackCaptureAllowed(String[] packageNames) + throws RemoteException { + int callingUser = UserHandle.getUserId(Binder.getCallingUid()); + final Computer snapshot = mPm.snapshotComputer(); + boolean[] results = new boolean[packageNames.length]; + for (int i = results.length - 1; i >= 0; --i) { + ApplicationInfo appInfo = snapshot.getApplicationInfo(packageNames[i], 0, callingUser); + results[i] = appInfo != null && appInfo.isAudioPlaybackCaptureAllowed(); + } + return results; + } + + @Override + public int getLocationFlags(String packageName) throws RemoteException { + int callingUser = UserHandle.getUserId(Binder.getCallingUid()); + ApplicationInfo appInfo = mPm.snapshotComputer().getApplicationInfo(packageName, + /*flags*/ 0, + /*userId*/ callingUser); + if (appInfo == null) { + throw new RemoteException( + "Couldn't get ApplicationInfo for package " + packageName); + } + return ((appInfo.isSystemApp() ? IPackageManagerNative.LOCATION_SYSTEM : 0) + | (appInfo.isVendor() ? IPackageManagerNative.LOCATION_VENDOR : 0) + | (appInfo.isProduct() ? IPackageManagerNative.LOCATION_PRODUCT : 0)); + } + + @Override + public String getModuleMetadataPackageName() throws RemoteException { + return mPm.getModuleMetadataPackageName(); + } + + @Override + public int getPackageUid(String packageName, int userId) throws RemoteException { + return mPm.getPackageUid(packageName, 0, userId); + } + + @Override + public boolean hasSha256SigningCertificate(String packageName, byte[] certificate) + throws RemoteException { + return mPm.snapshotComputer() + .hasSigningCertificate(packageName, certificate, CERT_INPUT_SHA256); + } + + @Override + public boolean hasSystemFeature(String featureName, int version) { + return mPm.hasSystemFeature(featureName, version); + } + + @Override + public void registerStagedApexObserver(IStagedApexObserver observer) { + mPm.mInstallerService.getStagingManager().registerStagedApexObserver(observer); + } + + @Override + public void unregisterStagedApexObserver(IStagedApexObserver observer) { + mPm.mInstallerService.getStagingManager().unregisterStagedApexObserver(observer); + } + + @Override + public String[] getStagedApexModuleNames() { + return mPm.mInstallerService.getStagingManager() + .getStagedApexModuleNames().toArray(new String[0]); + } + + @Override + @Nullable + public StagedApexInfo getStagedApexInfo(String moduleName) { + return mPm.mInstallerService.getStagingManager().getStagedApexInfo(moduleName); + } +} diff --git a/aosp/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java b/aosp/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java new file mode 100644 index 0000000000000000000000000000000000000000..9d06b0b2f241c87e8d62df699611fb15cae256fd --- /dev/null +++ b/aosp/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java @@ -0,0 +1,8265 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.Manifest.permission.MANAGE_DEVICE_ADMINS; +import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS; +import static android.app.AppOpsManager.MODE_IGNORED; +import static android.content.pm.PackageManager.APP_METADATA_SOURCE_APK; +import static android.content.pm.PackageManager.APP_METADATA_SOURCE_UNKNOWN; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import static android.content.pm.PackageManager.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_SYSTEM_ONLY; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET; +import static android.content.pm.PackageParser.isApkFile; +import static android.os.Process.INVALID_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.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL; +import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE; +import static android.util.FeatureFlagUtils.SETTINGS_TREAT_PAUSE_AS_QUARANTINE; + +import static com.android.internal.annotations.VisibleForTesting.Visibility; +import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME; +import static com.android.server.pm.DexOptHelper.useArtService; +import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet; +import static com.android.server.pm.InstructionSets.getPreferredInstructionSet; +import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures; +import static com.android.server.pm.PackageManagerServiceUtils.isInstalledByAdb; +import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; + +import android.Manifest; +import android.annotation.AppIdInt; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StringRes; +import android.annotation.UserIdInt; +import android.annotation.WorkerThread; +import android.app.ActivityManager; +import android.app.AppOpsManager; +import android.app.ApplicationExitInfo; +import android.app.ApplicationPackageManager; +import android.app.BroadcastOptions; +import android.app.IActivityManager; +import android.app.admin.IDevicePolicyManager; +import android.app.admin.SecurityLog; +import android.app.backup.IBackupManager; +import android.app.role.RoleManager; +import android.companion.virtual.VirtualDeviceManager; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.IntentSender.SendIntentException; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ArchivedPackageParcel; +import android.content.pm.AuxiliaryResolveInfo; +import android.content.pm.ChangedPackages; +import android.content.pm.Checksum; +import android.content.pm.ComponentInfo; +import android.content.pm.DataLoaderType; +import android.content.pm.FallbackCategoryProvider; +import android.content.pm.FeatureInfo; +import android.content.pm.Flags; +import android.content.pm.IDexModuleRegisterCallback; +import android.content.pm.IOnChecksumsReadyListener; +import android.content.pm.IPackageDataObserver; +import android.content.pm.IPackageDeleteObserver2; +import android.content.pm.IPackageLoadingProgressCallback; +import android.content.pm.IPackageMoveObserver; +import android.content.pm.IncrementalStatesInfo; +import android.content.pm.InstallSourceInfo; +import android.content.pm.InstantAppInfo; +import android.content.pm.InstantAppRequest; +import android.content.pm.ModuleInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageInfoLite; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.ComponentEnabledSetting; +import android.content.pm.PackageManagerInternal; +import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; +import android.content.pm.PackageParser.ParseFlags; +import android.content.pm.PackagePartitions; +import android.content.pm.ParceledListSlice; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.SharedLibraryInfo; +import android.content.pm.Signature; +import android.content.pm.SigningDetails; +import android.content.pm.SuspendDialogInfo; +import android.content.pm.TestUtilityService; +import android.content.pm.UserInfo; +import android.content.pm.UserPackage; +import android.content.pm.VerifierDeviceIdentity; +import android.content.pm.VerifierInfo; +import android.content.pm.VersionedPackage; +import android.content.pm.overlay.OverlayPaths; +import android.content.pm.parsing.PackageLite; +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.Binder; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.FileUtils; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.IRemoteCallback; +import android.os.Message; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.ParcelableException; +import android.os.PersistableBundle; +import android.os.Process; +import android.os.ReconcileSdkDataArgs; +import android.os.RemoteException; +import android.os.ResultReceiver; +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.incremental.IncrementalManager; +import android.os.incremental.PerUidReadTimeouts; +import android.os.storage.StorageManager; +import android.os.storage.StorageManagerInternal; +import android.os.storage.VolumeRecord; +import android.permission.PermissionManager; +import android.provider.DeviceConfig; +import android.provider.Settings.Global; +import android.provider.Settings.Secure; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.DisplayMetrics; +import android.util.EventLog; +import android.util.ExceptionUtils; +import android.util.FeatureFlagUtils; +import android.util.Log; +import android.util.Pair; +import android.util.Slog; +import android.util.SparseArray; +import android.util.SparseBooleanArray; +import android.util.Xml; +import android.view.Display; + +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.ResolverActivity; +import com.android.internal.content.F2fsUtils; +import com.android.internal.content.InstallLocationUtils; +import com.android.internal.content.om.OverlayConfig; +import com.android.internal.pm.parsing.PackageParser2; +import com.android.internal.pm.parsing.pkg.AndroidPackageInternal; +import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.component.ParsedInstrumentation; +import com.android.internal.pm.pkg.component.ParsedMainComponent; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.telephony.CarrierAppUtils; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; +import com.android.internal.util.ConcurrentUtils; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.FrameworkStatsLog; +import com.android.internal.util.FunctionalUtils; +import com.android.internal.util.Preconditions; +import com.android.modules.utils.TypedXmlPullParser; +import com.android.modules.utils.TypedXmlSerializer; +import com.android.permission.persistence.RuntimePermissionsPersistence; +import com.android.permission.persistence.RuntimePermissionsState; +import com.android.server.EventLogTags; +import com.android.server.FgThread; +import com.android.server.LocalManagerRegistry; +import com.android.server.LocalServices; +import com.android.server.LockGuard; +import com.android.server.PackageWatchdog; +import com.android.server.ServiceThread; +import com.android.server.SystemConfig; +import com.android.server.ThreadPriorityBooster; +import com.android.server.Watchdog; +import com.android.server.apphibernation.AppHibernationManagerInternal; +import com.android.server.art.DexUseManagerLocal; +import com.android.server.art.model.DeleteResult; +import com.android.server.compat.CompatChange; +import com.android.server.compat.PlatformCompat; +import com.android.server.pm.Installer.InstallerException; +import com.android.server.pm.Installer.LegacyDexoptDisabledException; +import com.android.server.pm.Settings.VersionInfo; +import com.android.server.pm.dex.ArtManagerService; +import com.android.server.pm.dex.ArtUtils; +import com.android.server.pm.dex.DexManager; +import com.android.server.pm.dex.DynamicCodeLogger; +import com.android.server.pm.local.PackageManagerLocalImpl; +import com.android.server.pm.parsing.PackageCacher; +import com.android.server.pm.parsing.PackageInfoUtils; +import com.android.server.pm.parsing.pkg.AndroidPackageUtils; +import com.android.server.pm.permission.LegacyPermissionManagerInternal; +import com.android.server.pm.permission.LegacyPermissionManagerService; +import com.android.server.pm.permission.LegacyPermissionSettings; +import com.android.server.pm.permission.PermissionManagerService; +import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.ArchiveState; +import com.android.server.pm.pkg.PackageState; +import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.pkg.PackageUserState; +import com.android.server.pm.pkg.PackageUserStateInternal; +import com.android.server.pm.pkg.SharedUserApi; +import com.android.server.pm.pkg.mutate.PackageStateMutator; +import com.android.server.pm.pkg.mutate.PackageStateWrite; +import com.android.server.pm.pkg.mutate.PackageUserStateWrite; +import com.android.server.pm.resolution.ComponentResolver; +import com.android.server.pm.resolution.ComponentResolverApi; +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; +import com.android.server.pm.verify.domain.DomainVerificationService; +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; +import com.android.server.sdksandbox.SdkSandboxManagerLocal; +import com.android.server.storage.DeviceStorageMonitorInternal; +import com.android.server.utils.SnapshotCache; +import com.android.server.utils.TimingsTraceAndSlog; +import com.android.server.utils.Watchable; +import com.android.server.utils.Watched; +import com.android.server.utils.WatchedArrayMap; +import com.android.server.utils.WatchedSparseBooleanArray; +import com.android.server.utils.WatchedSparseIntArray; +import com.android.server.utils.Watcher; + +import dalvik.system.VMRuntime; + +import libcore.util.EmptyArray; +import libcore.util.HexEncoding; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +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.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.function.Predicate; + +/** + * Keep track of all those APKs everywhere. + *

+ * Internally there are three important locks: + *

    + *
  • {@link #mLock} is used to guard all in-memory parsed package details + * and other related state. It is a fine-grained lock that should only be held + * momentarily, as it's one of the most contended locks in the system. + *
  • {@link #mInstallLock} is used to guard all {@code installd} access, whose + * operations typically involve heavy lifting of application data on disk. Since + * {@code installd} is single-threaded, and it's operations can often be slow, + * this lock should never be acquired while already holding {@link #mLock}. + * Conversely, it's safe to acquire {@link #mLock} momentarily while already + * holding {@link #mInstallLock}. + *
  • {@link #mSnapshotLock} is used to guard access to two snapshot fields: the snapshot + * itself and the snapshot invalidation flag. This lock should never be acquired while + * already holding {@link #mLock}. Conversely, it's safe to acquire {@link #mLock} + * momentarily while already holding {@link #mSnapshotLock}. + *
+ * Many internal methods rely on the caller to hold the appropriate locks, and + * this contract is expressed through method name suffixes: + *
    + *
  • fooLI(): the caller must hold {@link #mInstallLock} + *
  • fooLIF(): the caller must hold {@link #mInstallLock} and the package + * being modified must be frozen + *
  • fooLPr(): the caller must hold {@link #mLock} for reading + *
  • fooLPw(): the caller must hold {@link #mLock} for writing + *
+ * {@link #mSnapshotLock} is taken in exactly one place - {@code snapshotComputer()}. It + * should not be taken anywhere else or used for any other purpose. + *

+ * Because this class is very central to the platform's security; please run all + * CTS and unit tests whenever making modifications: + * + *

+ * $ runtest -c android.content.pm.PackageManagerTests frameworks-core
+ * $ cts-tradefed run commandAndExit cts -m CtsAppSecurityHostTestCases
+ * 
+ */ +public class PackageManagerService implements PackageSender, TestUtilityService { + + static final String TAG = "PackageManager"; + public static final boolean DEBUG_SETTINGS = false; + static final boolean DEBUG_PREFERRED = false; + static final boolean DEBUG_UPGRADE = false; + static final boolean DEBUG_DOMAIN_VERIFICATION = false; + static final boolean DEBUG_BACKUP = false; + public static final boolean DEBUG_INSTALL = false; + public static final boolean DEBUG_REMOVE = false; + static final boolean DEBUG_PACKAGE_INFO = false; + static final boolean DEBUG_INTENT_MATCHING = false; + public static final boolean DEBUG_PACKAGE_SCANNING = false; + static final boolean DEBUG_VERIFY = false; + public static final boolean DEBUG_PERMISSIONS = false; + public static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE; + public static final boolean TRACE_SNAPSHOTS = false; + private static final boolean DEBUG_PER_UID_READ_TIMEOUTS = false; + + // Debug output for dexopting. This is shared between PackageManagerService, OtaDexoptService + // and PackageDexOptimizer. All these classes have their own flag to allow switching a single + // user, but by default initialize to this. + public static final boolean DEBUG_DEXOPT = false; + + static final boolean DEBUG_ABI_SELECTION = false; + public static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE; + + static final String SHELL_PACKAGE_NAME = "com.android.shell"; + + static final boolean HIDE_EPHEMERAL_APIS = false; + + static final String PRECOMPILE_LAYOUTS = "pm.precompile_layouts"; + + private static final int RADIO_UID = Process.PHONE_UID; + private static final int LOG_UID = Process.LOG_UID; + private static final int NFC_UID = Process.NFC_UID; + private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID; + private static final int SHELL_UID = Process.SHELL_UID; + private static final int SE_UID = Process.SE_UID; + private static final int NETWORKSTACK_UID = Process.NETWORK_STACK_UID; + private static final int UWB_UID = Process.UWB_UID; + + static final int SCAN_NO_DEX = 1 << 0; + static final int SCAN_UPDATE_SIGNATURE = 1 << 1; + static final int SCAN_NEW_INSTALL = 1 << 2; + static final int SCAN_UPDATE_TIME = 1 << 3; + static final int SCAN_BOOTING = 1 << 4; + static final int SCAN_REQUIRE_KNOWN = 1 << 7; + static final int SCAN_MOVE = 1 << 8; + static final int SCAN_INITIAL = 1 << 9; + static final int SCAN_DONT_KILL_APP = 1 << 10; + static final int SCAN_IGNORE_FROZEN = 1 << 11; + static final int SCAN_FIRST_BOOT_OR_UPGRADE = 1 << 12; + static final int SCAN_AS_INSTANT_APP = 1 << 13; + static final int SCAN_AS_FULL_APP = 1 << 14; + static final int SCAN_AS_VIRTUAL_PRELOAD = 1 << 15; + static final int SCAN_AS_SYSTEM = 1 << 16; + static final int SCAN_AS_PRIVILEGED = 1 << 17; + static final int SCAN_AS_OEM = 1 << 18; + static final int SCAN_AS_VENDOR = 1 << 19; + static final int SCAN_AS_PRODUCT = 1 << 20; + static final int SCAN_AS_SYSTEM_EXT = 1 << 21; + static final int SCAN_AS_ODM = 1 << 22; + static final int SCAN_AS_APK_IN_APEX = 1 << 23; + static final int SCAN_DROP_CACHE = 1 << 24; + static final int SCAN_AS_FACTORY = 1 << 25; + static final int SCAN_AS_APEX = 1 << 26; + static final int SCAN_AS_STOPPED_SYSTEM_APP = 1 << 27; + static final int SCAN_FAST_COMMAND = 1 << 28; + + @IntDef(flag = true, prefix = { "SCAN_" }, value = { + SCAN_NO_DEX, + SCAN_UPDATE_SIGNATURE, + SCAN_NEW_INSTALL, + SCAN_UPDATE_TIME, + SCAN_BOOTING, + SCAN_REQUIRE_KNOWN, + SCAN_MOVE, + SCAN_INITIAL, + SCAN_DONT_KILL_APP, + SCAN_IGNORE_FROZEN, + SCAN_FIRST_BOOT_OR_UPGRADE, + SCAN_AS_INSTANT_APP, + SCAN_AS_FULL_APP, + SCAN_AS_VIRTUAL_PRELOAD, + SCAN_AS_STOPPED_SYSTEM_APP, + SCAN_FAST_COMMAND, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ScanFlags {} + + /** + * Used as the result code of the {@link Computer#getPackageStartability(boolean, String, int, + * int)}. + */ + @IntDef(value = { + PACKAGE_STARTABILITY_OK, + PACKAGE_STARTABILITY_NOT_FOUND, + PACKAGE_STARTABILITY_NOT_SYSTEM, + PACKAGE_STARTABILITY_FROZEN, + PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PackageStartability {} + + /** + * Used as the result code of the {@link Computer#getPackageStartability(boolean, String, int, + * int)} to indicate the given package is allowed to start. + */ + public static final int PACKAGE_STARTABILITY_OK = 0; + + /** + * Used as the result code of the {@link Computer#getPackageStartability(boolean, String, int, + * int)} to indicate the given package is not allowed to start because it's not found + * (could be due to that package is invisible to the given user). + */ + public static final int PACKAGE_STARTABILITY_NOT_FOUND = 1; + + /** + * Used as the result code of the {@link Computer#getPackageStartability(boolean, String, int, + * int)} to indicate the given package is not allowed to start because it's not a system + * app and the system is running in safe mode. + */ + public static final int PACKAGE_STARTABILITY_NOT_SYSTEM = 2; + + /** + * Used as the result code of the {@link Computer#getPackageStartability(boolean, String, int, + * int)} to indicate the given package is not allowed to start because it's currently + * frozen. + */ + public static final int PACKAGE_STARTABILITY_FROZEN = 3; + + /** + * Used as the result code of the {@link Computer#getPackageStartability(boolean, String, int, + * int)} to indicate the given package is not allowed to start because it doesn't support + * direct boot. + */ + public static final int PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED = 4; + + private static final String STATIC_SHARED_LIB_DELIMITER = "_"; + /** + * Extension of the compressed packages + */ + public final static String COMPRESSED_EXTENSION = ".gz"; + /** Suffix of stub packages on the system partition */ + public final static String STUB_SUFFIX = "-Stub"; + + static final int[] EMPTY_INT_ARRAY = new int[0]; + + /** + * Timeout (in milliseconds) after which the watchdog should declare that + * our handler thread is wedged. The usual default for such things is one + * minute but we sometimes do very lengthy I/O operations on this thread, + * such as installing multi-gigabyte applications, so ours needs to be longer. + */ + static final long WATCHDOG_TIMEOUT = 1000*60*10; // ten minutes + + // How long to wait for Lock in async writeSettings and writePackageList. + private static final long WRITE_LOCK_TIMEOUT_MS = 1000 * 10; // 10 seconds + + /** + * Default IncFs timeouts. Maximum values in IncFs is 1hr. + * + *

If flag value is empty, the default value will be assigned. + * + * Flag type: {@code String} + * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE + */ + private static final String PROPERTY_INCFS_DEFAULT_TIMEOUTS = "incfs_default_timeouts"; + + /** + * Known digesters with optional timeouts. + * + * Flag type: {@code String} + * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE + */ + private static final String PROPERTY_KNOWN_DIGESTERS_LIST = "known_digesters_list"; + + /** + * Whether or not requesting the approval before committing sessions is available. + * + * Flag type: {@code boolean} + * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE + */ + private static final String PROPERTY_IS_PRE_APPROVAL_REQUEST_AVAILABLE = + "is_preapproval_available"; + + /** + * Whether or not the update ownership enforcement is available. + * + * Flag type: {@code boolean} + * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE + */ + private static final String PROPERTY_IS_UPDATE_OWNERSHIP_ENFORCEMENT_AVAILABLE = + "is_update_ownership_enforcement_available"; + + private static final String PROPERTY_DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED = + "deferred_no_kill_post_delete_delay_ms_extended"; + + /** + * The default response for package verification timeout. + * + * This can be either PackageManager.VERIFICATION_ALLOW or + * PackageManager.VERIFICATION_REJECT. + */ + static final int DEFAULT_VERIFICATION_RESPONSE = PackageManager.VERIFICATION_ALLOW; + + /** + * Adding an installer package name to a package that does not have one set requires the + * INSTALL_PACKAGES permission. + * + * If the caller targets R, this will throw a SecurityException. Otherwise the request will + * fail silently. In both cases, and regardless of whether this change is enabled, the + * installer package will remain unchanged. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) + private static final long THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE = + 150857253; + + public static final String PLATFORM_PACKAGE_NAME = "android"; + + static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; + + static final String PACKAGE_SCHEME = "package"; + + private static final String COMPANION_PACKAGE_NAME = "com.android.companiondevicemanager"; + + // How many required verifiers can be on the system. + private static final int REQUIRED_VERIFIERS_MAX_COUNT = 2; + + /** + * Specifies the minimum target SDK version an apk must specify in order to be installed + * on the system. This improves security and privacy by blocking low + * target sdk apps as malware can target older sdk versions to avoid + * the enforcement of new API behavior. + */ + public static final int MIN_INSTALLABLE_TARGET_SDK = + Flags.minTargetSdk24() ? Build.VERSION_CODES.N : Build.VERSION_CODES.M; + + // Compilation reasons. + // TODO(b/260124949): Clean this up with the legacy dexopt code. + public static final int REASON_FIRST_BOOT = 0; + public static final int REASON_BOOT_AFTER_OTA = 1; + public static final int REASON_POST_BOOT = 2; + public static final int REASON_INSTALL = 3; + public static final int REASON_INSTALL_FAST = 4; + public static final int REASON_INSTALL_BULK = 5; + public static final int REASON_INSTALL_BULK_SECONDARY = 6; + public static final int REASON_INSTALL_BULK_DOWNGRADED = 7; + public static final int REASON_INSTALL_BULK_SECONDARY_DOWNGRADED = 8; + public static final int REASON_BACKGROUND_DEXOPT = 9; + public static final int REASON_AB_OTA = 10; + public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 11; + public static final int REASON_CMDLINE = 12; + public static final int REASON_BOOT_AFTER_MAINLINE_UPDATE = 13; + public static final int REASON_SHARED = 14; + + public static final int REASON_LAST = REASON_SHARED; + + static final String RANDOM_DIR_PREFIX = "~~"; + static final char RANDOM_CODEPATH_PREFIX = '-'; + + public static final String APP_METADATA_FILE_NAME = "app.metadata"; + public static final String APP_METADATA_FILE_IN_APK_PATH = "assets/" + APP_METADATA_FILE_NAME; + + static final int DEFAULT_FILE_ACCESS_MODE = 0644; + + static final int DEFAULT_NATIVE_LIBRARY_FILE_ACCESS_MODE = 0755; + + final Handler mHandler; + final Handler mBackgroundHandler; + + final ProcessLoggingHandler mProcessLoggingHandler; + + private final int mSdkVersion; + final Context mContext; + final boolean mFactoryTest; + final DisplayMetrics mMetrics; + private final int mDefParseFlags; + private final String[] mSeparateProcesses; + private final boolean mIsUpgrade; + private final boolean mIsPreNMR1Upgrade; + private final boolean mIsPreQUpgrade; + // If mIsUpgrade == true, contains the prior SDK version, else -1. + private final int mPriorSdkVersion; + + // Used for privilege escalation. MUST NOT BE CALLED WITH mPackages + // LOCK HELD. Can be called with mInstallLock held. + @GuardedBy("mInstallLock") + final Installer mInstaller; + + /** Directory where installed applications are stored */ + private final File mAppInstallDir; + + // ---------------------------------------------------------------- + + // Lock for state used when installing and doing other long running + // operations. Methods that must be called with this lock held have + // the suffix "LI". + final Object mInstallLock; + + // ---------------------------------------------------------------- + + // Lock for global state used when modifying package state or settings. + // Methods that must be called with this lock held have + // the suffix "Locked". Some methods may use the legacy suffix "LP" + final PackageManagerTracedLock mLock; + + // Ensures order of overlay updates until data storage can be moved to overlay code + private final PackageManagerTracedLock mOverlayPathsLock = new PackageManagerTracedLock(); + + // Lock alias for doing package state mutation + private final PackageManagerTracedLock mPackageStateWriteLock; + + private final PackageStateMutator mPackageStateMutator = new PackageStateMutator( + this::getPackageSettingForMutation, + this::getDisabledPackageSettingForMutation); + + // Keys are String (package name), values are Package. + @Watched + @GuardedBy("mLock") + final WatchedArrayMap mPackages = new WatchedArrayMap<>(); + private final SnapshotCache> mPackagesSnapshot = + new SnapshotCache.Auto(mPackages, mPackages, "PackageManagerService.mPackages"); + + // Keys are isolated uids and values are the uid of the application + // that created the isolated process. + @Watched + @GuardedBy("mLock") + final WatchedSparseIntArray mIsolatedOwners = new WatchedSparseIntArray(); + private final SnapshotCache mIsolatedOwnersSnapshot = + new SnapshotCache.Auto(mIsolatedOwners, mIsolatedOwners, + "PackageManagerService.mIsolatedOwners"); + + /** + * Tracks existing packages prior to receiving an OTA. Keys are package name. + * Only non-null during an OTA, and even then it is nulled again once systemReady(). + */ + private @Nullable ArraySet mExistingPackages = null; + + /** + * List of code paths that need to be released when the system becomes ready. + *

+ * NOTE: We have to delay releasing cblocks for no other reason than we cannot + * retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}. When + * we no longer need to read that setting, cblock release can occur in the + * constructor. + * + * @see Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL + * @see #systemReady() + */ + @Nullable List mReleaseOnSystemReady; + + /** + * Whether or not system app permissions should be promoted from install to runtime. + */ + boolean mPromoteSystemApps; + + private final TestUtilityService mTestUtilityService; + + @Watched + @GuardedBy("mLock") + final Settings mSettings; + + /** + * Map of package names to frozen counts that are currently "frozen", + * which means active surgery is being done on the code/data for that + * package. The platform will refuse to launch frozen packages to avoid + * race conditions. + * + * @see PackageFreezer + */ + @GuardedBy("mLock") + final WatchedArrayMap mFrozenPackages = new WatchedArrayMap<>(); + private final SnapshotCache> mFrozenPackagesSnapshot = + new SnapshotCache.Auto(mFrozenPackages, mFrozenPackages, + "PackageManagerService.mFrozenPackages"); + + final ProtectedPackages mProtectedPackages; + + private boolean mFirstBoot; + + final boolean mIsEngBuild; + private final boolean mIsUserDebugBuild; + private final String mIncrementalVersion; + + PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy; + + private final ArrayMap mAvailableFeatures; + + @Watched + final InstantAppRegistry mInstantAppRegistry; + + @NonNull + final ChangedPackagesTracker mChangedPackagesTracker; + + @NonNull + private final PackageObserverHelper mPackageObserverHelper = new PackageObserverHelper(); + + @NonNull + private final PackageMonitorCallbackHelper mPackageMonitorCallbackHelper; + + private final ModuleInfoProvider mModuleInfoProvider; + + final ApexManager mApexManager; + + final PackageManagerServiceInjector mInjector; + + /** + * The list of all system partitions that may contain packages in ascending order of + * specificity (the more generic, the earlier in the list a partition appears). + */ + @VisibleForTesting(visibility = Visibility.PACKAGE) + public static final List SYSTEM_PARTITIONS = Collections.unmodifiableList( + PackagePartitions.getOrderedPartitions(ScanPartition::new)); + + private @NonNull final OverlayConfig mOverlayConfig; + + // Cached parsed flag value. Invalidated on each flag change. + PerUidReadTimeouts[] mPerUidReadTimeoutsCache; + + private static final PerUidReadTimeouts[] EMPTY_PER_UID_READ_TIMEOUTS_ARRAY = {}; + + private static class DefaultSystemWrapper implements + PackageManagerServiceInjector.SystemWrapper { + + @Override + public void disablePackageCaches() { + // disable all package caches that shouldn't apply within system server + PackageManager.disableApplicationInfoCache(); + PackageManager.disablePackageInfoCache(); + ApplicationPackageManager.invalidateGetPackagesForUidCache(); + ApplicationPackageManager.disableGetPackagesForUidCache(); + ApplicationPackageManager.invalidateHasSystemFeatureCache(); + PackageManager.corkPackageInfoCache(); + } + + @Override + public void enablePackageCaches() { + PackageManager.uncorkPackageInfoCache(); + } + } + + @Watched + final AppsFilterImpl mAppsFilter; + + final PackageParser2.Callback mPackageParserCallback; + + // Currently known shared libraries. + @Watched + private final SharedLibrariesImpl mSharedLibraries; + + // Mapping from instrumentation class names to info about them. + @Watched + private final WatchedArrayMap mInstrumentation = + new WatchedArrayMap<>(); + private final SnapshotCache> + mInstrumentationSnapshot = + new SnapshotCache.Auto<>(mInstrumentation, mInstrumentation, + "PackageManagerService.mInstrumentation"); + + // Packages whose data we have transfered into another package, thus + // should no longer exist. + final ArraySet mTransferredPackages = new ArraySet<>(); + + // Broadcast actions that are only available to the system. + @GuardedBy("mProtectedBroadcasts") + final ArraySet mProtectedBroadcasts = new ArraySet<>(); + + /** + * List of packages waiting for verification. + * Handler thread only! + */ + final SparseArray mPendingVerification = new SparseArray<>(); + + /** + * List of packages waiting for rollback to be enabled. + * Handler thread only! + */ + final SparseArray mPendingEnableRollback = new SparseArray<>(); + + final PackageInstallerService mInstallerService; + final ArtManagerService mArtManagerService; + + // TODO(b/260124949): Remove these. + final PackageDexOptimizer mPackageDexOptimizer; + @Nullable + final BackgroundDexOptService mBackgroundDexOptService; // null when ART Service is in use. + // DexManager handles the usage of dex files (e.g. secondary files, whether or not a package + // is used by other apps). + private final DexManager mDexManager; + private final DynamicCodeLogger mDynamicCodeLogger; + + private final AtomicInteger mNextMoveId = new AtomicInteger(); + final MovePackageHelper.MoveCallbacks mMoveCallbacks; + + /** + * Token for keys in mPendingVerification. + * Handler thread only! + */ + int mPendingVerificationToken = 0; + + /** + * Token for keys in mPendingEnableRollback. + * Handler thread only! + */ + int mPendingEnableRollbackToken = 0; + + @Watched(manual = true) + private volatile boolean mSystemReady; + @Watched(manual = true) + private volatile boolean mSafeMode; + @Watched + private final WatchedSparseBooleanArray mWebInstantAppsDisabled = + new WatchedSparseBooleanArray(); + + @Watched(manual = true) + private ApplicationInfo mAndroidApplication; + @Watched(manual = true) + private final ActivityInfo mResolveActivity = new ActivityInfo(); + private final ResolveInfo mResolveInfo = new ResolveInfo(); + @Watched(manual = true) + ComponentName mResolveComponentName; + private AndroidPackage mPlatformPackage; + ComponentName mCustomResolverComponentName; + + // Recorded overlay paths configuration for the Android app info. + private String[] mPlatformPackageOverlayPaths = null; + private String[] mPlatformPackageOverlayResourceDirs = null; + // And the same paths for the replaced resolver activity package + private String[] mReplacedResolverPackageOverlayPaths = null; + private String[] mReplacedResolverPackageOverlayResourceDirs = null; + + private boolean mResolverReplaced = false; + + @NonNull + final DomainVerificationManagerInternal mDomainVerificationManager; + + /** The service connection to the ephemeral resolver */ + final InstantAppResolverConnection mInstantAppResolverConnection; + /** Component used to show resolver settings for Instant Apps */ + final ComponentName mInstantAppResolverSettingsComponent; + + /** Activity used to install instant applications */ + @Watched(manual = true) + ActivityInfo mInstantAppInstallerActivity; + @Watched(manual = true) + private final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo(); + + private final Map + mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>()); + + private final Map + mPendingKillInstallObservers = Collections.synchronizedMap(new HashMap<>()); + + // Internal interface for permission manager + final PermissionManagerServiceInternal mPermissionManager; + + @Watched + final ComponentResolver mComponentResolver; + + // Set of packages names to keep cached, even if they are uninstalled for all users + @GuardedBy("mKeepUninstalledPackages") + @NonNull + private final ArraySet mKeepUninstalledPackages = new ArraySet<>(); + + // Cached reference to IDevicePolicyManager. + private IDevicePolicyManager mDevicePolicyManager = null; + + private File mCacheDir; + + private Future mPrepareAppDataFuture; + + final IncrementalManager mIncrementalManager; + + private final DefaultAppProvider mDefaultAppProvider; + + private final LegacyPermissionManagerInternal mLegacyPermissionManager; + + private final PackageProperty mPackageProperty = new PackageProperty(); + + final PendingPackageBroadcasts mPendingBroadcasts; + + static final int SEND_PENDING_BROADCAST = 1; + // public static final int UNUSED = 5; + static final int POST_INSTALL = 9; + static final int WRITE_SETTINGS = 13; + static final int WRITE_DIRTY_PACKAGE_RESTRICTIONS = 14; + static final int PACKAGE_VERIFIED = 15; + static final int CHECK_PENDING_VERIFICATION = 16; + // public static final int UNUSED = 17; + // public static final int UNUSED = 18; + static final int WRITE_PACKAGE_LIST = 19; + static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20; + static final int ENABLE_ROLLBACK_STATUS = 21; + static final int ENABLE_ROLLBACK_TIMEOUT = 22; + static final int DEFERRED_NO_KILL_POST_DELETE = 23; + static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24; + static final int INTEGRITY_VERIFICATION_COMPLETE = 25; + static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26; + static final int DOMAIN_VERIFICATION = 27; + static final int PRUNE_UNUSED_STATIC_SHARED_LIBRARIES = 28; + static final int DEFERRED_PENDING_KILL_INSTALL_OBSERVER = 29; + + static final int WRITE_USER_PACKAGE_RESTRICTIONS = 30; + + private static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000; + + private static final long DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED = + TimeUnit.DAYS.toMillis(1); + + private static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500; + private static final int DEFERRED_PENDING_KILL_INSTALL_OBSERVER_DELAY_MS = 1000; + + static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds + + private static final long BROADCAST_DELAY_DURING_STARTUP = 10 * 1000L; // 10 seconds (in millis) + private static final long BROADCAST_DELAY = 1 * 1000L; // 1 second (in millis) + + private static final long PRUNE_UNUSED_SHARED_LIBRARIES_DELAY = + TimeUnit.MINUTES.toMillis(3); // 3 minutes + + // When the service constructor finished plus a delay (used for broadcast delay computation) + private long mServiceStartWithDelay; + + static final long DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD = + TimeUnit.DAYS.toMillis(7); /* 7 days */ + + final UserManagerService mUserManager; + + final UserNeedsBadgingCache mUserNeedsBadging; + + // Stores a list of users whose package restrictions file needs to be updated + final ArraySet mDirtyUsers = new ArraySet<>(); + + final SparseArray mRunningInstalls = new SparseArray<>(); + int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows + + final @NonNull String[] mRequiredVerifierPackages; + final @NonNull String mRequiredInstallerPackage; + final @NonNull String mRequiredUninstallerPackage; + final @NonNull String mRequiredPermissionControllerPackage; + final @Nullable String mSetupWizardPackage; + final @Nullable String mStorageManagerPackage; + final @Nullable String mDefaultTextClassifierPackage; + final @Nullable String mSystemTextClassifierPackageName; + final @Nullable String mConfiguratorPackage; + final @Nullable String mAppPredictionServicePackage; + final @Nullable String mIncidentReportApproverPackage; + final @Nullable String mServicesExtensionPackageName; + final @Nullable String mSharedSystemSharedLibraryPackageName; + final @Nullable String mRetailDemoPackage; + final @Nullable String mOverlayConfigSignaturePackage; + final @Nullable String mRecentsPackage; + final @Nullable String mAmbientContextDetectionPackage; + final @Nullable String mWearableSensingPackage; + final @NonNull Set mInitialNonStoppedSystemPackages; + final boolean mShouldStopSystemPackagesByDefault; + private final @NonNull String mRequiredSdkSandboxPackage; + + @GuardedBy("mLock") + private final PackageUsage mPackageUsage = new PackageUsage(); + final CompilerStats mCompilerStats = new CompilerStats(); + + private final DomainVerificationConnection mDomainVerificationConnection; + + private final BroadcastHelper mBroadcastHelper; + private final RemovePackageHelper mRemovePackageHelper; + private final DeletePackageHelper mDeletePackageHelper; + private final InitAppsHelper mInitAppsHelper; + private final AppDataHelper mAppDataHelper; + @NonNull private final InstallPackageHelper mInstallPackageHelper; + private final PreferredActivityHelper mPreferredActivityHelper; + private final ResolveIntentHelper mResolveIntentHelper; + private final DexOptHelper mDexOptHelper; + private final SuspendPackageHelper mSuspendPackageHelper; + private final DistractingPackageHelper mDistractingPackageHelper; + private final StorageEventHelper mStorageEventHelper; + private final FreeStorageHelper mFreeStorageHelper; + + + private static final boolean ENABLE_BOOST = false; + + private static ThreadPriorityBooster sThreadPriorityBooster = new ThreadPriorityBooster( + Process.THREAD_PRIORITY_FOREGROUND, LockGuard.INDEX_PACKAGES); + + /** + * Boost the priority of the thread before holding PM traced lock. + * @hide + */ + public static void boostPriorityForPackageManagerTracedLockedSection() { + if (ENABLE_BOOST) { + sThreadPriorityBooster.boost(); + } + } + + + /** + * Restore the priority of the thread after release the PM traced lock. + * @hide + */ + public static void resetPriorityAfterPackageManagerTracedLockedSection() { + if (ENABLE_BOOST) { + sThreadPriorityBooster.reset(); + } + } + + /** + * Invalidate the package info cache, which includes updating the cached computer. + * @hide + */ + public static void invalidatePackageInfoCache() { + PackageManager.invalidatePackageInfoCache(); + onChanged(); + } + + private final Watcher mWatcher = new Watcher() { + @Override + public void onChange(@Nullable Watchable what) { + PackageManagerService.onChange(what); + } + }; + + /** + * A Snapshot is a subset of PackageManagerService state. A snapshot is either live + * or snapped. Live snapshots directly reference PackageManagerService attributes. + * Snapped snapshots contain deep copies of the attributes. + */ + class Snapshot { + public static final int LIVE = 1; + public static final int SNAPPED = 2; + + public final Settings settings; + public final WatchedSparseIntArray isolatedOwners; + public final WatchedArrayMap packages; + public final WatchedArrayMap instrumentation; + public final WatchedSparseBooleanArray webInstantAppsDisabled; + public final ComponentName resolveComponentName; + public final ActivityInfo resolveActivity; + public final ActivityInfo instantAppInstallerActivity; + public final ResolveInfo instantAppInstallerInfo; + public final InstantAppRegistry instantAppRegistry; + public final ApplicationInfo androidApplication; + public final String appPredictionServicePackage; + public final AppsFilterSnapshot appsFilter; + public final ComponentResolverApi componentResolver; + public final PackageManagerService service; + public final WatchedArrayMap frozenPackages; + public final SharedLibrariesRead sharedLibraries; + + Snapshot(int type) { + if (type == Snapshot.SNAPPED) { + settings = mSettings.snapshot(); + isolatedOwners = mIsolatedOwnersSnapshot.snapshot(); + packages = mPackagesSnapshot.snapshot(); + instrumentation = mInstrumentationSnapshot.snapshot(); + resolveComponentName = mResolveComponentName == null + ? null : mResolveComponentName.clone(); + resolveActivity = new ActivityInfo(mResolveActivity); + instantAppInstallerActivity = + (mInstantAppInstallerActivity == null) + ? null + : new ActivityInfo(mInstantAppInstallerActivity); + instantAppInstallerInfo = new ResolveInfo(mInstantAppInstallerInfo); + webInstantAppsDisabled = mWebInstantAppsDisabled.snapshot(); + instantAppRegistry = mInstantAppRegistry.snapshot(); + androidApplication = + (mAndroidApplication == null) + ? null + : new ApplicationInfo(mAndroidApplication); + appPredictionServicePackage = mAppPredictionServicePackage; + appsFilter = mAppsFilter.snapshot(); + componentResolver = mComponentResolver.snapshot(); + frozenPackages = mFrozenPackagesSnapshot.snapshot(); + sharedLibraries = mSharedLibraries.snapshot(); + } else if (type == Snapshot.LIVE) { + settings = mSettings; + isolatedOwners = mIsolatedOwners; + packages = mPackages; + instrumentation = mInstrumentation; + resolveComponentName = mResolveComponentName; + resolveActivity = mResolveActivity; + instantAppInstallerActivity = mInstantAppInstallerActivity; + instantAppInstallerInfo = mInstantAppInstallerInfo; + webInstantAppsDisabled = mWebInstantAppsDisabled; + instantAppRegistry = mInstantAppRegistry; + androidApplication = mAndroidApplication; + appPredictionServicePackage = mAppPredictionServicePackage; + appsFilter = mAppsFilter; + componentResolver = mComponentResolver; + frozenPackages = mFrozenPackages; + sharedLibraries = mSharedLibraries; + } else { + throw new IllegalArgumentException(); + } + service = PackageManagerService.this; + } + } + + // Compute read-only functions, based on live data. This attribute may be modified multiple + // times during the PackageManagerService constructor but it should not be modified thereafter. + private ComputerLocked mLiveComputer; + + private static final AtomicReference sSnapshot = new AtomicReference<>(); + + // If this differs from Computer#getVersion, the snapshot is invalid (stale). + private static final AtomicInteger sSnapshotPendingVersion = new AtomicInteger(1); + + /** + * This lock is used to make reads from {@link #sSnapshotPendingVersion} and + * {@link #sSnapshot} atomic inside {@code snapshotComputer()} when the versions mismatch. + * This lock is not meant to be used outside that method. This lock must be taken before + * {@link #mLock} is taken. + */ + private final Object mSnapshotLock = new Object(); + + /** + * The snapshot statistics. These are collected to track performance and to identify + * situations in which the snapshots are misbehaving. + */ + @Nullable + private final SnapshotStatistics mSnapshotStatistics; + + /** + * Return the cached computer. The method will rebuild the cached computer if necessary. + * The live computer will be returned if snapshots are disabled. + */ + @VisibleForTesting(visibility = Visibility.PACKAGE) + @NonNull + public Computer snapshotComputer() { + return snapshotComputer(true /*allowLiveComputer*/); + } + + /** + * This method should only ever be called from {@link PackageManagerLocal#snapshot()}. + * + * @param allowLiveComputer Whether to allow a live computer instance based on caller {@link + * #mLock} hold state. In certain cases, like for {@link + * PackageManagerLocal} API, it must be enforced that the caller gets + * a snapshot at time, and never the live variant. + * @deprecated Use {@link #snapshotComputer()} + */ + @Deprecated + @NonNull + public Computer snapshotComputer(boolean allowLiveComputer) { + var isHoldingPackageLock = Thread.holdsLock(mLock); + if (allowLiveComputer) { + if (isHoldingPackageLock) { + // If the current thread holds mLock then it may have modified state but not + // yet invalidated the snapshot. Always give the thread the live computer. + return mLiveComputer; + } + } + + var oldSnapshot = sSnapshot.get(); + var pendingVersion = sSnapshotPendingVersion.get(); + + if (oldSnapshot != null && oldSnapshot.getVersion() == pendingVersion) { + return oldSnapshot.use(); + } + + if (isHoldingPackageLock) { + // If the current thread holds mLock then it already has exclusive write access to the + // two snapshot fields, and we can just go ahead and rebuild the snapshot. + @SuppressWarnings("GuardedBy") + var newSnapshot = rebuildSnapshot(oldSnapshot, pendingVersion); + sSnapshot.set(newSnapshot); + return newSnapshot.use(); + } + + synchronized (mSnapshotLock) { + // Re-capture pending version in case a new invalidation occurred since last check + var rebuildSnapshot = sSnapshot.get(); + var rebuildVersion = sSnapshotPendingVersion.get(); + + // Check the versions again while the lock is held, in case the rebuild time caused + // multiple threads to wait on the snapshot lock. When the first thread finishes + // a rebuild, the snapshot is now valid and the other waiting threads can use it + // without kicking off their own rebuilds. + if (rebuildSnapshot != null && rebuildSnapshot.getVersion() == rebuildVersion) { + return rebuildSnapshot.use(); + } + + synchronized (mLock) { + // Fetch version one last time to ensure that the rebuilt snapshot matches + // the latest invalidation, which could have come in between entering the + // SnapshotLock and mLock sync blocks. + rebuildSnapshot = sSnapshot.get(); + rebuildVersion = sSnapshotPendingVersion.get(); + if (rebuildSnapshot != null && rebuildSnapshot.getVersion() == rebuildVersion) { + return rebuildSnapshot.use(); + } + + // Build the snapshot for this version + var newSnapshot = rebuildSnapshot(rebuildSnapshot, rebuildVersion); + sSnapshot.set(newSnapshot); + return newSnapshot.use(); + } + } + } + + @GuardedBy("mLock") + private Computer rebuildSnapshot(@Nullable Computer oldSnapshot, int newVersion) { + var now = SystemClock.currentTimeMicro(); + var hits = oldSnapshot == null ? -1 : oldSnapshot.getUsed(); + var args = new Snapshot(Snapshot.SNAPPED); + var newSnapshot = new ComputerEngine(args, newVersion); + var done = SystemClock.currentTimeMicro(); + + if (mSnapshotStatistics != null) { + mSnapshotStatistics.rebuild(now, done, hits, newSnapshot.getPackageStates().size()); + } + return newSnapshot; + } + + /** + * Create a live computer + */ + private ComputerLocked createLiveComputer() { + return new ComputerLocked(new Snapshot(Snapshot.LIVE)); + } + + /** + * This method is called when the state of PackageManagerService changes so as to + * invalidate the current snapshot. + * @param what The {@link Watchable} that reported the change + * @hide + */ + public static void onChange(@Nullable Watchable what) { + if (TRACE_SNAPSHOTS) { + Log.i(TAG, "snapshot: onChange(" + what + ")"); + } + sSnapshotPendingVersion.incrementAndGet(); + } + + /** + * Report a locally-detected change to observers. The parameter is left null, + * but it signifies that the change was detected by PackageManagerService itself. + */ + static void onChanged() { + onChange(null); + } + + void notifyInstallObserver(String packageName, boolean killApp) { + final InstallRequest installRequest = + killApp ? mPendingKillInstallObservers.remove(packageName) + : mNoKillInstallObservers.remove(packageName); + + if (installRequest != null) { + notifyInstallObserver(installRequest); + } + } + + void notifyInstallObserver(InstallRequest request) { + if (request.getObserver() != null) { + try { + Bundle extras = extrasForInstallResult(request); + request.getObserver().onPackageInstalled(request.getName(), + request.getReturnCode(), request.getReturnMsg(), extras); + } catch (RemoteException e) { + Slog.i(TAG, "Observer no longer exists."); + } + } + } + + void scheduleDeferredNoKillInstallObserver(InstallRequest request) { + String packageName = request.getPkg().getPackageName(); + mNoKillInstallObservers.put(packageName, request); + Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_INSTALL_OBSERVER, packageName); + mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS); + } + + void scheduleDeferredNoKillPostDelete(CleanUpArgs args) { + Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args); + // If the feature flag is on, retain the old files for a day. Otherwise, delete the old + // files after a few seconds. + long deleteDelayMillis = DEFERRED_NO_KILL_POST_DELETE_DELAY_MS; + if (Flags.improveInstallDontKill()) { + deleteDelayMillis = Binder.withCleanCallingIdentity(() -> { + return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE, + /* name= */ PROPERTY_DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED, + /* defaultValue= */ DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED); + }); + Slog.w(TAG, "Delaying the deletion of <" + args.getCodePath() + "> by " + + deleteDelayMillis + "ms or till the next reboot"); + } + mHandler.sendMessageDelayed(message, deleteDelayMillis); + } + + void schedulePruneUnusedStaticSharedLibraries(boolean delay) { + mHandler.removeMessages(PRUNE_UNUSED_STATIC_SHARED_LIBRARIES); + mHandler.sendEmptyMessageDelayed(PRUNE_UNUSED_STATIC_SHARED_LIBRARIES, + delay ? getPruneUnusedSharedLibrariesDelay() : 0); + } + + void scheduleDeferredPendingKillInstallObserver(InstallRequest request) { + final String packageName = request.getPkg().getPackageName(); + mPendingKillInstallObservers.put(packageName, request); + final Message message = mHandler.obtainMessage(DEFERRED_PENDING_KILL_INSTALL_OBSERVER, + packageName); + mHandler.sendMessageDelayed(message, DEFERRED_PENDING_KILL_INSTALL_OBSERVER_DELAY_MS); + } + + private static long getPruneUnusedSharedLibrariesDelay() { + return SystemProperties.getLong("debug.pm.prune_unused_shared_libraries_delay", + PRUNE_UNUSED_SHARED_LIBRARIES_DELAY); + } + + /** + * Requests checksums for the APK file. + * See {@link PackageInstaller.Session#requestChecksums} for details. + */ + public void requestFileChecksums(@NonNull File file, + @NonNull String installerPackageName, @Checksum.TypeMask int optional, + @Checksum.TypeMask int required, @Nullable List trustedInstallers, + @NonNull IOnChecksumsReadyListener onChecksumsReadyListener) + throws FileNotFoundException { + if (!file.exists()) { + throw new FileNotFoundException(file.getAbsolutePath()); + } + + final Executor executor = mInjector.getBackgroundExecutor(); + final Handler handler = mInjector.getBackgroundHandler(); + final Certificate[] trustedCerts = (trustedInstallers != null) ? decodeCertificates( + trustedInstallers) : null; + + final List> filesToChecksum = new ArrayList<>(1); + filesToChecksum.add(Pair.create(null, file)); + + executor.execute(() -> { + ApkChecksums.Injector injector = new ApkChecksums.Injector( + () -> mContext, + () -> handler, + mInjector::getIncrementalManager, + () -> mInjector.getLocalService(PackageManagerInternal.class)); + ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName, + trustedCerts, onChecksumsReadyListener, injector); + }); + } + + void requestChecksumsInternal(@NonNull Computer snapshot, @NonNull String packageName, + boolean includeSplits, @Checksum.TypeMask int optional, @Checksum.TypeMask int required, + @Nullable List trustedInstallers, + @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId, + @NonNull Executor executor, @NonNull Handler handler) { + Objects.requireNonNull(packageName); + Objects.requireNonNull(onChecksumsReadyListener); + Objects.requireNonNull(executor); + Objects.requireNonNull(handler); + + final ApplicationInfo applicationInfo = snapshot.getApplicationInfoInternal(packageName, 0, + Binder.getCallingUid(), userId); + if (applicationInfo == null) { + throw new ParcelableException(new PackageManager.NameNotFoundException(packageName)); + } + + final InstallSourceInfo installSourceInfo = snapshot.getInstallSourceInfo(packageName, + userId); + final String installerPackageName; + if (installSourceInfo != null) { + final String initiatingPackageName = installSourceInfo.getInitiatingPackageName(); + if (!isInstalledByAdb(initiatingPackageName)) { + installerPackageName = initiatingPackageName; + } else { + installerPackageName = installSourceInfo.getInstallingPackageName(); + } + } else { + installerPackageName = null; + } + + List> filesToChecksum = new ArrayList<>(); + + // Adding base split. + filesToChecksum.add(Pair.create(null, new File(applicationInfo.sourceDir))); + + // Adding other splits. + if (includeSplits && applicationInfo.splitNames != null) { + for (int i = 0, size = applicationInfo.splitNames.length; i < size; ++i) { + filesToChecksum.add(Pair.create(applicationInfo.splitNames[i], + new File(applicationInfo.splitSourceDirs[i]))); + } + } + + final Certificate[] trustedCerts = (trustedInstallers != null) ? decodeCertificates( + trustedInstallers) : null; + + executor.execute(() -> { + ApkChecksums.Injector injector = new ApkChecksums.Injector( + () -> mContext, + () -> handler, + mInjector::getIncrementalManager, + () -> mInjector.getLocalService(PackageManagerInternal.class)); + ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName, + trustedCerts, onChecksumsReadyListener, injector); + }); + } + + private static @NonNull Certificate[] decodeCertificates(@NonNull List certs) { + try { + final CertificateFactory cf = CertificateFactory.getInstance("X.509"); + final Certificate[] result = new Certificate[certs.size()]; + for (int i = 0, size = certs.size(); i < size; ++i) { + final InputStream is = new ByteArrayInputStream((byte[]) certs.get(i)); + final X509Certificate cert = (X509Certificate) cf.generateCertificate(is); + result[i] = cert; + } + return result; + } catch (CertificateException e) { + throw ExceptionUtils.propagate(e); + } + } + + private static Bundle extrasForInstallResult(InstallRequest request) { + Bundle extras = null; + switch (request.getReturnCode()) { + case PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION: { + extras = new Bundle(); + extras.putString(PackageManager.EXTRA_FAILURE_EXISTING_PERMISSION, + request.getOrigPermission()); + extras.putString(PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE, + request.getOrigPackage()); + break; + } + case PackageManager.INSTALL_SUCCEEDED: { + extras = new Bundle(); + extras.putBoolean(Intent.EXTRA_REPLACING, + request.getRemovedInfo() != null + && request.getRemovedInfo().mRemovedPackage != null); + break; + } + } + if (!request.getWarnings().isEmpty()) { + extras.putStringArrayList(PackageInstaller.EXTRA_WARNINGS, request.getWarnings()); + } + return extras; + } + + ArchivedPackageParcel getArchivedPackageInternal(@NonNull String packageName, int userId) { + Objects.requireNonNull(packageName); + int binderUid = Binder.getCallingUid(); + + Computer snapshot = snapshotComputer(); + snapshot.enforceCrossUserPermission(binderUid, userId, true, true, + "getArchivedPackage"); + + ArchivedPackageParcel archPkg = new ArchivedPackageParcel(); + archPkg.packageName = packageName; + + ArchiveState archiveState; + synchronized (mLock) { + PackageSetting ps = mSettings.getPackageLPr(packageName); + if (ps == null) { + return null; + } + var psi = ps.getUserStateOrDefault(userId); + archiveState = psi.getArchiveState(); + if (archiveState == null && !psi.isInstalled()) { + return null; + } + + archPkg.signingDetails = ps.getSigningDetails(); + + long longVersionCode = ps.getVersionCode(); + archPkg.versionCodeMajor = (int) (longVersionCode >> 32); + archPkg.versionCode = (int) longVersionCode; + + archPkg.targetSdkVersion = ps.getTargetSdkVersion(); + + // These get translated in flags important for user data management. + archPkg.defaultToDeviceProtectedStorage = String.valueOf( + ps.isDefaultToDeviceProtectedStorage()); + archPkg.requestLegacyExternalStorage = String.valueOf( + ps.isRequestLegacyExternalStorage()); + archPkg.userDataFragile = String.valueOf(ps.isUserDataFragile()); + } + + try { + if (archiveState != null) { + archPkg.archivedActivities = PackageArchiver.createArchivedActivities( + archiveState); + } else { + final int iconSize = mContext.getSystemService( + ActivityManager.class).getLauncherLargeIconSize(); + + var mainActivities = + mInstallerService.mPackageArchiver.getLauncherActivityInfos(packageName, + userId); + archPkg.archivedActivities = PackageArchiver.createArchivedActivities( + mainActivities, iconSize); + } + } catch (Exception e) { + throw new IllegalArgumentException("Package does not have a main activity", e); + } + + return archPkg; + } + + void markPackageAsArchivedIfNeeded(PackageSetting pkgSetting, + ArchivedPackageParcel archivePackage, int[] userIds) { + if (pkgSetting == null || archivePackage == null + || archivePackage.archivedActivities == null || userIds == null + || userIds.length == 0) { + return; + } + + // Initialize all necessary settings for archival installation. + pkgSetting + // No package. + .setPkg(null) + // Mark for later restore. + .setPendingRestore(true); + for (int userId : userIds) { + // Unmark "installed" for all users. + pkgSetting + .modifyUserState(userId) + .setInstalled(false); + } + + String responsibleInstallerPackage = PackageArchiver.getResponsibleInstallerPackage( + pkgSetting); + // TODO(b/278553670) Check if responsibleInstallerPackage supports unarchival. + if (TextUtils.isEmpty(responsibleInstallerPackage)) { + Slog.e(TAG, "Can't create archive state: responsible installer is empty"); + return; + } + for (int userId : userIds) { + var archiveState = mInstallerService.mPackageArchiver.createArchiveState( + archivePackage, userId, responsibleInstallerPackage); + if (archiveState != null) { + pkgSetting + .modifyUserState(userId) + .setArchiveState(archiveState); + } + } + } + + + void scheduleWriteSettings() { + // We normally invalidate when we write settings, but in cases where we delay and + // coalesce settings writes, this strategy would have us invalidate the cache too late. + // Invalidating on schedule addresses this problem. + invalidatePackageInfoCache(); + if (!mHandler.hasMessages(WRITE_SETTINGS)) { + mHandler.sendEmptyMessageDelayed(WRITE_SETTINGS, WRITE_SETTINGS_DELAY); + } + } + + void scheduleWritePackageList(int userId) { + invalidatePackageInfoCache(); + if (!mHandler.hasMessages(WRITE_PACKAGE_LIST)) { + Message msg = mHandler.obtainMessage(WRITE_PACKAGE_LIST); + msg.arg1 = userId; + mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY); + } + } + + void scheduleWritePackageRestrictions(UserHandle user) { + final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier(); + scheduleWritePackageRestrictions(userId); + } + + void scheduleWritePackageRestrictions(int userId) { + invalidatePackageInfoCache(); + if (userId == UserHandle.USER_ALL) { + synchronized (mDirtyUsers) { + for (int aUserId : mUserManager.getUserIds()) { + mDirtyUsers.add(aUserId); + } + } + } else { + if (!mUserManager.exists(userId)) { + return; + } + synchronized (mDirtyUsers) { + mDirtyUsers.add(userId); + } + } + if (!mBackgroundHandler.hasMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS)) { + mBackgroundHandler.sendMessageDelayed( + mBackgroundHandler.obtainMessage(WRITE_DIRTY_PACKAGE_RESTRICTIONS, this), + WRITE_SETTINGS_DELAY); + } + } + + void writePendingRestrictions() { + final Integer[] dirtyUsers; + synchronized (mLock) { + mBackgroundHandler.removeMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS); + synchronized (mDirtyUsers) { + if (mDirtyUsers.isEmpty()) { + return; + } + dirtyUsers = mDirtyUsers.toArray(Integer[]::new); + mDirtyUsers.clear(); + } + } + mSettings.writePackageRestrictions(dirtyUsers); + } + + private boolean tryUnderLock(boolean sync, long timeoutMs, Runnable runnable) { + try { + if (sync) { + mLock.lock(); + } else if (!mLock.tryLock(timeoutMs, TimeUnit.MILLISECONDS)) { + return false; + } + try { + runnable.run(); + return true; + } finally { + mLock.unlock(); + } + } catch (InterruptedException e) { + Slog.e(TAG, "Failed to obtain mLock", e); + } + return false; + } + + boolean tryWriteSettings(boolean sync) { + return tryUnderLock(sync, WRITE_LOCK_TIMEOUT_MS, () -> { + mHandler.removeMessages(WRITE_SETTINGS); + mBackgroundHandler.removeMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS); + writeSettingsLPrTEMP(sync); + synchronized (mDirtyUsers) { + mDirtyUsers.clear(); + } + }); + } + + boolean tryWritePackageList(int userId) { + return tryUnderLock(/*sync=*/false, WRITE_LOCK_TIMEOUT_MS, () -> { + mHandler.removeMessages(WRITE_PACKAGE_LIST); + mSettings.writePackageListLPr(userId); + }); + } + + private static final Handler.Callback BACKGROUND_HANDLER_CALLBACK = new Handler.Callback() { + @Override + public boolean handleMessage(@NonNull Message msg) { + switch (msg.what) { + case WRITE_DIRTY_PACKAGE_RESTRICTIONS: { + PackageManagerService pm = (PackageManagerService) msg.obj; + pm.writePendingRestrictions(); + return true; + } + case WRITE_USER_PACKAGE_RESTRICTIONS: { + final Runnable r = (Runnable) msg.obj; + r.run(); + return true; + } + } + return false; + } + }; + + /** Starts PackageManagerService. */ + public static PackageManagerService main(Context context, + Installer installer, @NonNull DomainVerificationService domainVerificationService, + boolean factoryTest) { + // Self-check for initial settings. + PackageManagerServiceCompilerMapping.checkProperties(); + final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", + Trace.TRACE_TAG_PACKAGE_MANAGER); + t.traceBegin("create package manager"); + final PackageManagerTracedLock lock = new PackageManagerTracedLock(); + final Object installLock = new Object(); + + HandlerThread backgroundThread = new ServiceThread("PackageManagerBg", + Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); + backgroundThread.start(); + Handler backgroundHandler = new Handler(backgroundThread.getLooper(), + BACKGROUND_HANDLER_CALLBACK); + + PackageManagerServiceInjector injector = new PackageManagerServiceInjector( + context, lock, installer, installLock, new PackageAbiHelperImpl(), + backgroundHandler, + SYSTEM_PARTITIONS, + (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mUserNeedsBadging), + (i, pm) -> PermissionManagerService.create(context, + i.getSystemConfig().getAvailableFeatures()), + (i, pm) -> new UserManagerService(context, pm, + new UserDataPreparer(installer, installLock, context), lock), + (i, pm) -> new Settings(Environment.getDataDirectory(), + RuntimePermissionsPersistence.createInstance(), + i.getPermissionManagerServiceInternal(), + domainVerificationService, backgroundHandler, + lock), + (i, pm) -> AppsFilterImpl.create(i, + i.getLocalService(PackageManagerInternal.class)), + (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"), + (i, pm) -> SystemConfig.getInstance(), + (i, pm) -> new PackageDexOptimizer(i.getInstaller(), i.getInstallLock(), + i.getContext(), "*dexopt*"), + (i, pm) -> new DexManager(i.getContext(), i.getPackageDexOptimizer(), + i.getInstaller(), i.getInstallLock(), i.getDynamicCodeLogger()), + (i, pm) -> new DynamicCodeLogger(i.getInstaller()), + (i, pm) -> new ArtManagerService(i.getContext(), i.getInstaller(), + i.getInstallLock()), + (i, pm) -> ApexManager.getInstance(), + (i, pm) -> (IncrementalManager) + i.getContext().getSystemService(Context.INCREMENTAL_SERVICE), + (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class), + () -> LocalServices.getService(UserManagerInternal.class)), + (i, pm) -> new DisplayMetrics(), + (i, pm) -> new PackageParser2(pm.mSeparateProcesses, i.getDisplayMetrics(), + new PackageCacher(pm.mCacheDir, pm.mPackageParserCallback), + pm.mPackageParserCallback) /* scanningCachingPackageParserProducer */, + (i, pm) -> new PackageParser2(pm.mSeparateProcesses, i.getDisplayMetrics(), null, + pm.mPackageParserCallback) /* scanningPackageParserProducer */, + (i, pm) -> new PackageParser2(pm.mSeparateProcesses, i.getDisplayMetrics(), null, + pm.mPackageParserCallback) /* preparingPackageParserProducer */, + // Prepare a supplier of package parser for the staging manager to parse apex file + // during the staging installation. + (i, pm) -> new PackageInstallerService( + i.getContext(), pm, i::getScanningPackageParser), + (i, pm, cn) -> new InstantAppResolverConnection( + i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE), + (i, pm) -> new ModuleInfoProvider(i.getContext()), + (i, pm) -> LegacyPermissionManagerService.create(i.getContext()), + (i, pm) -> domainVerificationService, + (i, pm) -> { + HandlerThread thread = new ServiceThread(TAG, + Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/); + thread.start(); + return new PackageHandler(thread.getLooper(), pm); + }, + new DefaultSystemWrapper(), + LocalServices::getService, + context::getSystemService, + (i, pm) -> { + if (useArtService()) { + return null; + } + try { + return new BackgroundDexOptService(i.getContext(), i.getDexManager(), pm); + } catch (LegacyDexoptDisabledException e) { + throw new RuntimeException(e); + } + }, + (i, pm) -> IBackupManager.Stub.asInterface(ServiceManager.getService( + Context.BACKUP_SERVICE)), + (i, pm) -> new SharedLibrariesImpl(pm, i), + (i, pm) -> new CrossProfileIntentFilterHelper(i.getSettings(), + i.getUserManagerService(), i.getLock(), i.getUserManagerInternal(), + context), + (i, pm) -> new UpdateOwnershipHelper(), + (i, pm) -> new PackageMonitorCallbackHelper()); + + if (Build.VERSION.SDK_INT <= 0) { + Slog.w(TAG, "**** ro.build.version.sdk not set!"); + } + + PackageManagerService m = new PackageManagerService(injector, factoryTest, + PackagePartitions.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG, + Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL); + t.traceEnd(); // "create package manager" + + final CompatChange.ChangeListener selinuxChangeListener = packageName -> { + synchronized (m.mInstallLock) { + final Computer snapshot = m.snapshotComputer(); + final PackageStateInternal packageState = + snapshot.getPackageStateInternal(packageName); + if (packageState == null) { + Slog.e(TAG, "Failed to find package setting " + packageName); + return; + } + AndroidPackage pkg = packageState.getPkg(); + SharedUserApi sharedUser = snapshot.getSharedUser( + packageState.getSharedUserAppId()); + String oldSeInfo = packageState.getSeInfo(); + + if (pkg == null) { + Slog.e(TAG, "Failed to find package " + packageName); + return; + } + final String newSeInfo = SELinuxMMAC.getSeInfo(packageState, pkg, sharedUser, + m.mInjector.getCompatibility()); + + if (!newSeInfo.equals(oldSeInfo)) { + Slog.i(TAG, "Updating seInfo for package " + packageName + " from: " + + oldSeInfo + " to: " + newSeInfo); + m.commitPackageStateMutation(null, packageName, + state -> state.setOverrideSeInfo(newSeInfo)); + m.mAppDataHelper.prepareAppDataAfterInstallLIF(pkg); + } + } + }; + + injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_LATEST_CHANGES, + selinuxChangeListener); + injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_R_CHANGES, + selinuxChangeListener); + + m.installAllowlistedSystemPackages(); + IPackageManagerImpl iPackageManager = m.new IPackageManagerImpl(); + ServiceManager.addService("package", iPackageManager); + final PackageManagerNative pmn = new PackageManagerNative(m); + ServiceManager.addService("package_native", pmn); + return m; + } + + /** Install/uninstall system packages for all users based on their user-type, as applicable. */ + private void installAllowlistedSystemPackages() { + if (mUserManager.installWhitelistedSystemPackages(isFirstBoot(), isDeviceUpgrading(), + mExistingPackages)) { + scheduleWritePackageRestrictions(UserHandle.USER_ALL); + scheduleWriteSettings(); + } + } + + // Link watchables to the class + @SuppressWarnings("GuardedBy") + private void registerObservers(boolean verify) { + // Null check to handle nullable test parameters + if (mPackages != null) { + mPackages.registerObserver(mWatcher); + } + if (mSharedLibraries != null) { + mSharedLibraries.registerObserver(mWatcher); + } + if (mInstrumentation != null) { + mInstrumentation.registerObserver(mWatcher); + } + if (mWebInstantAppsDisabled != null) { + mWebInstantAppsDisabled.registerObserver(mWatcher); + } + if (mAppsFilter != null) { + mAppsFilter.registerObserver(mWatcher); + } + if (mInstantAppRegistry != null) { + mInstantAppRegistry.registerObserver(mWatcher); + } + if (mSettings != null) { + mSettings.registerObserver(mWatcher); + } + if (mIsolatedOwners != null) { + mIsolatedOwners.registerObserver(mWatcher); + } + if (mComponentResolver != null) { + mComponentResolver.registerObserver(mWatcher); + } + if (mFrozenPackages != null) { + mFrozenPackages.registerObserver(mWatcher); + } + if (verify) { + // If neither "build" attribute is true then this may be a mockito test, + // and verification can fail as a false positive. + Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild)); + } + } + + /** + * An extremely minimal constructor designed to start up a PackageManagerService instance for + * testing. + * + * It is assumed that all methods under test will mock the internal fields and thus + * none of the initialization is needed. + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public PackageManagerService(@NonNull PackageManagerServiceInjector injector, + @NonNull PackageManagerServiceTestParams testParams) { + mInjector = injector; + mInjector.bootstrap(this); + mAppsFilter = injector.getAppsFilter(); + mComponentResolver = injector.getComponentResolver(); + mContext = injector.getContext(); + mInstaller = injector.getInstaller(); + mInstallLock = injector.getInstallLock(); + mLock = injector.getLock(); + mPackageStateWriteLock = mLock; + mPermissionManager = injector.getPermissionManagerServiceInternal(); + mSettings = injector.getSettings(); + mUserManager = injector.getUserManagerService(); + mUserNeedsBadging = new UserNeedsBadgingCache(mUserManager); + mDomainVerificationManager = injector.getDomainVerificationManagerInternal(); + mHandler = injector.getHandler(); + mBackgroundHandler = injector.getBackgroundHandler(); + mSharedLibraries = injector.getSharedLibrariesImpl(); + + mApexManager = testParams.apexManager; + mArtManagerService = testParams.artManagerService; + mAvailableFeatures = testParams.availableFeatures; + mBackgroundDexOptService = testParams.backgroundDexOptService; + mDefParseFlags = testParams.defParseFlags; + mDefaultAppProvider = testParams.defaultAppProvider; + mLegacyPermissionManager = testParams.legacyPermissionManagerInternal; + mDexManager = testParams.dexManager; + mDynamicCodeLogger = testParams.dynamicCodeLogger; + mFactoryTest = testParams.factoryTest; + mIncrementalManager = testParams.incrementalManager; + mInstallerService = testParams.installerService; + mInstantAppRegistry = testParams.instantAppRegistry; + mChangedPackagesTracker = testParams.changedPackagesTracker; + mInstantAppResolverConnection = testParams.instantAppResolverConnection; + mInstantAppResolverSettingsComponent = testParams.instantAppResolverSettingsComponent; + mIsPreNMR1Upgrade = testParams.isPreNmr1Upgrade; + mIsPreQUpgrade = testParams.isPreQupgrade; + mPriorSdkVersion = testParams.priorSdkVersion; + mIsUpgrade = testParams.isUpgrade; + mMetrics = testParams.Metrics; + mModuleInfoProvider = testParams.moduleInfoProvider; + mMoveCallbacks = testParams.moveCallbacks; + mOverlayConfig = testParams.overlayConfig; + mPackageDexOptimizer = testParams.packageDexOptimizer; + mPackageParserCallback = testParams.packageParserCallback; + mPendingBroadcasts = testParams.pendingPackageBroadcasts; + mTestUtilityService = testParams.testUtilityService; + mProcessLoggingHandler = testParams.processLoggingHandler; + mProtectedPackages = testParams.protectedPackages; + mSeparateProcesses = testParams.separateProcesses; + mRequiredVerifierPackages = testParams.requiredVerifierPackages; + mRequiredInstallerPackage = testParams.requiredInstallerPackage; + mRequiredUninstallerPackage = testParams.requiredUninstallerPackage; + mRequiredPermissionControllerPackage = testParams.requiredPermissionControllerPackage; + mSetupWizardPackage = testParams.setupWizardPackage; + mStorageManagerPackage = testParams.storageManagerPackage; + mDefaultTextClassifierPackage = testParams.defaultTextClassifierPackage; + mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage; + mRetailDemoPackage = testParams.retailDemoPackage; + mRecentsPackage = testParams.recentsPackage; + mAmbientContextDetectionPackage = testParams.ambientContextDetectionPackage; + mWearableSensingPackage = testParams.wearableSensingPackage; + mConfiguratorPackage = testParams.configuratorPackage; + mAppPredictionServicePackage = testParams.appPredictionServicePackage; + mIncidentReportApproverPackage = testParams.incidentReportApproverPackage; + mServicesExtensionPackageName = testParams.servicesExtensionPackageName; + mSharedSystemSharedLibraryPackageName = testParams.sharedSystemSharedLibraryPackageName; + mOverlayConfigSignaturePackage = testParams.overlayConfigSignaturePackage; + mResolveComponentName = testParams.resolveComponentName; + mRequiredSdkSandboxPackage = testParams.requiredSdkSandboxPackage; + mInitialNonStoppedSystemPackages = testParams.initialNonStoppedSystemPackages; + mShouldStopSystemPackagesByDefault = testParams.shouldStopSystemPackagesByDefault; + + mLiveComputer = createLiveComputer(); + mSnapshotStatistics = null; + + mPackages.putAll(testParams.packages); + mFreeStorageHelper = testParams.freeStorageHelper; + mSdkVersion = testParams.sdkVersion; + mAppInstallDir = testParams.appInstallDir; + mIsEngBuild = testParams.isEngBuild; + mIsUserDebugBuild = testParams.isUserDebugBuild; + mIncrementalVersion = testParams.incrementalVersion; + mDomainVerificationConnection = new DomainVerificationConnection(this); + + mBroadcastHelper = testParams.broadcastHelper; + mAppDataHelper = testParams.appDataHelper; + mInstallPackageHelper = testParams.installPackageHelper; + mRemovePackageHelper = testParams.removePackageHelper; + mInitAppsHelper = testParams.initAndSystemPackageHelper; + mDeletePackageHelper = testParams.deletePackageHelper; + mPreferredActivityHelper = testParams.preferredActivityHelper; + mResolveIntentHelper = testParams.resolveIntentHelper; + mDexOptHelper = testParams.dexOptHelper; + mSuspendPackageHelper = testParams.suspendPackageHelper; + mDistractingPackageHelper = testParams.distractingPackageHelper; + + mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper); + + mStorageEventHelper = testParams.storageEventHelper; + mPackageMonitorCallbackHelper = testParams.packageMonitorCallbackHelper; + + registerObservers(false); + invalidatePackageInfoCache(); + } + + public PackageManagerService(PackageManagerServiceInjector injector, boolean factoryTest, + final String partitionsFingerprint, final boolean isEngBuild, + final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) { + mIsEngBuild = isEngBuild; + mIsUserDebugBuild = isUserDebugBuild; + mSdkVersion = sdkVersion; + mIncrementalVersion = incrementalVersion; + mInjector = injector; + mInjector.getSystemWrapper().disablePackageCaches(); + + final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", + Trace.TRACE_TAG_PACKAGE_MANAGER); + mPendingBroadcasts = new PendingPackageBroadcasts(); + + mInjector.bootstrap(this); + mLock = injector.getLock(); + mPackageStateWriteLock = mLock; + mInstallLock = injector.getInstallLock(); + LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES); + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, + SystemClock.uptimeMillis()); + + mContext = injector.getContext(); + mFactoryTest = factoryTest; + mMetrics = injector.getDisplayMetrics(); + mInstaller = injector.getInstaller(); + mFreeStorageHelper = new FreeStorageHelper(this); + + // Create sub-components that provide services / data. Order here is important. + t.traceBegin("createSubComponents"); + + // Expose private service for system components to use. + LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl()); + LocalManagerRegistry.addManager(PackageManagerLocal.class, + new PackageManagerLocalImpl(this)); + LocalServices.addService(TestUtilityService.class, this); + mTestUtilityService = LocalServices.getService(TestUtilityService.class); + mUserManager = injector.getUserManagerService(); + mUserNeedsBadging = new UserNeedsBadgingCache(mUserManager); + mComponentResolver = injector.getComponentResolver(); + mPermissionManager = injector.getPermissionManagerServiceInternal(); + mSettings = injector.getSettings(); + mIncrementalManager = mInjector.getIncrementalManager(); + mDefaultAppProvider = mInjector.getDefaultAppProvider(); + mLegacyPermissionManager = mInjector.getLegacyPermissionManagerInternal(); + PlatformCompat platformCompat = mInjector.getCompatibility(); + mPackageParserCallback = new PackageParser2.Callback() { + @Override + public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) { + return platformCompat.isChangeEnabled(changeId, appInfo); + } + + @Override + public boolean hasFeature(String feature) { + return PackageManagerService.this.hasSystemFeature(feature, 0); + } + + @Override + public Set getHiddenApiWhitelistedApps() { + return SystemConfig.getInstance().getHiddenApiWhitelistedApps(); + } + + @Override + public Set getInstallConstraintsAllowlist() { + return SystemConfig.getInstance().getInstallConstraintsAllowlist(); + } + }; + + // CHECKSTYLE:ON IndentationCheck + t.traceEnd(); + + t.traceBegin("addSharedUsers"); + mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, + ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, + ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + mSettings.addSharedUserLPw("android.uid.log", LOG_UID, + ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, + ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, + ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID, + ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + mSettings.addSharedUserLPw("android.uid.se", SE_UID, + ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID, + ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID, + ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + t.traceEnd(); + + String separateProcesses = SystemProperties.get("debug.separate_processes"); + + if (separateProcesses != null && separateProcesses.length() > 0) { + if ("*".equals(separateProcesses)) { + mDefParseFlags = ParsingPackageUtils.PARSE_IGNORE_PROCESSES; + mSeparateProcesses = null; + Slog.w(TAG, "Running with debug.separate_processes: * (ALL)"); + } else { + mDefParseFlags = 0; + mSeparateProcesses = separateProcesses.split(","); + Slog.w(TAG, "Running with debug.separate_processes: " + + separateProcesses); + } + } else { + mDefParseFlags = 0; + mSeparateProcesses = null; + } + + mPackageDexOptimizer = injector.getPackageDexOptimizer(); + mDexManager = injector.getDexManager(); + mDynamicCodeLogger = injector.getDynamicCodeLogger(); + mBackgroundDexOptService = injector.getBackgroundDexOptService(); + mArtManagerService = injector.getArtManagerService(); + mMoveCallbacks = new MovePackageHelper.MoveCallbacks(FgThread.get().getLooper()); + mSharedLibraries = mInjector.getSharedLibrariesImpl(); + mBackgroundHandler = injector.getBackgroundHandler(); + + mContext.getSystemService(DisplayManager.class) + .getDisplay(Display.DEFAULT_DISPLAY).getMetrics(mMetrics); + + t.traceBegin("get system config"); + SystemConfig systemConfig = injector.getSystemConfig(); + mAvailableFeatures = systemConfig.getAvailableFeatures(); + t.traceEnd(); + + mProtectedPackages = new ProtectedPackages(mContext); + + mApexManager = injector.getApexManager(); + mAppsFilter = mInjector.getAppsFilter(); + + mChangedPackagesTracker = new ChangedPackagesTracker(); + + mAppInstallDir = new File(Environment.getDataDirectory(), "app"); + + mDomainVerificationConnection = new DomainVerificationConnection(this); + mDomainVerificationManager = injector.getDomainVerificationManagerInternal(); + mDomainVerificationManager.setConnection(mDomainVerificationConnection); + + mBroadcastHelper = new BroadcastHelper(mInjector); + mPackageMonitorCallbackHelper = injector.getPackageMonitorCallbackHelper(); + mAppDataHelper = new AppDataHelper(this); + mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper, mBroadcastHelper); + mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper, + mBroadcastHelper); + mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper, mRemovePackageHelper, + mDeletePackageHelper, mBroadcastHelper); + + mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager, + mInjector.getUserManagerInternal(), mDeletePackageHelper); + + mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper); + mPreferredActivityHelper = new PreferredActivityHelper(this, mBroadcastHelper); + mResolveIntentHelper = new ResolveIntentHelper(mContext, mPreferredActivityHelper, + injector.getCompatibility(), mUserManager, mDomainVerificationManager, + mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity, + injector.getBackgroundHandler()); + mDexOptHelper = new DexOptHelper(this); + mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mBroadcastHelper, + mProtectedPackages); + mDistractingPackageHelper = new DistractingPackageHelper(this, mBroadcastHelper, + mSuspendPackageHelper); + mStorageEventHelper = new StorageEventHelper(this, mDeletePackageHelper, + mRemovePackageHelper); + + synchronized (mLock) { + // Create the computer as soon as the state objects have been installed. The + // cached computer is the same as the live computer until the end of the + // constructor, at which time the invalidation method updates it. + mSnapshotStatistics = new SnapshotStatistics(); + sSnapshotPendingVersion.incrementAndGet(); + mLiveComputer = createLiveComputer(); + registerObservers(true); + } + + Computer computer = mLiveComputer; + // CHECKSTYLE:OFF IndentationCheck + synchronized (mInstallLock) { + // writer + synchronized (mLock) { + mHandler = injector.getHandler(); + mProcessLoggingHandler = new ProcessLoggingHandler(); + Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); + + ArrayMap libConfig + = systemConfig.getSharedLibraries(); + final int builtInLibCount = libConfig.size(); + for (int i = 0; i < builtInLibCount; i++) { + mSharedLibraries.addBuiltInSharedLibraryLPw(libConfig.valueAt(i)); + } + + // Now that we have added all the libraries, iterate again to add dependency + // information IFF their dependencies are added. + long undefinedVersion = SharedLibraryInfo.VERSION_UNDEFINED; + for (int i = 0; i < builtInLibCount; i++) { + String name = libConfig.keyAt(i); + SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i); + final int dependencyCount = entry.dependencies.length; + for (int j = 0; j < dependencyCount; j++) { + final SharedLibraryInfo dependency = + computer.getSharedLibraryInfo(entry.dependencies[j], undefinedVersion); + if (dependency != null) { + computer.getSharedLibraryInfo(name, undefinedVersion) + .addDependency(dependency); + } + } + } + + SELinuxMMAC.readInstallPolicy(); + + t.traceBegin("loadFallbacks"); + FallbackCategoryProvider.loadFallbacks(); + t.traceEnd(); + + t.traceBegin("read user settings"); + mFirstBoot = !mSettings.readLPw(computer, + mInjector.getUserManagerInternal().getUsers( + /* excludePartial= */ true, + /* excludeDying= */ false, + /* excludePreCreated= */ false)); + t.traceEnd(); + + if (mFirstBoot) { + t.traceBegin("setFirstBoot: "); + try { + mInstaller.setFirstBoot(); + } catch (InstallerException e) { + Slog.w(TAG, "Could not set First Boot: ", e); + } + t.traceEnd(); + } + + mPermissionManager.readLegacyPermissionsTEMP(mSettings.mPermissions); + mPermissionManager.readLegacyPermissionStateTEMP(); + + if (mFirstBoot) { + DexOptHelper.requestCopyPreoptedFiles(); + } + + String customResolverActivityName = Resources.getSystem().getString( + R.string.config_customResolverActivity); + if (!TextUtils.isEmpty(customResolverActivityName)) { + mCustomResolverComponentName = ComponentName.unflattenFromString( + customResolverActivityName); + } + + long startTime = SystemClock.uptimeMillis(); + + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, + startTime); + + final String bootClassPath = System.getenv("BOOTCLASSPATH"); + final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH"); + + if (bootClassPath == null) { + Slog.w(TAG, "No BOOTCLASSPATH found!"); + } + + if (systemServerClassPath == null) { + Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!"); + } + + final VersionInfo ver = mSettings.getInternalVersion(); + mIsUpgrade = + !partitionsFingerprint.equals(ver.fingerprint); + if (mIsUpgrade) { + PackageManagerServiceUtils.logCriticalInfo(Log.INFO, + "Upgrading from " + ver.fingerprint + " (" + ver.buildFingerprint + ") to " + + PackagePartitions.FINGERPRINT + " (" + Build.FINGERPRINT + ")"); + } + mPriorSdkVersion = mIsUpgrade ? ver.sdkVersion : -1; + mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper, + mInjector.getSystemPartitions()); + + // when upgrading from pre-M, promote system app permissions from install to runtime + mPromoteSystemApps = + mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1; + + mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1; + mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q; + + final WatchedArrayMap packageSettings = + mSettings.getPackagesLocked(); + + if (isDeviceUpgrading()) { + // Save the names of pre-existing packages prior to scanning, so we can determine + // which system packages are completely new due to an upgrade. + mExistingPackages = new ArraySet<>(packageSettings.size()); + for (int i = 0; i < packageSettings.size(); i++) { + mExistingPackages.add(packageSettings.valueAt(i).getPackageName()); + } + + // Triggering {@link com.android.server.pm.crossprofile. + // CrossProfileIntentFilterHelper.updateDefaultCrossProfileIntentFilter} to update + // {@link CrossProfileIntentFilter}s between eligible users and their parent + t.traceBegin("cross profile intent filter update"); + mInjector.getCrossProfileIntentFilterHelper() + .updateDefaultCrossProfileIntentFilter(); + t.traceEnd(); + } + + mCacheDir = PackageManagerServiceUtils.preparePackageParserCache( + mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion); + + mInitialNonStoppedSystemPackages = mInjector.getSystemConfig() + .getInitialNonStoppedSystemPackages(); + mShouldStopSystemPackagesByDefault = mContext.getResources() + .getBoolean(R.bool.config_stopSystemPackagesByDefault); + + final int[] userIds = mUserManager.getUserIds(); + PackageParser2 packageParser = mInjector.getScanningCachingPackageParser(); + mOverlayConfig = mInitAppsHelper.initSystemApps(packageParser, packageSettings, userIds, + startTime); + mInitAppsHelper.initNonSystemApps(packageParser, userIds, startTime); + packageParser.close(); + + mRequiredVerifierPackages = getRequiredButNotReallyRequiredVerifiersLPr(computer); + mRequiredInstallerPackage = getRequiredInstallerLPr(computer); + mRequiredUninstallerPackage = getRequiredUninstallerLPr(computer); + + // PermissionController hosts default permission granting and role management, so it's a + // critical part of the core system. + mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr(computer); + + // Resolve the storage manager. + mStorageManagerPackage = getStorageManagerPackageName(computer); + + // Resolve protected action filters. Only the setup wizard is allowed to + // have a high priority filter for these actions. + mSetupWizardPackage = getSetupWizardPackageNameImpl(computer); + mComponentResolver.fixProtectedFilterPriorities(mSetupWizardPackage); + + mDefaultTextClassifierPackage = ensureSystemPackageName(computer, + mContext.getString(R.string.config_servicesExtensionPackage)); + mSystemTextClassifierPackageName = ensureSystemPackageName(computer, + mContext.getString(R.string.config_defaultTextClassifierPackage)); + mConfiguratorPackage = ensureSystemPackageName(computer, + mContext.getString(R.string.config_deviceConfiguratorPackageName)); + mAppPredictionServicePackage = ensureSystemPackageName(computer, + getPackageFromComponentString(R.string.config_defaultAppPredictionService)); + mIncidentReportApproverPackage = ensureSystemPackageName(computer, + mContext.getString(R.string.config_incidentReportApproverPackage)); + mRetailDemoPackage = getRetailDemoPackageName(); + mOverlayConfigSignaturePackage = ensureSystemPackageName(computer, + mInjector.getSystemConfig().getOverlayConfigSignaturePackage()); + mRecentsPackage = ensureSystemPackageName(computer, + getPackageFromComponentString(R.string.config_recentsComponentName)); + mAmbientContextDetectionPackage = ensureSystemPackageName(computer, + getPackageFromComponentString( + R.string.config_defaultAmbientContextDetectionService)); + mWearableSensingPackage = ensureSystemPackageName(computer, + getPackageFromComponentString( + R.string.config_defaultWearableSensingService)); + + // Now that we know all of the shared libraries, update all clients to have + // the correct library paths. + mSharedLibraries.updateAllSharedLibrariesLPw( + null, null, Collections.unmodifiableMap(mPackages)); + + for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) { + // NOTE: We ignore potential failures here during a system scan (like + // the rest of the commands above) because there's precious little we + // can do about it. A settings error is reported, though. + final List changedAbiCodePath = + ScanPackageUtils.applyAdjustedAbiToSharedUser(setting, + null /*scannedPackage*/, + mInjector.getAbiHelper().getAdjustedAbiForSharedUser( + setting.getPackageStates(), null /*scannedPackage*/)); + if (!useArtService() && // Skip for ART Service since it has its own dex file GC. + changedAbiCodePath != null && changedAbiCodePath.size() > 0) { + for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) { + final String codePathString = changedAbiCodePath.get(i); + try { + mInstaller.rmdex(codePathString, + getDexCodeInstructionSet(getPreferredInstructionSet())); + } catch (LegacyDexoptDisabledException e) { + throw new RuntimeException(e); + } catch (InstallerException ignored) { + } + } + } + // Adjust seInfo to ensure apps which share a sharedUserId are placed in the same + // SELinux domain. + setting.fixSeInfoLocked(); + setting.updateProcesses(); + } + + // Now that we know all the packages we are keeping, + // read and update their last usage times. + mPackageUsage.read(packageSettings); + mCompilerStats.read(); + + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, + SystemClock.uptimeMillis()); + Slog.i(TAG, "Time to scan packages: " + + ((SystemClock.uptimeMillis() - startTime) / 1000f) + + " seconds"); + + // If the partitions fingerprint has changed since the last time we booted, + // we need to re-grant app permission to catch any new ones that + // appear. This is really a hack, and means that apps can in some + // cases get permissions that the user didn't initially explicitly + // allow... it would be nice to have some better way to handle + // this situation. + if (mIsUpgrade) { + Slog.i(TAG, "Partitions fingerprint changed from " + ver.fingerprint + " to " + + PackagePartitions.FINGERPRINT + + "; regranting permissions for internal storage"); + } + mPermissionManager.onStorageVolumeMounted( + StorageManager.UUID_PRIVATE_INTERNAL, mIsUpgrade); + ver.sdkVersion = mSdkVersion; + + // If this is the first boot or an update from pre-M, then we need to initialize the + // default preferred apps across all defined users. + if (mPromoteSystemApps || mFirstBoot) { + final List users = mInjector.getUserManagerInternal().getUsers(true); + for (int i = 0; i < users.size(); i++) { + mSettings.applyDefaultPreferredAppsLPw(users.get(i).id); + + } + } + + // If this is first boot after an OTA, 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) { + Slog.i(TAG, "Build fingerprint changed; clearing code caches"); + for (int i = 0; i < packageSettings.size(); i++) { + final PackageSetting ps = packageSettings.valueAt(i); + if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.getVolumeUuid())) { + // No apps are running this early, so no need to freeze + mAppDataHelper.clearAppDataLIF(ps.getPkg(), UserHandle.USER_ALL, + FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL + | Installer.FLAG_CLEAR_CODE_CACHE_ONLY + | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES); + } + } + ver.buildFingerprint = Build.FINGERPRINT; + ver.fingerprint = PackagePartitions.FINGERPRINT; + } + + // Defer the app data fixup until we are done with app data clearing above. + mPrepareAppDataFuture = mAppDataHelper.fixAppsDataOnBoot(); + + // Legacy existing (installed before Q) non-system apps to hide + // their icons in launcher. + if (mIsPreQUpgrade) { + Slog.i(TAG, "Allowlisting all existing apps to hide their icons"); + int size = packageSettings.size(); + for (int i = 0; i < size; i++) { + final PackageSetting ps = packageSettings.valueAt(i); + if ((ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0) { + continue; + } + ps.disableComponentLPw(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME, + UserHandle.USER_SYSTEM); + } + } + + // clear only after permissions and other defaults have been updated + mPromoteSystemApps = false; + + // All the changes are done during package scanning. + ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION; + + // can downgrade to reader + t.traceBegin("write settings"); + writeSettingsLPrTEMP(); + t.traceEnd(); + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, + SystemClock.uptimeMillis()); + + ComponentName intentFilterVerifierComponent = + getIntentFilterVerifierComponentNameLPr(computer); + ComponentName domainVerificationAgent = + getDomainVerificationAgentComponentNameLPr(computer); + + DomainVerificationProxy domainVerificationProxy = DomainVerificationProxy.makeProxy( + intentFilterVerifierComponent, domainVerificationAgent, mContext, + mDomainVerificationManager, mDomainVerificationManager.getCollector(), + mDomainVerificationConnection); + + mDomainVerificationManager.setProxy(domainVerificationProxy); + + mServicesExtensionPackageName = getRequiredServicesExtensionPackageLPr(computer); + mSharedSystemSharedLibraryPackageName = getRequiredSharedLibrary(computer, + PackageManager.SYSTEM_SHARED_LIBRARY_SHARED, + SharedLibraryInfo.VERSION_UNDEFINED); + + mSettings.setPermissionControllerVersion( + computer.getPackageInfo(mRequiredPermissionControllerPackage, 0, + UserHandle.USER_SYSTEM).getLongVersionCode()); + + // Resolve the sdk sandbox package + mRequiredSdkSandboxPackage = getRequiredSdkSandboxPackageName(computer); + + // Initialize InstantAppRegistry's Instant App list for all users. + forEachPackageState(computer, packageState -> { + var pkg = packageState.getAndroidPackage(); + if (pkg == null || packageState.isSystem()) { + return; + } + for (int userId : userIds) { + if (!packageState.getUserStateOrDefault(userId).isInstantApp() + || !packageState.getUserStateOrDefault(userId).isInstalled()) { + continue; + } + mInstantAppRegistry.addInstantApp(userId, packageState.getAppId()); + } + }); + + mInstallerService = mInjector.getPackageInstallerService(); + final ComponentName instantAppResolverComponent = getInstantAppResolver(computer); + if (instantAppResolverComponent != null) { + if (DEBUG_INSTANT) { + Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent); + } + mInstantAppResolverConnection = + mInjector.getInstantAppResolverConnection(instantAppResolverComponent); + mInstantAppResolverSettingsComponent = + getInstantAppResolverSettingsLPr(computer, + instantAppResolverComponent); + } 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> userPackages = new HashMap<>(); + for (int userId : userIds) { + userPackages.put(userId, computer.getInstalledPackages(/*flags*/ 0, userId) + .getList()); + } + mDexManager.load(userPackages); + mDynamicCodeLogger.load(userPackages); + if (mIsUpgrade) { + FrameworkStatsLog.write( + FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED, + BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME, + SystemClock.uptimeMillis() - startTime); + } + + // If this is first boot or first boot after OTA then set the file path to the app + // metadata files for preloaded packages. + if (mFirstBoot || isDeviceUpgrading()) { + ArrayMap paths = systemConfig.getAppMetadataFilePaths(); + for (Map.Entry entry : paths.entrySet()) { + String pkgName = entry.getKey(); + String path = entry.getValue(); + File file = new File(path); + if (!file.exists()) { + path = null; + } + PackageSetting disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(pkgName); + if (disabledPkgSetting == null) { + PackageSetting pkgSetting = mSettings.getPackageLPr(pkgName); + if (pkgSetting != null) { + pkgSetting.setAppMetadataFilePath(path); + if (Flags.aslInApkAppMetadataSource()) { + pkgSetting.setAppMetadataSource( + PackageManager.APP_METADATA_SOURCE_SYSTEM_IMAGE); + } + } else { + Slog.w(TAG, "Cannot set app metadata file for nonexistent package " + + pkgName); + } + } else { + disabledPkgSetting.setAppMetadataFilePath(path); + if (Flags.aslInApkAppMetadataSource()) { + disabledPkgSetting.setAppMetadataSource( + PackageManager.APP_METADATA_SOURCE_SYSTEM_IMAGE); + } + } + } + } + + // Rebuild the live computer since some attributes have been rebuilt. + mLiveComputer = createLiveComputer(); + + } // synchronized (mLock) + } // synchronized (mInstallLock) + // CHECKSTYLE:ON IndentationCheck + + mModuleInfoProvider = mInjector.getModuleInfoProvider(); + + mInjector.getSystemWrapper().enablePackageCaches(); + + // The initial scanning above does many calls into installd while + // holding the mPackages lock, but we're mostly interested in yelling + // once we have a booted system. + mInstaller.setWarnIfHeld(mLock); + + ParsingPackageUtils.readConfigUseRoundIcon(mContext.getResources()); + + mServiceStartWithDelay = SystemClock.uptimeMillis() + (60 * 1000L); + + Slog.i(TAG, "Fix for b/169414761 is applied"); + } + + @GuardedBy("mLock") + void updateInstantAppInstallerLocked(String modifiedPackage) { + // we're only interested in updating the installer application 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()); + } + + public boolean isFirstBoot() { + // allow instant applications + return mFirstBoot; + } + + public boolean isDeviceUpgrading() { + // allow instant applications + // The system property allows testing ota flow when upgraded to the same image. + return mIsUpgrade || SystemProperties.getBoolean( + "persist.pm.mock-upgrade", false /* default */); + } + + @NonNull + private String[] getRequiredButNotReallyRequiredVerifiersLPr(@NonNull Computer computer) { + final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); + + final List matches = + mResolveIntentHelper.queryIntentReceiversInternal(computer, intent, + PACKAGE_MIME_TYPE, + MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, + UserHandle.USER_SYSTEM, Binder.getCallingUid()); + final int size = matches.size(); + if (size == 0) { + Log.w(TAG, "There should probably be a verifier, but, none were found"); + return EmptyArray.STRING; + } else if (size <= REQUIRED_VERIFIERS_MAX_COUNT) { + String[] verifiers = new String[size]; + for (int i = 0; i < size; ++i) { + verifiers[i] = matches.get(i).getComponentInfo().packageName; + if (TextUtils.isEmpty(verifiers[i])) { + throw new RuntimeException("Invalid verifier: " + matches); + } + } + return verifiers; + } + throw new RuntimeException( + "There must be no more than " + REQUIRED_VERIFIERS_MAX_COUNT + " verifiers; found " + + matches); + } + + @NonNull + private String getRequiredSharedLibrary(@NonNull Computer snapshot, @NonNull String name, + int version) { + SharedLibraryInfo libraryInfo = snapshot.getSharedLibraryInfo(name, version); + if (libraryInfo == null) { + throw new IllegalStateException("Missing required shared library:" + name); + } + String packageName = libraryInfo.getPackageName(); + if (packageName == null) { + throw new IllegalStateException("Expected a package for shared library " + name); + } + return packageName; + } + + @NonNull + private String getRequiredServicesExtensionPackageLPr(@NonNull Computer computer) { + String configServicesExtensionPackage = mContext.getString( + R.string.config_servicesExtensionPackage); + if (TextUtils.isEmpty(configServicesExtensionPackage)) { + throw new RuntimeException( + "Required services extension package failed due to " + + "config_servicesExtensionPackage is empty."); + } + String servicesExtensionPackage = ensureSystemPackageName(computer, + configServicesExtensionPackage); + if (TextUtils.isEmpty(servicesExtensionPackage)) { + throw new RuntimeException( + "Required services extension package is missing, " + + "config_servicesExtensionPackage had defined with " + + configServicesExtensionPackage + + ", but can not find the package info on the system image, check if " + + "the package has a problem."); + } + return servicesExtensionPackage; + } + + private @NonNull String getRequiredInstallerLPr(@NonNull Computer computer) { + final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.setDataAndType(Uri.parse("content://com.example/foo.apk"), PACKAGE_MIME_TYPE); + + final List matches = computer.queryIntentActivitiesInternal(intent, + PACKAGE_MIME_TYPE, + MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, + UserHandle.USER_SYSTEM); + if (matches.size() == 1) { + ResolveInfo resolveInfo = matches.get(0); + if (!resolveInfo.activityInfo.applicationInfo.isPrivilegedApp()) { + throw new RuntimeException("The installer must be a privileged app"); + } + return matches.get(0).getComponentInfo().packageName; + } else { + throw new RuntimeException("There must be exactly one installer; found " + matches); + } + } + + private @NonNull String getRequiredUninstallerLPr(@NonNull Computer computer) { + final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.setData(Uri.fromParts(PACKAGE_SCHEME, "foo.bar", null)); + + final ResolveInfo resolveInfo = mResolveIntentHelper.resolveIntentInternal(computer, intent, + null, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, + 0 /*privateResolveFlags*/, UserHandle.USER_SYSTEM, false, Binder.getCallingUid()); + if (resolveInfo == null || + mResolveActivity.name.equals(resolveInfo.getComponentInfo().name)) { + throw new RuntimeException("There must be exactly one uninstaller; found " + + resolveInfo); + } + return resolveInfo.getComponentInfo().packageName; + } + + private @NonNull String getRequiredPermissionControllerLPr(@NonNull Computer computer) { + final Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSIONS); + intent.addCategory(Intent.CATEGORY_DEFAULT); + + final List matches = computer.queryIntentActivitiesInternal(intent, null, + MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, + UserHandle.USER_SYSTEM); + if (matches.size() == 1) { + ResolveInfo resolveInfo = matches.get(0); + if (!resolveInfo.activityInfo.applicationInfo.isPrivilegedApp()) { + throw new RuntimeException("The permissions manager must be a privileged app"); + } + return matches.get(0).getComponentInfo().packageName; + } else { + throw new RuntimeException("There must be exactly one permissions manager; found " + + matches); + } + } + + @NonNull + private ComponentName getIntentFilterVerifierComponentNameLPr(@NonNull Computer computer) { + final Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION); + + final List matches = + mResolveIntentHelper.queryIntentReceiversInternal(computer, intent, + PACKAGE_MIME_TYPE, + MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, + UserHandle.USER_SYSTEM, Binder.getCallingUid()); + ResolveInfo best = null; + final int N = matches.size(); + for (int i = 0; i < N; i++) { + final ResolveInfo cur = matches.get(i); + final String packageName = cur.getComponentInfo().packageName; + if (checkPermission( + android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT, packageName, + UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) { + continue; + } + + if (best == null || cur.priority > best.priority) { + best = cur; + } + } + + if (best != null) { + return best.getComponentInfo().getComponentName(); + } + Slog.w(TAG, "Intent filter verifier not found"); + return null; + } + + @Nullable + private ComponentName getDomainVerificationAgentComponentNameLPr(@NonNull Computer computer) { + Intent intent = new Intent(Intent.ACTION_DOMAINS_NEED_VERIFICATION); + List matches = + mResolveIntentHelper.queryIntentReceiversInternal(computer, intent, null, + MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, + UserHandle.USER_SYSTEM, Binder.getCallingUid()); + ResolveInfo best = null; + final int N = matches.size(); + for (int i = 0; i < N; i++) { + final ResolveInfo cur = matches.get(i); + final String packageName = cur.getComponentInfo().packageName; + if (checkPermission( + android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, packageName, + UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) { + Slog.w(TAG, "Domain verification agent found but does not hold permission: " + + packageName); + continue; + } + + if (best == null || cur.priority > best.priority) { + if (computer.isComponentEffectivelyEnabled(cur.getComponentInfo(), + UserHandle.SYSTEM)) { + best = cur; + } else { + Slog.w(TAG, "Domain verification agent found but not enabled"); + } + } + } + + if (best != null) { + return best.getComponentInfo().getComponentName(); + } + Slog.w(TAG, "Domain verification agent not found"); + return null; + } + + @Nullable ComponentName getInstantAppResolver(@NonNull Computer snapshot) { + final String[] packageArray = + mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage); + if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) { + if (DEBUG_INSTANT) { + Slog.d(TAG, "Ephemeral resolver NOT found; empty package list"); + } + return null; + } + + final int callingUid = Binder.getCallingUid(); + final int resolveFlags = + MATCH_DIRECT_BOOT_AWARE + | MATCH_DIRECT_BOOT_UNAWARE + | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0); + final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE); + List resolvers = snapshot.queryIntentServicesInternal(resolverIntent, null, + resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/); + final int N = resolvers.size(); + if (N == 0) { + if (DEBUG_INSTANT) { + Slog.d(TAG, "Ephemeral resolver NOT found; no matching intent filters"); + } + return null; + } + + final Set possiblePackages = new ArraySet<>(Arrays.asList(packageArray)); + for (int i = 0; i < N; i++) { + final ResolveInfo info = resolvers.get(i); + + if (info.serviceInfo == null) { + continue; + } + + final String packageName = info.serviceInfo.packageName; + if (!possiblePackages.contains(packageName) && !Build.IS_DEBUGGABLE) { + if (DEBUG_INSTANT) { + Slog.d(TAG, "Ephemeral resolver not in allowed package list;" + + " pkg: " + packageName + ", info:" + info); + } + continue; + } + + if (DEBUG_INSTANT) { + Slog.v(TAG, "Ephemeral resolver found;" + + " pkg: " + packageName + ", info:" + info); + } + return new ComponentName(packageName, info.serviceInfo.name); + } + if (DEBUG_INSTANT) { + Slog.v(TAG, "Ephemeral resolver NOT found"); + } + return null; + } + + @GuardedBy("mLock") + private @Nullable ActivityInfo getInstantAppInstallerLPr() { + String[] orderedActions = mIsEngBuild + ? new String[]{ + Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE + "_TEST", + Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE} + : new String[]{ + Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE}; + + final int resolveFlags = + MATCH_DIRECT_BOOT_AWARE + | MATCH_DIRECT_BOOT_UNAWARE + | Intent.FLAG_IGNORE_EPHEMERAL + | (mIsEngBuild ? 0 : MATCH_SYSTEM_ONLY); + final Computer computer = snapshotComputer(); + final Intent intent = new Intent(); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE); + List matches = null; + for (String action : orderedActions) { + intent.setAction(action); + matches = computer.queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE, + resolveFlags, UserHandle.USER_SYSTEM); + if (matches.isEmpty()) { + if (DEBUG_INSTANT) { + Slog.d(TAG, "Instant App installer not found with " + action); + } + } else { + break; + } + } + Iterator iter = matches.iterator(); + while (iter.hasNext()) { + final ResolveInfo rInfo = iter.next(); + if (checkPermission( + Manifest.permission.INSTALL_PACKAGES, + rInfo.activityInfo.packageName, 0) == PERMISSION_GRANTED || mIsEngBuild) { + continue; + } + iter.remove(); + } + if (matches.size() == 0) { + return null; + } else if (matches.size() == 1) { + return (ActivityInfo) matches.get(0).getComponentInfo(); + } else { + throw new RuntimeException( + "There must be at most one ephemeral installer; found " + matches); + } + } + + private @Nullable ComponentName getInstantAppResolverSettingsLPr(@NonNull Computer computer, + @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 matches = computer.queryIntentActivitiesInternal(intent, null, + resolveFlags, UserHandle.USER_SYSTEM); + if (matches.isEmpty()) { + return null; + } + return matches.get(0).getComponentInfo().getComponentName(); + } + + public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) { + // Because this is accessed via the package manager service AIDL, + // go through the permission manager service AIDL + return mContext.getSystemService(PermissionManager.class) + .getPermissionGroupInfo(groupName, flags); + } + + /** + * Blocking call to clear all cached app data above quota. + */ + public void freeAllAppCacheAboveQuota(String volumeUuid) throws IOException { + synchronized (mInstallLock) { + // To avoid refactoring Installer.freeCache() and InstalldNativeService.freeCache(), + // Long.MAX_VALUE is passed as an argument which is used in neither of two methods + // when FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES is set + try { + mInstaller.freeCache(volumeUuid, Long.MAX_VALUE, Installer.FLAG_FREE_CACHE_V2 + | Installer.FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES); + } catch (InstallerException ignored) { + } + } + return; + } + + /** + * 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, + @StorageManager.AllocateFlags int flags) throws IOException { + mFreeStorageHelper.freeStorage(volumeUuid, bytes, flags); + } + + int freeCacheForInstallation(int recommendedInstallLocation, PackageLite pkgLite, + String resolvedPath, String mPackageAbiOverride, int installFlags) { + return mFreeStorageHelper.freeCacheForInstallation(recommendedInstallLocation, pkgLite, + resolvedPath, mPackageAbiOverride, installFlags); + } + + public ModuleInfo getModuleInfo(String packageName, @PackageManager.ModuleInfoFlags int flags) { + return mModuleInfoProvider.getModuleInfo(packageName, flags); + } + + void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) { + mChangedPackagesTracker.updateSequenceNumber(pkgSetting.getPackageName(), userList); + } + + public boolean hasSystemFeature(String name, int version) { + // allow instant applications + final FeatureInfo feat = mAvailableFeatures.get(name); + if (feat == null) { + return false; + } else { + return feat.version >= version; + } + } + + // NOTE: Can't remove due to unsupported app usage + public int checkPermission(String permName, String pkgName, int userId) { + return mPermissionManager.checkPermission(pkgName, permName, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId); + } + + public String getSdkSandboxPackageName() { + return mRequiredSdkSandboxPackage; + } + + String getPackageInstallerPackageName() { + return mRequiredInstallerPackage; + } + + void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj, + Intent origIntent, String resolvedType, String callingPackage, + @Nullable String callingFeatureId, boolean isRequesterInstantApp, + Bundle verificationBundle, int userId) { + final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO, + new InstantAppRequest(responseObj, origIntent, resolvedType, + callingPackage, callingFeatureId, isRequesterInstantApp, userId, + verificationBundle, false /*resolveForStart*/, + responseObj.hostDigestPrefixSecure, responseObj.token)); + mHandler.sendMessage(msg); + } + + // findPreferredActivityBody returns two items: a "things changed" flag and a + // ResolveInfo, which is the preferred activity itself. + static class FindPreferredActivityBodyResult { + boolean mChanged; + ResolveInfo mPreferredResolveInfo; + } + + public @NonNull ParceledListSlice queryIntentReceivers(@NonNull Computer snapshot, + Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, + @UserIdInt int userId) { + return new ParceledListSlice<>(mResolveIntentHelper.queryIntentReceiversInternal( + snapshot, intent, resolvedType, flags, userId, Binder.getCallingUid())); + } + + public static void reportSettingsProblem(int priority, String msg) { + logCriticalInfo(priority, msg); + } + + // TODO:(b/135203078): Move to parsing + static void renameStaticSharedLibraryPackage(ParsedPackage parsedPackage) { + // Derive the new package synthetic package name + parsedPackage.setPackageName(toStaticSharedLibraryPackageName( + parsedPackage.getPackageName(), parsedPackage.getStaticSharedLibraryVersion())); + } + + private static String toStaticSharedLibraryPackageName( + String packageName, long libraryVersion) { + return packageName + STATIC_SHARED_LIB_DELIMITER + libraryVersion; + } + + public void performFstrimIfNeeded() { + mFreeStorageHelper.performFstrimIfNeeded(); + } + + public void updatePackagesIfNeeded() { + mDexOptHelper.performPackageDexOptUpgradeIfNeeded(); + } + + private void notifyPackageUseInternal(String packageName, int reason) { + long time = System.currentTimeMillis(); + synchronized (mLock) { + final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName); + if (pkgSetting == null) { + return; + } + pkgSetting.getPkgState().setLastPackageUsageTimeInMills(reason, time); + } + } + + /*package*/ DexManager getDexManager() { + return mDexManager; + } + + /*package*/ DexOptHelper getDexOptHelper() { + return mDexOptHelper; + } + + /*package*/ DynamicCodeLogger getDynamicCodeLogger() { + return mDynamicCodeLogger; + } + + public void shutdown() { + mCompilerStats.writeNow(); + mDexManager.writePackageDexUsageNow(); + mDynamicCodeLogger.writeNow(); + PackageWatchdog.getInstance(mContext).writeNow(); + + synchronized (mLock) { + mPackageUsage.writeNow(mSettings.getPackagesLocked()); + + if (mHandler.hasMessages(WRITE_SETTINGS) + || mBackgroundHandler.hasMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS) + || mHandler.hasMessages(WRITE_PACKAGE_LIST)) { + while (!tryWriteSettings(/*sync=*/true)) { + Slog.wtf(TAG, "Failed to write settings on shutdown"); + } + } + } + } + + @NonNull + int[] resolveUserIds(int userId) { + return (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[] { userId }; + } + + private void setUpInstantAppInstallerActivityLP(ActivityInfo installerActivity) { + if (installerActivity == null) { + if (DEBUG_INSTANT) { + Slog.d(TAG, "Clear ephemeral installer activity"); + } + mInstantAppInstallerActivity = null; + onChanged(); + return; + } + + if (DEBUG_INSTANT) { + Slog.d(TAG, "Set ephemeral installer activity: " + + installerActivity.getComponentName()); + } + // Set up information for ephemeral installer activity + mInstantAppInstallerActivity = installerActivity; + mInstantAppInstallerActivity.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS + | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS; + mInstantAppInstallerActivity.exported = true; + mInstantAppInstallerActivity.enabled = true; + mInstantAppInstallerInfo.activityInfo = mInstantAppInstallerActivity; + mInstantAppInstallerInfo.priority = 1; + mInstantAppInstallerInfo.preferredOrder = 1; + mInstantAppInstallerInfo.isDefault = true; + mInstantAppInstallerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART + | IntentFilter.MATCH_ADJUSTMENT_NORMAL; + onChanged(); + } + + void killApplication(String pkgName, @AppIdInt int appId, String reason, int exitInfoReason) { + killApplication(pkgName, appId, UserHandle.USER_ALL, reason, exitInfoReason); + } + + void killApplication(String pkgName, @AppIdInt int appId, + @UserIdInt int userId, String reason, int exitInfoReason) { + // Request the ActivityManager to kill the process(only for existing packages) + // so that we do not end up in a confused state while the user is still using the older + // version of the application while the new one gets installed. + final long token = Binder.clearCallingIdentity(); + try { + IActivityManager am = ActivityManager.getService(); + if (am != null) { + try { + am.killApplication(pkgName, appId, userId, reason, exitInfoReason); + } catch (RemoteException e) { + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void notifyPackageAdded(String packageName, int uid) { + mPackageObserverHelper.notifyAdded(packageName, uid); + } + + @Override + public void notifyPackageChanged(String packageName, int uid) { + mPackageObserverHelper.notifyChanged(packageName, uid); + } + + @Override + public void notifyPackageRemoved(String packageName, int uid) { + mPackageObserverHelper.notifyRemoved(packageName, uid); + UserPackage.removeFromCache(UserHandle.getUserId(uid), packageName); + } + + boolean isUserRestricted(int userId, String restrictionKey) { + Bundle restrictions = mUserManager.getUserRestrictions(userId); + if (restrictions.getBoolean(restrictionKey, false)) { + Log.w(TAG, "User is restricted: " + restrictionKey); + return true; + } + return false; + } + + private void enforceCanSetPackagesSuspendedAsUser(@NonNull Computer snapshot, + boolean quarantined, UserPackage suspender, int callingUid, int targetUserId, + String callingMethod) { + if (callingUid == Process.ROOT_UID + // Need to compare app-id to allow system dialogs access on secondary users + || UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) { + return; + } + + final String ownerPackage = + mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(targetUserId); + if (ownerPackage != null) { + final int ownerUid = snapshot.getPackageUid(ownerPackage, 0, targetUserId); + if (ownerUid == callingUid) { + return; + } + } + + if (quarantined) { + final boolean hasQuarantineAppsPerm = mContext.checkCallingOrSelfPermission( + android.Manifest.permission.QUARANTINE_APPS) == PERMISSION_GRANTED; + // TODO: b/305256093 - In order to facilitate testing, temporarily allowing apps + // with SUSPEND_APPS permission to quarantine apps. Remove this once the testing + // is done and this is no longer needed. + if (!hasQuarantineAppsPerm) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS, + callingMethod); + } + } else { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS, + callingMethod); + } + + final int packageUid = snapshot.getPackageUid(suspender.packageName, 0, targetUserId); + final boolean allowedPackageUid = packageUid == callingUid; + // TODO(b/139383163): remove special casing for shell and enforce INTERACT_ACROSS_USERS_FULL + final boolean allowedShell = callingUid == SHELL_UID + && UserHandle.isSameApp(packageUid, callingUid); + + if (!allowedShell && !allowedPackageUid) { + throw new SecurityException("Suspending package " + suspender.packageName + + " in user " + targetUserId + " does not belong to calling uid " + callingUid); + } + } + + void unsuspendForSuspendingPackage(@NonNull Computer computer, String suspendingPackage, + @UserIdInt int suspendingUserId) { + // TODO: This can be replaced by a special parameter to iterate all packages, rather than + // this weird pre-collect of all packages. + final String[] allPackages = computer.getPackageStates().keySet().toArray(new String[0]); + final Predicate suspenderPredicate = + UserPackage.of(suspendingUserId, suspendingPackage)::equals; + mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer, + allPackages, suspenderPredicate, suspendingUserId); + } + + void removeAllDistractingPackageRestrictions(@NonNull Computer snapshot, int userId) { + final String[] allPackages = snapshot.getAllAvailablePackageNames(); + mDistractingPackageHelper.removeDistractingPackageRestrictions(snapshot, allPackages, + userId); + } + + private void enforceCanSetDistractingPackageRestrictionsAsUser(int callingUid, int userId, + String callingMethod) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, + callingMethod); + + if (!PackageManagerServiceUtils.isSystemOrRoot(callingUid) + && UserHandle.getUserId(callingUid) != userId) { + throw new SecurityException("Calling uid " + callingUid + " cannot call for user " + + userId); + } + } + + void setEnableRollbackCode(int token, int enableRollbackCode) { + final Message msg = mHandler.obtainMessage(ENABLE_ROLLBACK_STATUS); + msg.arg1 = token; + msg.arg2 = enableRollbackCode; + mHandler.sendMessage(msg); + } + + /** + * Callback from PackageSettings whenever an app is first transitioned out of the + * 'stopped' state. Normally we just issue the broadcast, but we can't do that if + * the app was "launched" for a restoreAtInstall operation. Therefore we check + * here whether the app is the target of an ongoing install, and only send the + * broadcast immediately if it is not in that state. If it *is* undergoing a restore, + * the first-launch broadcast will be sent implicitly on that basis in POST_INSTALL + * handling. + */ + void notifyFirstLaunch(final String packageName, final String installerPackage, + final int userId) { + // Serialize this with the rest of the install-process message chain. In the + // restore-at-install case, this Runnable will necessarily run before the + // POST_INSTALL message is processed, so the contents of mRunningInstalls + // are coherent. In the non-restore case, the app has already completed install + // and been launched through some other means, so it is not in a problematic + // state for observers to see the FIRST_LAUNCH signal. + mHandler.post(() -> { + for (int i = 0; i < mRunningInstalls.size(); i++) { + final InstallRequest installRequest = mRunningInstalls.valueAt(i); + if (installRequest.getReturnCode() != PackageManager.INSTALL_SUCCEEDED) { + continue; + } + if (packageName.equals(installRequest.getPkg().getPackageName())) { + // right package; but is it for the right user? + for (int uIndex = 0; uIndex < installRequest.getNewUsers().length; uIndex++) { + if (userId == installRequest.getNewUsers()[uIndex]) { + if (DEBUG_BACKUP) { + Slog.i(TAG, "Package " + packageName + + " being restored so deferring FIRST_LAUNCH"); + } + return; + } + } + } + } + // didn't find it, so not being restored + if (DEBUG_BACKUP) { + Slog.i(TAG, "Package " + packageName + " sending normal FIRST_LAUNCH"); + } + final boolean isInstantApp = snapshotComputer().isInstantAppInternal( + packageName, userId, Process.SYSTEM_UID); + final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId }; + final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; + mBroadcastHelper.sendFirstLaunchBroadcast( + packageName, installerPackage, userIds, instantUserIds); + }); + } + + @SuppressWarnings("GuardedBy") + VersionInfo getSettingsVersionForPackage(AndroidPackage pkg) { + if (pkg.isExternalStorage()) { + if (TextUtils.isEmpty(pkg.getVolumeUuid())) { + return mSettings.getExternalVersion(); + } else { + return mSettings.findOrCreateVersion(pkg.getVolumeUuid()); + } + } else { + return mSettings.getInternalVersion(); + } + } + + public void deleteExistingPackageAsUser(VersionedPackage versionedPackage, + final IPackageDeleteObserver2 observer, final int userId) { + mDeletePackageHelper.deleteExistingPackageAsUser( + versionedPackage, observer, userId); + } + + public void deletePackageVersioned(VersionedPackage versionedPackage, + final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags) { + mDeletePackageHelper.deletePackageVersionedInternal( + versionedPackage, observer, userId, deleteFlags, false); + } + + boolean isCallerVerifier(@NonNull Computer snapshot, int callingUid) { + final int callingUserId = UserHandle.getUserId(callingUid); + for (String requiredVerifierPackage : mRequiredVerifierPackages) { + if (callingUid == snapshot.getPackageUid(requiredVerifierPackage, 0, callingUserId)) { + return true; + } + } + return false; + } + + public boolean isPackageDeviceAdminOnAnyUser(@NonNull Computer snapshot, String packageName) { + final int callingUid = Binder.getCallingUid(); + if (snapshot.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 (snapshot.getInstantAppPackageName(callingUid) != null + && !snapshot.isCallerSameApp(packageName, callingUid)) { + return false; + } + return isPackageDeviceAdmin(packageName, UserHandle.USER_ALL); + } + + // TODO(b/261957226): centralise this logic in DPM + boolean isPackageDeviceAdmin(String packageName, int userId) { + final IDevicePolicyManager dpm = getDevicePolicyManager(); + try { + if (dpm != null) { + final ComponentName deviceOwnerComponentName = dpm.getDeviceOwnerComponent( + /* callingUserOnly =*/ false); + final String deviceOwnerPackageName = deviceOwnerComponentName == null ? null + : deviceOwnerComponentName.getPackageName(); + // Does the package contains the device owner? + // TODO Do we have to do it even if userId != UserHandle.USER_ALL? Otherwise, + // this check is probably not needed, since DO should be registered as a device + // admin on some user too. (Original bug for this: b/17657954) + if (packageName.equals(deviceOwnerPackageName)) { + return true; + } + // Does it contain a device admin for any user? + int[] users; + if (userId == UserHandle.USER_ALL) { + users = mUserManager.getUserIds(); + } else { + users = new int[]{userId}; + } + for (int i = 0; i < users.length; ++i) { + if (dpm.packageHasActiveAdmins(packageName, users[i])) { + return true; + } + if (isDeviceManagementRoleHolder(packageName, users[i])) { + return true; + } + } + } + } catch (RemoteException e) { + } + return false; + } + + private boolean isDeviceManagementRoleHolder(String packageName, int userId) { + return Objects.equals(packageName, getDevicePolicyManagementRoleHolderPackageName(userId)); + } + + @Nullable + public String getDevicePolicyManagementRoleHolderPackageName(int userId) { + return Binder.withCleanCallingIdentity(() -> { + RoleManager roleManager = mContext.getSystemService(RoleManager.class); + List roleHolders = + roleManager.getRoleHoldersAsUser( + RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, UserHandle.of(userId)); + if (roleHolders.isEmpty()) { + return null; + } + return roleHolders.get(0); + }); + } + + /** Returns the device policy manager interface. */ + private IDevicePolicyManager getDevicePolicyManager() { + if (mDevicePolicyManager == null) { + // No need to synchronize; worst-case scenario it will be fetched twice. + mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface( + ServiceManager.getService(Context.DEVICE_POLICY_SERVICE)); + } + return mDevicePolicyManager; + } + + private boolean clearApplicationUserDataLIF(@NonNull Computer snapshot, String packageName, + int userId) { + if (packageName == null) { + Slog.w(TAG, "Attempt to delete null packageName."); + return false; + } + + // Try finding details about the requested package + AndroidPackage pkg = snapshot.getPackage(packageName); + if (pkg == null) { + Slog.w(TAG, "Package named '" + packageName + "' doesn't exist."); + return false; + } + mPermissionManager.resetRuntimePermissions(pkg, userId); + + mAppDataHelper.clearAppDataLIF(pkg, userId, + FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL); + + final int appId = UserHandle.getAppId(pkg.getUid()); + mAppDataHelper.clearKeystoreData(userId, appId); + + UserManagerInternal umInternal = mInjector.getUserManagerInternal(); + StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class); + final int flags; + if (StorageManager.isCeStorageUnlocked(userId) && smInternal.isCeStoragePrepared(userId)) { + flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; + } else if (umInternal.isUserRunning(userId)) { + flags = StorageManager.FLAG_STORAGE_DE; + } else { + flags = 0; + } + mAppDataHelper.prepareAppDataContentsLIF(pkg, snapshot.getPackageStateInternal(packageName), + userId, flags); + + return true; + } + + /** + * Update component enabled settings to {@link PackageManager#COMPONENT_ENABLED_STATE_DEFAULT} + * if the resetEnabledSettingsOnAppDataCleared is {@code true}. + */ + @GuardedBy("mLock") + private void resetComponentEnabledSettingsIfNeededLPw(String packageName, int userId) { + final AndroidPackage pkg = packageName != null ? mPackages.get(packageName) : null; + if (pkg == null || !pkg.isResetEnabledSettingsOnAppDataCleared()) { + return; + } + final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName); + if (pkgSetting == null) { + return; + } + final ArrayList updatedComponents = new ArrayList<>(); + final Consumer resetSettings = (component) -> { + if (pkgSetting.restoreComponentLPw(component.getClassName(), userId)) { + updatedComponents.add(component.getClassName()); + } + }; + for (int i = 0; i < pkg.getActivities().size(); i++) { + resetSettings.accept(pkg.getActivities().get(i)); + } + for (int i = 0; i < pkg.getReceivers().size(); i++) { + resetSettings.accept(pkg.getReceivers().get(i)); + } + for (int i = 0; i < pkg.getServices().size(); i++) { + resetSettings.accept(pkg.getServices().get(i)); + } + for (int i = 0; i < pkg.getProviders().size(); i++) { + resetSettings.accept(pkg.getProviders().get(i)); + } + if (ArrayUtils.isEmpty(updatedComponents)) { + // nothing changed + return; + } + + updateSequenceNumberLP(pkgSetting, new int[] { userId }); + updateInstantAppInstallerLocked(packageName); + scheduleWritePackageRestrictions(userId); + + mPendingBroadcasts.addComponents(userId, packageName, updatedComponents); + if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) { + mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, BROADCAST_DELAY); + } + } + + /** This method takes a specific user id as well as UserHandle.USER_ALL. */ + @GuardedBy("mLock") + void clearPackagePreferredActivitiesLPw(String packageName, + @NonNull SparseBooleanArray outUserChanged, int userId) { + mSettings.clearPackagePreferredActivities(packageName, outUserChanged, userId); + } + + void restorePermissionsAndUpdateRolesForNewUserInstall(String packageName, + @UserIdInt int userId) { + // We may also need to apply pending (restored) runtime permission grants + // within these users. + mPermissionManager.restoreDelayedRuntimePermissions(packageName, userId); + + // Restore default browser setting if it is now installed. + String defaultBrowser; + synchronized (mLock) { + defaultBrowser = mSettings.getPendingDefaultBrowserLPr(userId); + } + if (Objects.equals(packageName, defaultBrowser)) { + mDefaultAppProvider.setDefaultBrowser(packageName, userId); + synchronized (mLock) { + mSettings.removePendingDefaultBrowserLPw(userId); + } + } + + // Persistent preferred activity might have came into effect due to this + // install. + mPreferredActivityHelper.updateDefaultHomeNotLocked(snapshotComputer(), userId); + } + + /** + * Variant that takes a {@link WatchedIntentFilter} + */ + public void addCrossProfileIntentFilter(@NonNull Computer snapshot, + WatchedIntentFilter intentFilter, String ownerPackage, int sourceUserId, + int targetUserId, int flags) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); + int callingUid = Binder.getCallingUid(); + enforceOwnerRights(snapshot, ownerPackage, callingUid); + + // Verifying that current calling uid should be able to add {@link CrossProfileIntentFilter} + // for source and target user + mUserManager.enforceCrossProfileIntentFilterAccess(sourceUserId, targetUserId, callingUid, + /* addCrossProfileIntentFilter */ true); + + PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(), + UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId); + if (!intentFilter.checkDataPathAndSchemeSpecificParts()) { + EventLog.writeEvent(0x534e4554, "246749936", callingUid); + throw new IllegalArgumentException("Invalid intent data paths or scheme specific parts" + + " in the filter."); + } + if (intentFilter.countActions() == 0) { + Slog.w(TAG, "Cannot set a crossProfile intent filter with no filter actions"); + return; + } + synchronized (mLock) { + CrossProfileIntentFilter newFilter = new CrossProfileIntentFilter(intentFilter, + ownerPackage, targetUserId, flags, mUserManager + .getCrossProfileIntentFilterAccessControl(sourceUserId, targetUserId)); + CrossProfileIntentResolver resolver = + mSettings.editCrossProfileIntentResolverLPw(sourceUserId); + ArrayList existing = resolver.findFilters(intentFilter); + // We have all those whose filter is equal. Now checking if the rest is equal as well. + if (existing != null) { + int size = existing.size(); + for (int i = 0; i < size; i++) { + if (newFilter.equalsIgnoreFilter(existing.get(i))) { + return; + } + } + } + resolver.addFilter(snapshotComputer(), newFilter); + } + scheduleWritePackageRestrictions(sourceUserId); + } + + + // Enforcing that callingUid is owning pkg on userId + private void enforceOwnerRights(@NonNull Computer snapshot, String pkg, int callingUid) { + // The system owns everything. + if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) { + return; + } + final String[] callerPackageNames = snapshot.getPackagesForUid(callingUid); + if (!ArrayUtils.contains(callerPackageNames, pkg)) { + throw new SecurityException("Calling uid " + callingUid + + " does not own package " + pkg); + } + final int callingUserId = UserHandle.getUserId(callingUid); + PackageInfo pi = snapshot.getPackageInfo(pkg, 0, callingUserId); + if (pi == null) { + throw new IllegalArgumentException("Unknown package " + pkg + " on user " + + callingUserId); + } + } + + public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) { + mBroadcastHelper.sendSessionCommitBroadcast(snapshotComputer(), sessionInfo, userId, + mAppPredictionServicePackage); + } + + private @Nullable String getSetupWizardPackageNameImpl(@NonNull Computer computer) { + final Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_SETUP_WIZARD); + + final List matches = computer.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(@NonNull Computer computer) { + final Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE); + + final List matches = computer.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.w(TAG, "There should probably be exactly one storage manager; found " + + matches.size() + ": matches=" + matches); + return null; + } + } + + @NonNull + private static String getRequiredSdkSandboxPackageName(@NonNull Computer computer) { + final Intent intent = new Intent(SdkSandboxManagerLocal.SERVICE_INTERFACE); + + final List matches = computer.queryIntentServicesInternal( + intent, + /* resolvedType= */ null, + MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, + UserHandle.USER_SYSTEM, + /* callingUid= */ Process.myUid(), + /* includeInstantApps= */ false); + if (matches.size() == 1) { + return matches.get(0).getComponentInfo().packageName; + } else { + throw new RuntimeException("There should exactly one sdk sandbox package; found " + + matches.size() + ": matches=" + matches); + } + } + + @Nullable + private String getRetailDemoPackageName() { + final String predefinedPkgName = mContext.getString(R.string.config_retailDemoPackage); + final String predefinedSignature = mContext.getString( + R.string.config_retailDemoPackageSignature); + + if (TextUtils.isEmpty(predefinedPkgName) || TextUtils.isEmpty(predefinedSignature)) { + return null; + } + + final AndroidPackage androidPkg = mPackages.get(predefinedPkgName); + if (androidPkg != null) { + final SigningDetails signingDetail = androidPkg.getSigningDetails(); + if (signingDetail != null && signingDetail.getSignatures() != null) { + try { + final MessageDigest msgDigest = MessageDigest.getInstance("SHA-256"); + for (Signature signature : signingDetail.getSignatures()) { + if (TextUtils.equals(predefinedSignature, + HexEncoding.encodeToString(msgDigest.digest( + signature.toByteArray()), false))) { + return predefinedPkgName; + } + } + } catch (NoSuchAlgorithmException e) { + Slog.e( + TAG, + "Unable to verify signatures as getting the retail demo package name", + e); + } + } + } + + return null; + } + + @Nullable + String getPackageFromComponentString(@StringRes int stringResId) { + final String componentString = mContext.getString(stringResId); + if (TextUtils.isEmpty(componentString)) { + return null; + } + final ComponentName component = ComponentName.unflattenFromString(componentString); + if (component == null) { + return null; + } + return component.getPackageName(); + } + + @Nullable + String ensureSystemPackageName(@NonNull Computer snapshot, + @Nullable String packageName) { + if (packageName == null) { + return null; + } + final long token = Binder.clearCallingIdentity(); + try { + if (snapshot.getPackageInfo(packageName, MATCH_FACTORY_ONLY, + UserHandle.USER_SYSTEM) == null) { + PackageInfo packageInfo = + snapshot.getPackageInfo(packageName, 0, UserHandle.USER_SYSTEM); + if (packageInfo != null) { + EventLog.writeEvent(0x534e4554, "145981139", packageInfo.applicationInfo.uid, + ""); + } + Log.w(TAG, "Missing required system package: " + packageName + (packageInfo != null + ? ", but found with extended search." : ".")); + return null; + } + } finally { + Binder.restoreCallingIdentity(token); + } + return packageName; + } + + public int getPackageUid(String packageName, long flags, int userId) { + return snapshotComputer().getPackageUid(packageName, flags, userId); + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public void updateComponentLabelIcon(/*@NonNull*/ ComponentName componentName, + @Nullable String nonLocalizedLabel, @Nullable Integer icon, int userId) { + if (componentName == null) { + throw new IllegalArgumentException("Must specify a component"); + } + + int callingUid = Binder.getCallingUid(); + String componentPkgName = componentName.getPackageName(); + + Computer computer = snapshotComputer(); + + int componentUid = computer.getPackageUid(componentPkgName, 0, userId); + if (!UserHandle.isSameApp(callingUid, componentUid)) { + throw new SecurityException("The calling UID (" + callingUid + ")" + + " does not match the target UID"); + } + + String allowedCallerPkg = + mContext.getString(R.string.config_overrideComponentUiPackage); + if (TextUtils.isEmpty(allowedCallerPkg)) { + throw new SecurityException( "There is no package defined as allowed to change a " + + "component's label or icon"); + } + + int allowedCallerUid = computer.getPackageUid(allowedCallerPkg, + PackageManager.MATCH_SYSTEM_ONLY, userId); + if (allowedCallerUid == -1 || !UserHandle.isSameApp(callingUid, allowedCallerUid)) { + throw new SecurityException("The calling UID (" + callingUid + ")" + + " is not allowed to change a component's label or icon"); + } + PackageStateInternal packageState = computer.getPackageStateInternal(componentPkgName); + if (packageState == null || packageState.getPkg() == null + || (!packageState.isSystem() + && !packageState.isUpdatedSystemApp())) { + throw new SecurityException( + "Changing the label is not allowed for " + componentName); + } + + if (!computer.getComponentResolver().componentExists(componentName)) { + throw new IllegalArgumentException("Component " + componentName + " not found"); + } + + Pair overrideLabelIcon = packageState.getUserStateOrDefault(userId) + .getOverrideLabelIconForComponent(componentName); + + String existingLabel = overrideLabelIcon == null ? null : overrideLabelIcon.first; + Integer existingIcon = overrideLabelIcon == null ? null : overrideLabelIcon.second; + + if (TextUtils.equals(existingLabel, nonLocalizedLabel) + && Objects.equals(existingIcon, icon)) { + // Nothing changed + return; + } + + commitPackageStateMutation(null, componentPkgName, + state -> state.userState(userId) + .setComponentLabelIcon(componentName, nonLocalizedLabel, icon)); + + mPendingBroadcasts.addComponent(userId, componentPkgName, componentName.getClassName()); + + if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) { + mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, BROADCAST_DELAY); + } + } + + private void setEnabledSettings(List settings, int userId, + @NonNull String callingPackage) { + final int callingUid = Binder.getCallingUid(); + // TODO: This method is not properly snapshotified beyond this call + final Computer preLockSnapshot = snapshotComputer(); + preLockSnapshot.enforceCrossUserPermission(callingUid, userId, + false /* requireFullPermission */, true /* checkShell */, "set enabled"); + + final int targetSize = settings.size(); + for (int i = 0; i < targetSize; i++) { + final int newState = settings.get(i).getEnabledState(); + if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT + || newState == COMPONENT_ENABLED_STATE_ENABLED + || newState == COMPONENT_ENABLED_STATE_DISABLED + || newState == COMPONENT_ENABLED_STATE_DISABLED_USER + || newState == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) { + throw new IllegalArgumentException("Invalid new component state: " + newState); + } + } + if (targetSize > 1) { + final ArraySet checkDuplicatedPackage = new ArraySet<>(); + final ArraySet checkDuplicatedComponent = new ArraySet<>(); + final ArrayMap checkConflictFlag = new ArrayMap<>(); + for (int i = 0; i < targetSize; i++) { + final ComponentEnabledSetting setting = settings.get(i); + final String packageName = setting.getPackageName(); + if (setting.isComponent()) { + final ComponentName componentName = setting.getComponentName(); + if (checkDuplicatedComponent.contains(componentName)) { + throw new IllegalArgumentException("The component " + componentName + + " is duplicated"); + } + checkDuplicatedComponent.add(componentName); + + // check if there is a conflict of the DONT_KILL_APP flag between components + // in the package + final Integer enabledFlags = checkConflictFlag.get(packageName); + if (enabledFlags == null) { + checkConflictFlag.put(packageName, setting.getEnabledFlags()); + } else if ((enabledFlags & PackageManager.DONT_KILL_APP) + != (setting.getEnabledFlags() & PackageManager.DONT_KILL_APP)) { + throw new IllegalArgumentException("A conflict of the DONT_KILL_APP flag " + + "between components in the package " + packageName); + } + } else { + if (checkDuplicatedPackage.contains(packageName)) { + throw new IllegalArgumentException("The package " + packageName + + " is duplicated"); + } + checkDuplicatedPackage.add(packageName); + } + } + } + + final boolean allowedByPermission = mContext.checkCallingOrSelfPermission( + android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE) == PERMISSION_GRANTED; + final boolean[] updateAllowed = new boolean[targetSize]; + Arrays.fill(updateAllowed, true); + + final Map pkgSettings = new ArrayMap<>(targetSize); + // reader + synchronized (mLock) { + final Computer snapshot = snapshotComputer(); + // Checks for target packages + for (int i = 0; i < targetSize; i++) { + final ComponentEnabledSetting setting = settings.get(i); + final String packageName = setting.getPackageName(); + if (pkgSettings.containsKey(packageName)) { + // this package has verified + continue; + } + final boolean isCallerTargetApp = ArrayUtils.contains( + snapshot.getPackagesForUid(callingUid), packageName); + final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName); + // Limit who can change which apps + if (!isCallerTargetApp && !allowedByPermission) { + // Don't allow apps that don't have permission to modify other apps + throw new SecurityException("Attempt to change component state; " + + "pid=" + Binder.getCallingPid() + + ", uid=" + callingUid + + (!setting.isComponent() ? ", package=" + packageName + : ", component=" + setting.getComponentName())); + } + if (pkgSetting == null || snapshot.shouldFilterApplicationIncludingUninstalled( + pkgSetting, callingUid, userId)) { + throw new IllegalArgumentException(setting.isComponent() + ? "Unknown component: " + setting.getComponentName() + : "Unknown package: " + packageName); + } + // Don't allow changing protected packages. + if (!isCallerTargetApp + && mProtectedPackages.isPackageStateProtected(userId, packageName)) { + throw new SecurityException( + "Cannot disable a protected package: " + packageName); + } + if (callingUid == Process.SHELL_UID + && (pkgSetting.getFlags() & ApplicationInfo.FLAG_TEST_ONLY) == 0) { + // Shell can only change whole packages between ENABLED and DISABLED_USER states + // unless it is a test package. + final int oldState = pkgSetting.getEnabled(userId); + final int newState = setting.getEnabledState(); + if (!setting.isComponent() + && + (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 " + + setting.getComponentName() + " to " + newState); + } + } + pkgSettings.put(packageName, pkgSetting); + } + // Checks for target components + for (int i = 0; i < targetSize; i++) { + final ComponentEnabledSetting setting = settings.get(i); + // skip if it's application + if (!setting.isComponent()) continue; + + // Only allow apps with CHANGE_COMPONENT_ENABLED_STATE permission to change hidden + // app details activity + final String packageName = setting.getPackageName(); + final String className = setting.getClassName(); + if (!allowedByPermission + && PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) { + throw new SecurityException("Cannot disable a system-generated component"); + } + // Verify that this is a valid class name. + final AndroidPackage pkg = pkgSettings.get(packageName).getPkg(); + if (pkg == null || !AndroidPackageUtils.hasComponentClassName(pkg, className)) { + if (pkg != null + && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.JELLY_BEAN) { + throw new IllegalArgumentException("Component class " + className + + " does not exist in " + packageName); + } else { + Slog.w(TAG, "Failed setComponentEnabledSetting: component class " + + className + " does not exist in " + packageName); + updateAllowed[i] = false; + } + } + } + } + + // More work for application enabled setting updates + for (int i = 0; i < targetSize; i++) { + final ComponentEnabledSetting setting = settings.get(i); + // skip if it's component + if (setting.isComponent()) continue; + + final PackageSetting pkgSetting = pkgSettings.get(setting.getPackageName()); + final int newState = setting.getEnabledState(); + synchronized (mLock) { + if (pkgSetting.getEnabled(userId) == newState) { + // Nothing to do + updateAllowed[i] = false; + continue; + } + } + // If we're enabling a system stub, there's a little more work to do. + // Prior to enabling the package, we need to decompress the APK(s) to the + // data partition and then replace the version on the system partition. + final AndroidPackage deletedPkg = pkgSetting.getPkg(); + final boolean isSystemStub = (deletedPkg != null) + && deletedPkg.isStub() + && pkgSetting.isSystem(); + if (isSystemStub + && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT + || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) { + if (!enableCompressedPackage(deletedPkg, pkgSetting)) { + Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable " + + "commpressed package " + setting.getPackageName()); + updateAllowed[i] = false; + } + } + } + + // packageName -> list of components to send broadcasts now + final ArrayMap> sendNowBroadcasts = new ArrayMap<>(targetSize); + synchronized (mLock) { + Computer computer = snapshotComputer(); + boolean scheduleBroadcastMessage = false; + boolean isSynchronous = false; + boolean anyChanged = false; + + for (int i = 0; i < targetSize; i++) { + if (!updateAllowed[i]) { + continue; + } + // update enabled settings + final ComponentEnabledSetting setting = settings.get(i); + final String packageName = setting.getPackageName(); + if (!setEnabledSettingInternalLocked(computer, pkgSettings.get(packageName), + setting, userId, callingPackage)) { + continue; + } + anyChanged = true; + + if ((setting.getEnabledFlags() & PackageManager.SYNCHRONOUS) != 0) { + isSynchronous = true; + } + // collect broadcast list for the package + final String componentName = setting.isComponent() + ? setting.getClassName() : packageName; + if ((setting.getEnabledFlags() & PackageManager.DONT_KILL_APP) == 0) { + ArrayList componentList = sendNowBroadcasts.get(packageName); + componentList = componentList == null ? new ArrayList<>() : componentList; + if (!componentList.contains(componentName)) { + componentList.add(componentName); + } + sendNowBroadcasts.put(packageName, componentList); + // Purge entry from pending broadcast list if another one exists already + // since we are sending one right away. + mPendingBroadcasts.remove(userId, packageName); + } else { + mPendingBroadcasts.addComponent(userId, packageName, componentName); + scheduleBroadcastMessage = true; + } + } + if (!anyChanged) { + // nothing changed, return immediately + return; + } + + if (isSynchronous) { + flushPackageRestrictionsAsUserInternalLocked(userId); + } else { + scheduleWritePackageRestrictions(userId); + } + if (scheduleBroadcastMessage) { + if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) { + // Schedule a message - if it has been a "reasonably long time" since the + // service started, send the broadcast with a delay of one second to avoid + // delayed reactions from the receiver, else keep the default ten second delay + // to avoid extreme thrashing on service startup. + final long broadcastDelay = SystemClock.uptimeMillis() > mServiceStartWithDelay + ? BROADCAST_DELAY + : BROADCAST_DELAY_DURING_STARTUP; + mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, broadcastDelay); + } + } + } + + final long callingId = Binder.clearCallingIdentity(); + try { + final Computer newSnapshot = snapshotComputer(); + for (int i = 0; i < sendNowBroadcasts.size(); i++) { + final String packageName = sendNowBroadcasts.keyAt(i); + final ArrayList components = sendNowBroadcasts.valueAt(i); + final int packageUid = UserHandle.getUid( + userId, pkgSettings.get(packageName).getAppId()); + mBroadcastHelper.sendPackageChangedBroadcast(newSnapshot, packageName, + false /* dontKillApp */, components, packageUid, null /* reason */); + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + @GuardedBy("mLock") + private boolean setEnabledSettingInternalLocked(@NonNull Computer computer, + PackageSetting pkgSetting, ComponentEnabledSetting setting, @UserIdInt int userId, + String callingPackage) { + final int newState = setting.getEnabledState(); + final String packageName = setting.getPackageName(); + boolean success = false; + if (!setting.isComponent()) { + // We're dealing with an application/package level state change + pkgSetting.setEnabled(newState, userId, callingPackage); + if ((newState == COMPONENT_ENABLED_STATE_DISABLED_USER + || newState == COMPONENT_ENABLED_STATE_DISABLED) + && checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId) + == PERMISSION_GRANTED) { + // This app should not generally be allowed to get disabled by the UI, but + // if it ever does, we don't want to end up with some of the user's apps + // permanently suspended. + unsuspendForSuspendingPackage(computer, packageName, userId); + removeAllDistractingPackageRestrictions(computer, userId); + } + success = true; + } else { + // We're dealing with a component level state change + final String className = setting.getClassName(); + switch (newState) { + case COMPONENT_ENABLED_STATE_ENABLED: + success = pkgSetting.enableComponentLPw(className, userId); + break; + case COMPONENT_ENABLED_STATE_DISABLED: + success = pkgSetting.disableComponentLPw(className, userId); + break; + case COMPONENT_ENABLED_STATE_DEFAULT: + success = pkgSetting.restoreComponentLPw(className, userId); + break; + default: + Slog.e(TAG, "Failed setComponentEnabledSetting: component " + + packageName + "/" + className + + " requested an invalid new component state: " + newState); + break; + } + } + if (!success) { + return false; + } + + updateSequenceNumberLP(pkgSetting, new int[] { userId }); + final long callingId = Binder.clearCallingIdentity(); + try { + updateInstantAppInstallerLocked(packageName); + } finally { + Binder.restoreCallingIdentity(callingId); + } + + return true; + } + + @GuardedBy("mLock") + private void flushPackageRestrictionsAsUserInternalLocked(int userId) { + // NOTE: this invokes synchronous disk access, so callers using this + // method should consider running on a background thread + mSettings.writePackageRestrictionsLPr(userId); + synchronized (mDirtyUsers) { + mDirtyUsers.remove(userId); + if (mDirtyUsers.isEmpty()) { + mBackgroundHandler.removeMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS); + } + } + } + + /** + * Used by SystemServer + */ + public void waitForAppDataPrepared() { + if (mPrepareAppDataFuture == null) { + return; + } + ConcurrentUtils.waitForFutureNoInterrupt(mPrepareAppDataFuture, "wait for prepareAppData"); + mPrepareAppDataFuture = null; + } + + public void systemReady() { + PackageManagerServiceUtils.enforceSystemOrRoot( + "Only the system can claim the system is ready"); + + final ContentResolver resolver = mContext.getContentResolver(); + if (mReleaseOnSystemReady != null) { + for (int i = mReleaseOnSystemReady.size() - 1; i >= 0; --i) { + final File dstCodePath = mReleaseOnSystemReady.get(i); + F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath); + } + mReleaseOnSystemReady = null; + } + mSystemReady = true; + ContentObserver co = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + final boolean ephemeralFeatureDisabled = + Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0; + for (int userId : UserManagerService.getInstance().getUserIds()) { + final boolean instantAppsDisabledForUser = + ephemeralFeatureDisabled || Secure.getIntForUser(resolver, + Secure.INSTANT_APPS_ENABLED, 1, userId) == 0; + mWebInstantAppsDisabled.put(userId, instantAppsDisabledForUser); + } + } + }; + mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global + .getUriFor(Global.ENABLE_EPHEMERAL_FEATURE), + false, co, UserHandle.USER_ALL); + mContext.getContentResolver().registerContentObserver(android.provider.Settings.Secure + .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL); + co.onChange(true); + + mAppsFilter.onSystemReady(LocalServices.getService(PackageManagerInternal.class)); + + // Disable any carrier apps. We do this very early in boot to prevent the apps from being + // disabled after already being started. + CarrierAppUtils.disableCarrierAppsUntilPrivileged( + mContext.getOpPackageName(), UserHandle.USER_SYSTEM, mContext); + + disableSkuSpecificApps(); + + // Read the compatibilty setting when the system is ready. + boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt( + mContext.getContentResolver(), + android.provider.Settings.Global.COMPATIBILITY_MODE, 1) == 1; + ParsingPackageUtils.setCompatibilityModeEnabled(compatibilityModeEnabled); + + if (DEBUG_SETTINGS) { + Log.d(TAG, "compatibility mode:" + compatibilityModeEnabled); + } + + synchronized (mLock) { + ArrayList changed = mSettings.systemReady(mComponentResolver); + for (int i = 0; i < changed.size(); i++) { + mSettings.writePackageRestrictionsLPr(changed.get(i)); + } + } + + mUserManager.systemReady(); + + // Watch for external volumes that come and go over time + final StorageManager storage = mInjector.getSystemService(StorageManager.class); + storage.registerListener(mStorageEventHelper); + + mInstallerService.systemReady(); + mPackageDexOptimizer.systemReady(); + + // Now that we're mostly running, clean up stale users and apps + mUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL); + mStorageEventHelper.reconcileApps(snapshotComputer(), StorageManager.UUID_PRIVATE_INTERNAL); + + mPermissionManager.onSystemReady(); + + int[] grantPermissionsUserIds = EMPTY_INT_ARRAY; + final List livingUsers = mInjector.getUserManagerInternal().getUsers( + /* excludePartial= */ true, + /* excludeDying= */ true, + /* excludePreCreated= */ false); + final int livingUserCount = livingUsers.size(); + for (int i = 0; i < livingUserCount; i++) { + final int userId = livingUsers.get(i).id; + final boolean isPermissionUpgradeNeeded = !Objects.equals( + mPermissionManager.getDefaultPermissionGrantFingerprint(userId), + Build.FINGERPRINT); + if (isPermissionUpgradeNeeded) { + grantPermissionsUserIds = ArrayUtils.appendInt( + grantPermissionsUserIds, userId); + } + } + // If we upgraded grant all default permissions before kicking off. + for (int userId : grantPermissionsUserIds) { + mLegacyPermissionManager.grantDefaultPermissions(userId); + mPermissionManager.setDefaultPermissionGrantFingerprint(Build.FINGERPRINT, userId); + } + if (grantPermissionsUserIds == EMPTY_INT_ARRAY) { + // If we did not grant default permissions, we preload from this the + // default permission exceptions lazily to ensure we don't hit the + // disk on a new user creation. + mLegacyPermissionManager.scheduleReadDefaultPermissionExceptions(); + } + + if (mInstantAppResolverConnection != null) { + mContext.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mInstantAppResolverConnection.optimisticBind(); + mContext.unregisterReceiver(this); + } + }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); + } + + IntentFilter overlayFilter = new IntentFilter(Intent.ACTION_OVERLAY_CHANGED); + overlayFilter.addDataScheme("package"); + mContext.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent == null) { + return; + } + Uri data = intent.getData(); + if (data == null) { + return; + } + String packageName = data.getSchemeSpecificPart(); + if (packageName == null) { + return; + } + final Computer snapshot = snapshotComputer(); + AndroidPackage pkg = snapshot.getPackage(packageName); + if (pkg == null) { + return; + } + mBroadcastHelper.sendPackageChangedBroadcast(snapshot, pkg.getPackageName(), + true /* dontKillApp */, + new ArrayList<>(Collections.singletonList(pkg.getPackageName())), + pkg.getUid(), + Intent.ACTION_OVERLAY_CHANGED); + } + }, overlayFilter); + + mModuleInfoProvider.systemReady(); + + // Installer service might attempt to install some packages that have been staged for + // installation on reboot. Make sure this is the last component to be call since the + // installation might require other components to be ready. + mInstallerService.restoreAndApplyStagedSessionIfNeeded(); + + mExistingPackages = null; + + // Clear cache on flags changes. + DeviceConfig.addOnPropertiesChangedListener( + NAMESPACE_PACKAGE_MANAGER_SERVICE, mInjector.getBackgroundExecutor(), + properties -> { + final Set keyset = properties.getKeyset(); + if (keyset.contains(PROPERTY_INCFS_DEFAULT_TIMEOUTS) || keyset.contains( + PROPERTY_KNOWN_DIGESTERS_LIST)) { + mPerUidReadTimeoutsCache = null; + } + }); + + if (!useArtService()) { + // The background dexopt job is scheduled in DexOptHelper.initializeArtManagerLocal when + // ART Service is in use. + try { + mBackgroundDexOptService.systemReady(); + } catch (LegacyDexoptDisabledException e) { + throw new RuntimeException(e); + } + } + + // Prune unused static shared libraries which have been cached a period of time + schedulePruneUnusedStaticSharedLibraries(false /* delay */); + + DexUseManagerLocal dexUseManager = DexOptHelper.getDexUseManagerLocal(); + if (dexUseManager != null) { + dexUseManager.systemReady(); + } + } + + //TODO: b/111402650 + private void disableSkuSpecificApps() { + String[] apkList = mContext.getResources().getStringArray( + R.array.config_disableApksUnlessMatchedSku_apk_list); + String[] skuArray = mContext.getResources().getStringArray( + R.array.config_disableApkUnlessMatchedSku_skus_list); + if (ArrayUtils.isEmpty(apkList)) { + return; + } + String sku = SystemProperties.get("ro.boot.hardware.sku"); + if (!TextUtils.isEmpty(sku) && ArrayUtils.contains(skuArray, sku)) { + return; + } + final Computer snapshot = snapshotComputer(); + for (String packageName : apkList) { + setSystemAppHiddenUntilInstalled(snapshot, packageName, true); + final List users = mInjector.getUserManagerInternal().getUsers(false); + for (int i = 0; i < users.size(); i++) { + setSystemAppInstallState(snapshot, packageName, false, users.get(i).id); + } + } + } + + public PackageFreezer freezePackage(String packageName, int userId, String killReason, + int exitInfoReason, InstallRequest request) { + return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason, request); + } + + public PackageFreezer freezePackageForDelete(String packageName, int userId, int deleteFlags, + String killReason, int exitInfoReason) { + if ((deleteFlags & PackageManager.DELETE_DONT_KILL_APP) != 0) { + return new PackageFreezer(this, null /* request */); + } else { + return freezePackage(packageName, userId, killReason, exitInfoReason, + null /* request */); + } + } + + /** Called by UserManagerService */ + void cleanUpUser(UserManagerService userManager, @UserIdInt int userId) { + synchronized (mLock) { + synchronized (mDirtyUsers) { + mDirtyUsers.remove(userId); + } + mUserNeedsBadging.delete(userId); + mDeletePackageHelper.removeUnusedPackagesLPw(userManager, userId); + mSettings.removeUserLPw(userId); + mPendingBroadcasts.remove(userId); + mAppsFilter.onUserDeleted(snapshotComputer(), userId); + mPermissionManager.onUserRemoved(userId); + } + mInstantAppRegistry.onUserRemoved(userId); + mPackageMonitorCallbackHelper.onUserRemoved(userId); + } + + /** + * Called by UserManagerService. + * + * @param userTypeInstallablePackages system packages that should be initially installed for + * this type of user, or {@code null} if all system packages + * should be installed + * @param disallowedPackages packages that should not be initially installed. Takes precedence + * over installablePackages. + */ + void createNewUser(int userId, @Nullable Set userTypeInstallablePackages, + String[] disallowedPackages) { + synchronized (mInstallLock) { + mSettings.createNewUserLI(this, mInstaller, userId, + userTypeInstallablePackages, disallowedPackages); + } + synchronized (mLock) { + scheduleWritePackageRestrictions(userId); + scheduleWritePackageList(userId); + mAppsFilter.onUserCreated(snapshotComputer(), userId); + } + } + + void onNewUserCreated(@UserIdInt int userId, boolean convertedFromPreCreated) { + if (DEBUG_PERMISSIONS) { + Slog.d(TAG, "onNewUserCreated(id=" + userId + + ", convertedFromPreCreated=" + convertedFromPreCreated + ")"); + } + if (!convertedFromPreCreated || !readPermissionStateForUser(userId)) { + mPermissionManager.onUserCreated(userId); + mLegacyPermissionManager.grantDefaultPermissions(userId); + mPermissionManager.setDefaultPermissionGrantFingerprint(Build.FINGERPRINT, userId); + mDomainVerificationManager.clearUser(userId); + } + } + + private boolean readPermissionStateForUser(@UserIdInt int userId) { + synchronized (mLock) { + mPermissionManager.writeLegacyPermissionStateTEMP(); + mSettings.readPermissionStateForUserSyncLPr(userId); + mPermissionManager.readLegacyPermissionStateTEMP(); + final boolean isPermissionUpgradeNeeded = !Objects.equals( + mPermissionManager.getDefaultPermissionGrantFingerprint(userId), + Build.FINGERPRINT); + return isPermissionUpgradeNeeded; + } + } + + public boolean isStorageLow() { + // allow instant applications + final long token = Binder.clearCallingIdentity(); + try { + final DeviceStorageMonitorInternal + dsm = mInjector.getLocalService(DeviceStorageMonitorInternal.class); + if (dsm != null) { + return dsm.isMemoryLow(); + } else { + return false; + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + private void deletePackageIfUnused(@NonNull Computer snapshot, final String packageName) { + PackageStateInternal ps = snapshot.getPackageStateInternal(packageName); + if (ps == null) { + return; + } + final SparseArray userStates = ps.getUserStates(); + for (int index = 0; index < userStates.size(); index++) { + if (userStates.valueAt(index).isInstalled()) { + return; + } + } + // 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(() -> mDeletePackageHelper.deletePackageX( + packageName, PackageManager.VERSION_CODE_HIGHEST, + 0, PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/)); + } + + void deletePreloadsFileCache() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CLEAR_APP_CACHE, + "deletePreloadsFileCache"); + File dir = Environment.getDataPreloadsFileCacheDirectory(); + Slog.i(PackageManagerService.TAG, "Deleting preloaded file cache " + dir); + FileUtils.deleteContents(dir); + } + + void setSystemAppHiddenUntilInstalled(@NonNull Computer snapshot, String packageName, + boolean hidden) { + final int callingUid = Binder.getCallingUid(); + final boolean calledFromSystemOrPhone = callingUid == Process.PHONE_UID + || callingUid == Process.SYSTEM_UID; + if (!calledFromSystemOrPhone) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, + "setSystemAppHiddenUntilInstalled"); + } + + final PackageStateInternal stateRead = snapshot.getPackageStateInternal(packageName); + if (stateRead == null || !stateRead.isSystem() || stateRead.getPkg() == null) { + return; + } + if (stateRead.getPkg().isCoreApp() && !calledFromSystemOrPhone) { + throw new SecurityException("Only system or phone callers can modify core apps"); + } + + commitPackageStateMutation(null, mutator -> { + mutator.forPackage(packageName) + .setHiddenUntilInstalled(hidden); + mutator.forDisabledSystemPackage(packageName) + .setHiddenUntilInstalled(hidden); + }); + } + + boolean setSystemAppInstallState(@NonNull Computer snapshot, String packageName, + boolean installed, int userId) { + final int callingUid = Binder.getCallingUid(); + final boolean calledFromSystemOrPhone = callingUid == Process.PHONE_UID + || callingUid == Process.SYSTEM_UID; + if (!calledFromSystemOrPhone) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, + "setSystemAppHiddenUntilInstalled"); + } + + final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); + // The target app should always be in system + if (packageState == null || !packageState.isSystem() || packageState.getPkg() == null) { + return false; + } + if (packageState.getPkg().isCoreApp() && !calledFromSystemOrPhone) { + throw new SecurityException("Only system or phone callers can modify core apps"); + } + // Check if the install state is the same + if (packageState.getUserStateOrDefault(userId).isInstalled() == installed) { + return false; + } + + final long callingId = Binder.clearCallingIdentity(); + try { + if (installed) { + // install the app from uninstalled state + mInstallPackageHelper.installExistingPackageAsUser( + packageName, + userId, + PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS, + PackageManager.INSTALL_REASON_DEVICE_SETUP, + null, + null); + return true; + } + + // uninstall the app from installed state + deletePackageVersioned( + new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST), + new PackageManager.LegacyPackageDeleteObserver(null).getBinder(), + userId, + PackageManager.DELETE_SYSTEM_APP); + return true; + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + void finishPackageInstall(int token, boolean didLaunch) { + PackageManagerServiceUtils.enforceSystemOrRoot( + "Only the system is allowed to finish installs"); + + if (PackageManagerService.DEBUG_INSTALL) { + Slog.v(PackageManagerService.TAG, "BM finishing package install for " + token); + } + Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "restore", token); + + final Message msg = mHandler.obtainMessage(PackageManagerService.POST_INSTALL, token, + didLaunch ? 1 : 0); + mHandler.sendMessage(msg); + } + + void checkPackageStartable(@NonNull Computer snapshot, @NonNull String packageName, + @UserIdInt int userId) { + final int callingUid = Binder.getCallingUid(); + if (snapshot.getInstantAppPackageName(callingUid) != null) { + throw new SecurityException("Instant applications don't have access to this method"); + } + if (!mUserManager.exists(userId)) { + throw new SecurityException("User doesn't exist"); + } + snapshot.enforceCrossUserPermission(callingUid, userId, false, false, + "checkPackageStartable"); + switch (snapshot.getPackageStartability(mSafeMode, packageName, callingUid, userId)) { + case PACKAGE_STARTABILITY_NOT_FOUND: + throw new SecurityException("Package " + packageName + " was not found!"); + case PACKAGE_STARTABILITY_NOT_SYSTEM: + throw new SecurityException("Package " + packageName + " not a system app!"); + case PACKAGE_STARTABILITY_FROZEN: + throw new SecurityException("Package " + packageName + " is currently frozen!"); + case PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED: + throw new SecurityException("Package " + packageName + " is not encryption aware!"); + case PACKAGE_STARTABILITY_OK: + default: + } + } + + void setPackageStoppedState(@NonNull Computer snapshot, @NonNull String packageName, + boolean stopped, @UserIdInt int userId) { + if (!mUserManager.exists(userId)) return; + final int callingUid = Binder.getCallingUid(); + boolean wasStopped = false; + if (snapshot.getInstantAppPackageName(callingUid) == null) { + final int permission = mContext.checkCallingOrSelfPermission( + Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); + final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); + if (!allowedByPermission + && !ArrayUtils.contains(snapshot.getPackagesForUid(callingUid), packageName)) { + throw new SecurityException( + "Permission Denial: attempt to change stopped state from pid=" + + Binder.getCallingPid() + + ", uid=" + callingUid + ", package=" + packageName); + } + snapshot.enforceCrossUserPermission(callingUid, userId, + true /* requireFullPermission */, true /* checkShell */, "stop package"); + + final PackageStateInternal packageState = + snapshot.getPackageStateForInstalledAndFiltered( + packageName, callingUid, userId); + final PackageUserState packageUserState = packageState == null + ? null : packageState.getUserStateOrDefault(userId); + if (packageState != null && packageUserState.isStopped() != stopped) { + boolean wasNotLaunched = packageUserState.isNotLaunched(); + wasStopped = packageUserState.isStopped(); + commitPackageStateMutation(null, packageName, state -> { + PackageUserStateWrite userState = state.userState(userId); + userState.setStopped(stopped); + if (wasNotLaunched) { + userState.setNotLaunched(false); + } + }); + + if (wasNotLaunched) { + final String installerPackageName = + packageState.getInstallSource().mInstallerPackageName; + if (installerPackageName != null) { + notifyFirstLaunch(packageName, installerPackageName, userId); + } + } + + scheduleWritePackageRestrictions(userId); + } + } + + // If this would cause the app to leave force-stop, then also make sure to unhibernate the + // app if needed. + if (!stopped) { + mHandler.post(() -> { + AppHibernationManagerInternal ah = + mInjector.getLocalService(AppHibernationManagerInternal.class); + if (ah != null && ah.isHibernatingForUser(packageName, userId)) { + ah.setHibernatingForUser(packageName, userId, false); + ah.setHibernatingGlobally(packageName, false); + } + }); + // Send UNSTOPPED broadcast if necessary + if (wasStopped && Flags.stayStopped()) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "unstoppedBroadcast"); + final PackageManagerInternal pmi = + mInjector.getLocalService(PackageManagerInternal.class); + final int [] userIds = resolveUserIds(userId); + final SparseArray broadcastAllowList = + snapshotComputer().getVisibilityAllowLists(packageName, userIds); + final Bundle extras = new Bundle(); + extras.putInt(Intent.EXTRA_UID, pmi.getPackageUid(packageName, 0, userId)); + extras.putInt(Intent.EXTRA_USER_HANDLE, userId); + extras.putLong(Intent.EXTRA_TIME, SystemClock.elapsedRealtime()); + mHandler.post(() -> { + mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_UNSTOPPED, + packageName, extras, + Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, + userIds, null, broadcastAllowList, null, + null); + }); + mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_UNSTOPPED, + packageName, extras, userIds, null /* instantUserIds */, + broadcastAllowList, mHandler, null /* filterExtras */); + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + } + + void notifyComponentUsed(@NonNull Computer snapshot, @NonNull String packageName, + @UserIdInt int userId, @Nullable String recentCallingPackage, + @NonNull String debugInfo) { + synchronized (mLock) { + final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName); + // If the package doesn't exist, don't need to proceed to setPackageStoppedState. + if (pkgSetting == null) { + return; + } + if (pkgSetting.getUserStateOrDefault(userId).isQuarantined()) { + Slog.i(TAG, + "Component is quarantined+suspended but being used: " + + packageName + " by " + recentCallingPackage + ", debugInfo: " + + debugInfo); + } + } + PackageManagerService.this + .setPackageStoppedState(snapshot, packageName, false /* stopped */, + userId); + } + + public class IPackageManagerImpl extends IPackageManagerBase { + + public IPackageManagerImpl() { + super(PackageManagerService.this, mContext, mDexOptHelper, mModuleInfoProvider, + mPreferredActivityHelper, mResolveIntentHelper, mDomainVerificationManager, + mDomainVerificationConnection, mInstallerService, mPackageProperty, + mResolveComponentName, mInstantAppResolverSettingsComponent, + mServicesExtensionPackageName, mSharedSystemSharedLibraryPackageName); + } + + @Override + public void checkPackageStartable(String packageName, int userId) { + PackageManagerService.this + .checkPackageStartable(snapshotComputer(), packageName, userId); + } + + @Override + public void clearApplicationProfileData(String packageName) { + PackageManagerServiceUtils.enforceSystemOrRootOrShell( + "Only the system or shell can clear all profile data"); + + final Computer snapshot = snapshotComputer(); + final AndroidPackage pkg = snapshot.getPackage(packageName); + try (PackageFreezer ignored = + freezePackage(packageName, UserHandle.USER_ALL, + "clearApplicationProfileData", + ApplicationExitInfo.REASON_OTHER, null /* request */)) { + synchronized (mInstallLock) { + mAppDataHelper.clearAppProfilesLIF(pkg); + } + } + } + + @android.annotation.EnforcePermission(android.Manifest.permission.CLEAR_APP_USER_DATA) + @Override + public void clearApplicationUserData(final String packageName, + final IPackageDataObserver observer, final int userId) { + clearApplicationUserData_enforcePermission(); + + final int callingUid = Binder.getCallingUid(); + final Computer snapshot = snapshotComputer(); + snapshot.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, + false /* checkShell */, "clear application data"); + + if (snapshot.getPackageStateForInstalledAndFiltered( + packageName, callingUid, userId) == null) { + if (observer != null) { + mHandler.post(() -> { + try { + observer.onRemoveCompleted(packageName, false); + } catch (RemoteException e) { + Log.i(TAG, "Observer no longer exists."); + } + }); + } + return; + } + if (mProtectedPackages.isPackageDataProtected(userId, packageName)) { + throw new SecurityException("Cannot clear data for a protected package: " + + packageName); + } + final int callingPid = Binder.getCallingPid(); + EventLog.writeEvent(EventLogTags.PM_CLEAR_APP_DATA_CALLER, callingPid, callingUid, + 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; + try (PackageFreezer freezer = freezePackage(packageName, UserHandle.USER_ALL, + "clearApplicationUserData", + ApplicationExitInfo.REASON_USER_REQUESTED, null /* request */)) { + synchronized (mInstallLock) { + succeeded = clearApplicationUserDataLIF(snapshotComputer(), packageName, + userId); + } + mInstantAppRegistry.deleteInstantApplicationMetadata(packageName, userId); + synchronized (mLock) { + if (succeeded) { + resetComponentEnabledSettingsIfNeededLPw(packageName, userId); + } + } + } + if (succeeded) { + // invoke DeviceStorageMonitor's update method to clear any notifications + DeviceStorageMonitorInternal dsm = LocalServices + .getService(DeviceStorageMonitorInternal.class); + if (dsm != null) { + dsm.checkMemory(); + } + if (checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId) + == PERMISSION_GRANTED) { + final Computer snapshot = snapshotComputer(); + unsuspendForSuspendingPackage(snapshot, packageName, userId); + removeAllDistractingPackageRestrictions(snapshot, userId); + synchronized (mLock) { + flushPackageRestrictionsAsUserInternalLocked(userId); + } + } + } + if (observer != null) { + try { + observer.onRemoveCompleted(packageName, succeeded); + } catch (RemoteException e) { + Log.i(TAG, "Observer no longer exists."); + } + } //end if observer + } //end run + }); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + @Override + public void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage) { + clearCrossProfileIntentFilters_enforcePermission(); + final int callingUid = Binder.getCallingUid(); + final Computer snapshot = snapshotComputer(); + enforceOwnerRights(snapshot, ownerPackage, callingUid); + PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(), + UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId); + PackageManagerService.this.mInjector.getCrossProfileIntentFilterHelper() + .clearCrossProfileIntentFilters(sourceUserId, ownerPackage, + null); + scheduleWritePackageRestrictions(sourceUserId); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + @Override + public boolean removeCrossProfileIntentFilter(IntentFilter intentFilter, + String ownerPackage, + int sourceUserId, + int targetUserId, int flags) { + removeCrossProfileIntentFilter_enforcePermission(); + final int callingUid = Binder.getCallingUid(); + enforceOwnerRights(snapshotComputer(), ownerPackage, callingUid); + mUserManager.enforceCrossProfileIntentFilterAccess(sourceUserId, targetUserId, + callingUid, /* addCrossProfileIntentFilter */ false); + PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(), + UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId); + + boolean removedMatchingFilter = false; + synchronized (mLock) { + CrossProfileIntentResolver resolver = + mSettings.editCrossProfileIntentResolverLPw(sourceUserId); + + ArraySet set = + new ArraySet<>(resolver.filterSet()); + for (int i = 0; i < set.size(); i++) { + final CrossProfileIntentFilter filter = set.valueAt(i); + if (IntentFilter.filterEquals(filter.mFilter, intentFilter) + && filter.getOwnerPackage().equals(ownerPackage) + && filter.getTargetUserId() == targetUserId + && filter.getFlags() == flags) { + resolver.removeFilter(filter); + removedMatchingFilter = true; + break; + } + } + } + if (removedMatchingFilter) { + scheduleWritePackageRestrictions(sourceUserId); + } + return removedMatchingFilter; + } + + @Override + public final void deleteApplicationCacheFiles(final String packageName, + final IPackageDataObserver observer) { + final int userId = UserHandle.getCallingUserId(); + deleteApplicationCacheFilesAsUser(packageName, userId, observer); + } + + @Override + public void deleteApplicationCacheFilesAsUser(final String packageName, final int userId, + final IPackageDataObserver observer) { + final int callingUid = Binder.getCallingUid(); + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES) + != PackageManager.PERMISSION_GRANTED) { + // If the caller has the old delete cache permission, silently ignore. Else throw. + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.DELETE_CACHE_FILES) + == PackageManager.PERMISSION_GRANTED) { + Slog.w(TAG, "Calling uid " + callingUid + " does not have " + + android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES + + ", silently ignoring"); + return; + } + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES, null); + } + final Computer snapshot = snapshotComputer(); + snapshot.enforceCrossUserPermission(callingUid, userId, /* requireFullPermission= */ true, + /* checkShell= */ false, "delete application cache files"); + final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission( + android.Manifest.permission.ACCESS_INSTANT_APPS); + final int callingPid = Binder.getCallingPid(); + EventLog.writeEvent(EventLogTags.PM_CLEAR_APP_DATA_CALLER, callingPid, callingUid, + packageName); + + // Queue up an async operation since the package deletion may take a little while. + mHandler.post(() -> { + // Snapshot in the Handler Runnable since this may be deferred quite a bit + // TODO: Is this and the later mInstallLock re-snapshot necessary? + final Computer newSnapshot = snapshotComputer(); + final PackageStateInternal ps = newSnapshot.getPackageStateInternal(packageName); + boolean doClearData = true; + if (ps != null) { + final boolean targetIsInstantApp = + ps.getUserStateOrDefault(UserHandle.getUserId(callingUid)).isInstantApp(); + doClearData = !targetIsInstantApp + || hasAccessInstantApps == PackageManager.PERMISSION_GRANTED; + } + if (doClearData) { + synchronized (mInstallLock) { + final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL; + // Snapshot again after mInstallLock? + final AndroidPackage pkg = snapshotComputer().getPackage(packageName); + // We're only clearing cache files, so we don't care if the + // app is unfrozen and still able to run + mAppDataHelper.clearAppDataLIF(pkg, userId, + flags | Installer.FLAG_CLEAR_CACHE_ONLY); + mAppDataHelper.clearAppDataLIF(pkg, userId, + flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); + } + } + if (observer != null) { + try { + observer.onRemoveCompleted(packageName, true); + } catch (RemoteException e) { + Log.i(TAG, "Observer no longer exists."); + } + } + }); + } + + @Override + public void enterSafeMode() { + PackageManagerServiceUtils.enforceSystemOrRoot( + "Only the system can request entering safe mode"); + + if (!mSystemReady) { + mSafeMode = true; + } + } + + @Override + public void extendVerificationTimeout(int verificationId, int verificationCodeAtTimeout, + long millisecondsToDelay) { + // Negative ids correspond to testing verifiers and will be silently enforced in + // the handler thread. + if (verificationId >= 0) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.PACKAGE_VERIFICATION_AGENT, + "Only package verification agents can extend verification timeouts"); + } + final int callingUid = Binder.getCallingUid(); + + mHandler.post(() -> { + final int id = verificationId >= 0 ? verificationId : -verificationId; + final PackageVerificationState state = mPendingVerification.get(id); + if (state == null || !state.extendTimeout(callingUid)) { + // Invalid uid or already extended. + return; + } + + final PackageVerificationResponse response = new PackageVerificationResponse( + verificationCodeAtTimeout, callingUid); + + long delay = millisecondsToDelay; + if (delay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT) { + delay = PackageManager.MAXIMUM_VERIFICATION_TIMEOUT; + } + if (delay < 0) { + delay = 0; + } + + final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED); + msg.arg1 = id; + msg.obj = response; + mHandler.sendMessageDelayed(msg, delay); + }); + } + + @WorkerThread + @Override + public void flushPackageRestrictionsAsUser(int userId) { + final Computer snapshot = snapshotComputer(); + final int callingUid = Binder.getCallingUid(); + if (snapshot.getInstantAppPackageName(callingUid) != null) { + return; + } + if (!mUserManager.exists(userId)) { + return; + } + snapshot.enforceCrossUserPermission(callingUid, userId, + false /* requireFullPermission*/, false /* checkShell */, + "flushPackageRestrictions"); + synchronized (mLock) { + flushPackageRestrictionsAsUserInternalLocked(userId); + } + } + + + @android.annotation.EnforcePermission(android.Manifest.permission.CLEAR_APP_CACHE) + @Override + public void freeStorage(final String volumeUuid, final long freeStorageSize, + final @StorageManager.AllocateFlags int flags, final IntentSender pi) { + freeStorage_enforcePermission(); + mHandler.post(() -> { + boolean success = false; + try { + PackageManagerService.this.freeStorage(volumeUuid, freeStorageSize, flags); + success = true; + } catch (IOException e) { + Slog.w(TAG, e); + } + if (pi != null) { + try { + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setPendingIntentBackgroundActivityLaunchAllowed(false); + pi.sendIntent(null, success ? 1 : 0, null /* intent */, + null /* onFinished*/, null /* handler */, + null /* requiredPermission */, options.toBundle()); + } catch (SendIntentException e) { + Slog.w(TAG, e); + } + } + }); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.CLEAR_APP_CACHE) + @Override + public void freeStorageAndNotify(final String volumeUuid, final long freeStorageSize, + final @StorageManager.AllocateFlags int flags, final IPackageDataObserver observer) { + freeStorageAndNotify_enforcePermission(); + mHandler.post(() -> { + boolean success = false; + try { + PackageManagerService.this.freeStorage(volumeUuid, freeStorageSize, flags); + success = true; + } catch (IOException e) { + Slog.w(PackageManagerService.TAG, e); + } + if (observer != null) { + try { + observer.onRemoveCompleted(null, success); + } catch (RemoteException e) { + Slog.w(PackageManagerService.TAG, e); + } + } + }); + } + + @Override + public ChangedPackages getChangedPackages(int sequenceNumber, int userId) { + final int callingUid = Binder.getCallingUid(); + final Computer snapshot = snapshotComputer(); + if (snapshot.getInstantAppPackageName(callingUid) != null) { + return null; + } + if (!mUserManager.exists(userId)) { + return null; + } + snapshot.enforceCrossUserPermission(callingUid, userId, false, false, + "getChangedPackages"); + final ChangedPackages changedPackages = mChangedPackagesTracker.getChangedPackages( + sequenceNumber, userId); + + if (changedPackages != null) { + final List packageNames = changedPackages.getPackageNames(); + for (int index = packageNames.size() - 1; index >= 0; index--) { + // Filter out the changes if the calling package should not be able to see it. + final PackageStateInternal packageState = + snapshot.getPackageStateInternal(packageNames.get(index)); + if (snapshot.shouldFilterApplication(packageState, callingUid, userId)) { + packageNames.remove(index); + } + } + } + + return changedPackages; + } + + @Override + public byte[] getDomainVerificationBackup(int userId) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Only the system may call getDomainVerificationBackup()"); + } + + try { + try (ByteArrayOutputStream output = new ByteArrayOutputStream()) { + TypedXmlSerializer serializer = Xml.resolveSerializer(output); + mDomainVerificationManager.writeSettings(snapshotComputer(), serializer, true, + userId); + return output.toByteArray(); + } + } catch (Exception e) { + if (PackageManagerService.DEBUG_BACKUP) { + Slog.e(PackageManagerService.TAG, "Unable to write domain verification for backup", e); + } + return null; + } + } + + @Override + public IBinder getHoldLockToken() { + if (!Build.IS_DEBUGGABLE) { + throw new SecurityException("getHoldLockToken requires a debuggable build"); + } + + mContext.enforceCallingPermission( + Manifest.permission.INJECT_EVENTS, + "getHoldLockToken requires INJECT_EVENTS permission"); + + final Binder token = new Binder(); + token.attachInterface(this, "holdLock:" + Binder.getCallingUid()); + return token; + } + + @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_INSTANT_APPS) + @Override + public String getInstantAppAndroidId(String packageName, int userId) { + getInstantAppAndroidId_enforcePermission(); + final Computer snapshot = snapshotComputer(); + snapshot.enforceCrossUserPermission(Binder.getCallingUid(), userId, + true /* requireFullPermission */, false /* checkShell */, + "getInstantAppAndroidId"); + // Make sure the target is an Instant App. + if (!snapshot.isInstantApp(packageName, userId)) { + return null; + } + return mInstantAppRegistry.getInstantAppAndroidId(packageName, userId); + } + + @Override + public byte[] getInstantAppCookie(String packageName, int userId) { + if (HIDE_EPHEMERAL_APIS) { + return null; + } + + final Computer snapshot = snapshotComputer(); + snapshot.enforceCrossUserPermission(Binder.getCallingUid(), userId, + true /* requireFullPermission */, false /* checkShell */, + "getInstantAppCookie"); + if (!snapshot.isCallerSameApp(packageName, Binder.getCallingUid())) { + return null; + } + PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); + if (packageState == null || packageState.getPkg() == null) { + return null; + } + return mInstantAppRegistry.getInstantAppCookie(packageState.getPkg(), userId); + } + + @Override + public Bitmap getInstantAppIcon(String packageName, int userId) { + if (HIDE_EPHEMERAL_APIS) { + return null; + } + + final Computer snapshot = snapshotComputer(); + if (!snapshot.canViewInstantApps(Binder.getCallingUid(), userId)) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, + "getInstantAppIcon"); + } + snapshot.enforceCrossUserPermission(Binder.getCallingUid(), userId, + true /* requireFullPermission */, false /* checkShell */, + "getInstantAppIcon"); + + return mInstantAppRegistry.getInstantAppIcon(packageName, userId); + } + + @Override + public ParceledListSlice getInstantApps(int userId) { + if (HIDE_EPHEMERAL_APIS) { + return null; + } + + final Computer snapshot = snapshotComputer(); + if (!snapshot.canViewInstantApps(Binder.getCallingUid(), userId)) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, + "getEphemeralApplications"); + } + snapshot.enforceCrossUserPermission(Binder.getCallingUid(), userId, + true /* requireFullPermission */, false /* checkShell */, + "getEphemeralApplications"); + + List instantApps = mInstantAppRegistry.getInstantApps(snapshot, userId); + if (instantApps != null) { + return new ParceledListSlice<>(instantApps); + } + return null; + } + + @Override + public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags) { + return mPreferredActivityHelper.getLastChosenActivity(snapshotComputer(), intent, + resolvedType, flags); + } + + @Override + public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage, + String featureId, int userId) throws RemoteException { + return mResolveIntentHelper.getLaunchIntentSenderForPackage(snapshotComputer(), + packageName, callingPackage, featureId, userId); + } + + @Override + public List getMimeGroup(String packageName, String mimeGroup) { + final Computer snapshot = snapshotComputer(); + enforceOwnerRights(snapshot, packageName, Binder.getCallingUid()); + return getMimeGroupInternal(snapshot, packageName, mimeGroup); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) + @Override + public int getMoveStatus(int moveId) { + getMoveStatus_enforcePermission(); + return mMoveCallbacks.mLastStatus.get(moveId); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.GET_APP_METADATA) + @Override + public ParcelFileDescriptor getAppMetadataFd(String packageName, int userId) { + getAppMetadataFd_enforcePermission(); + final int callingUid = Binder.getCallingUid(); + final Computer snapshot = snapshotComputer(); + final PackageStateInternal ps = snapshot.getPackageStateForInstalledAndFiltered( + packageName, callingUid, userId); + if (ps == null) { + throw new ParcelableException( + new PackageManager.NameNotFoundException(packageName)); + } + String filePath = ps.getAppMetadataFilePath(); + if (filePath == null) { + return null; + } + File file = new File(filePath); + if (Flags.aslInApkAppMetadataSource() && !file.exists() + && ps.getAppMetadataSource() == APP_METADATA_SOURCE_APK) { + String apkPath = ps.getPkg().getSplits().get(0).getPath(); + if (!PackageManagerServiceUtils.extractAppMetadataFromApk(apkPath, file)) { + if (file.exists()) { + file.delete(); + } + synchronized (mLock) { + PackageSetting pkgSetting = mSettings.getPackageLPr(packageName); + pkgSetting.setAppMetadataFilePath(null); + pkgSetting.setAppMetadataSource(APP_METADATA_SOURCE_UNKNOWN); + } + return null; + } + } + try { + return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); + } catch (FileNotFoundException e) { + return null; + } + } + + @android.annotation.EnforcePermission(android.Manifest.permission.GET_APP_METADATA) + @Override + public int getAppMetadataSource(String packageName, int userId) { + getAppMetadataSource_enforcePermission(); + final int callingUid = Binder.getCallingUid(); + final Computer snapshot = snapshotComputer(); + final PackageStateInternal ps = snapshot.getPackageStateForInstalledAndFiltered( + packageName, callingUid, userId); + if (ps == null) { + throw new ParcelableException( + new PackageManager.NameNotFoundException(packageName)); + } + return ps.getAppMetadataSource(); + } + + @Override + public String getPermissionControllerPackageName() { + final int callingUid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getUserId(callingUid); + final Computer snapshot = snapshotComputer(); + if (snapshot.getPackageStateForInstalledAndFiltered( + mRequiredPermissionControllerPackage, callingUid, callingUserId) != null) { + return mRequiredPermissionControllerPackage; + } + + throw new IllegalStateException("PermissionController is not found"); + } + + @Override + @SuppressWarnings("GuardedBy") + public int getRuntimePermissionsVersion(@UserIdInt int userId) { + Preconditions.checkArgumentNonnegative(userId); + enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions( + "getRuntimePermissionVersion"); + return mSettings.getDefaultRuntimePermissionsVersion(userId); + } + + @Override + public String getSplashScreenTheme(@NonNull String packageName, int userId) { + final Computer snapshot = snapshotComputer(); + final int callingUid = Binder.getCallingUid(); + snapshot.enforceCrossUserPermission( + callingUid, userId, false /* requireFullPermission */, + false /* checkShell */, "getSplashScreenTheme"); + PackageStateInternal packageState = snapshot.getPackageStateForInstalledAndFiltered( + packageName, callingUid, userId); + return packageState == null ? null + : packageState.getUserStateOrDefault(userId).getSplashScreenTheme(); + } + + @Override + @PackageManager.UserMinAspectRatio + public int getUserMinAspectRatio(@NonNull String packageName, int userId) { + final Computer snapshot = snapshotComputer(); + final int callingUid = Binder.getCallingUid(); + final PackageStateInternal packageState = snapshot + .getPackageStateForInstalledAndFiltered(packageName, callingUid, userId); + return packageState == null ? USER_MIN_ASPECT_RATIO_UNSET + : packageState.getUserStateOrDefault(userId).getMinAspectRatio(); + } + + @Override + public Bundle getSuspendedPackageAppExtras(String packageName, int userId) { + final int callingUid = Binder.getCallingUid(); + final Computer snapshot = snapshot(); + if (snapshot.getPackageUid(packageName, 0, userId) != callingUid) { + throw new SecurityException("Calling package " + packageName + + " does not belong to calling uid " + callingUid); + } + return SuspendPackageHelper + .getSuspendedPackageAppExtras(snapshot, packageName, userId, callingUid); + } + + @Override + public String getSuspendingPackage(String packageName, int userId) { + try { + final int callingUid = Binder.getCallingUid(); + final Computer snapshot = snapshot(); + // This will do visibility checks as well. + if (!snapshot.isPackageSuspendedForUser(packageName, userId)) { + return null; + } + final UserPackage suspender = mSuspendPackageHelper.getSuspendingPackage( + snapshot, packageName, userId, callingUid); + return suspender != null ? suspender.packageName : null; + } catch (PackageManager.NameNotFoundException e) { + return null; + } + } + + @Override + public @NonNull ParceledListSlice getSystemAvailableFeatures() { + // allow instant applications + ArrayList res; + res = new ArrayList<>(mAvailableFeatures.size() + 1); + res.addAll(mAvailableFeatures.values()); + final FeatureInfo fi = new FeatureInfo(); + fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version", + FeatureInfo.GL_ES_VERSION_UNDEFINED); + res.add(fi); + + return new ParceledListSlice<>(res); + } + + @Override + public @NonNull List getInitialNonStoppedSystemPackages() { + return mInitialNonStoppedSystemPackages != null + ? new ArrayList<>(mInitialNonStoppedSystemPackages) : new ArrayList<>(); + } + + @Override + public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) { + Objects.requireNonNull(packageNames, "packageNames cannot be null"); + mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, + "getUnsuspendablePackagesForUser"); + final int callingUid = Binder.getCallingUid(); + if (UserHandle.getUserId(callingUid) != userId) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "Calling uid " + callingUid + + " cannot query getUnsuspendablePackagesForUser for user " + + userId); + } + return mSuspendPackageHelper.getUnsuspendablePackagesForUser(snapshotComputer(), + packageNames, userId, callingUid); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT) + @Override + public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException { + getVerifierDeviceIdentity_enforcePermission(); + + synchronized (mLock) { + return mSettings.getVerifierDeviceIdentityLPw(mLiveComputer); + } + } + + @Override + public void makeProviderVisible(int recipientUid, @NonNull String visibleAuthority) { + final Computer snapshot = snapshotComputer(); + final int recipientUserId = UserHandle.getUserId(recipientUid); + final ProviderInfo providerInfo = + snapshot.getGrantImplicitAccessProviderInfo(recipientUid, visibleAuthority); + if (providerInfo == null) { + return; + } + int visibleUid = providerInfo.applicationInfo.uid; + PackageManagerService.this.grantImplicitAccess(snapshot, recipientUserId, + null /*Intent*/, UserHandle.getAppId(recipientUid), visibleUid, + false /*direct*/, false /* retainOnUpdate */); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.MAKE_UID_VISIBLE) + @Override + public void makeUidVisible(int recipientUid, int visibleUid) { + makeUidVisible_enforcePermission(); + final int callingUid = Binder.getCallingUid(); + final int recipientUserId = UserHandle.getUserId(recipientUid); + final int visibleUserId = UserHandle.getUserId(visibleUid); + final Computer snapshot = snapshotComputer(); + snapshot.enforceCrossUserPermission(callingUid, recipientUserId, + false /* requireFullPermission */, false /* checkShell */, "makeUidVisible"); + snapshot.enforceCrossUserPermission(callingUid, visibleUserId, + false /* requireFullPermission */, false /* checkShell */, "makeUidVisible"); + snapshot.enforceCrossUserPermission(recipientUid, visibleUserId, + false /* requireFullPermission */, false /* checkShell */, "makeUidVisible"); + + PackageManagerService.this.grantImplicitAccess(snapshot, recipientUserId, + null /*Intent*/, UserHandle.getAppId(recipientUid), visibleUid, + false /*direct*/, false /* retainOnUpdate */); + } + + @Override + public void holdLock(IBinder token, int durationMs) { + mTestUtilityService.verifyHoldLockToken(token); + + synchronized (mLock) { + SystemClock.sleep(durationMs); + } + } + + /** + * @hide + */ + @Override + public int installExistingPackageAsUser(String packageName, int userId, int installFlags, + int installReason, List whiteListedPermissions) { + return mInstallPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags, + installReason, whiteListedPermissions, null).first; + } + + private AndroidPackage scanAndInstallPackage(File packageFile) { + AndroidPackage p = null; + try { + PackageParser.PackageLite pkgLite = PackageParser.parsePackageLite(packageFile, 0); + String packageName = pkgLite.packageName; + // already installed package + if (mPackages.containsKey(packageName)) { + Slog.i(TAG, "package " + packageName + " has been installed."); + return null; + } + + try (PackageFreezer freezer = freezePackage(packageName, UserHandle.USER_ALL, + "scanAndInstallPackage", ApplicationExitInfo.REASON_OTHER, null)) { + // Scan the APK, and the information of a package contains 1 basic APK and 0 or more split APKs. + p = mInstallPackageHelper.initPackageTracedLI(packageFile, + PackageParser.PARSE_MUST_BE_APK, SCAN_FAST_COMMAND); + } catch (PackageManagerException e) { + Slog.w(TAG, "Failed to scan " + packageFile + ": " + e.getMessage()); + return null; + } + + if (!mPackages.containsKey(packageName)) { + Slog.e(TAG, "Failed to scan " + packageFile + ", mPackages has no " + packageName); + return null; + } + + AndroidPackage pkg = mPackages.get(packageName); + PackageSetting pkgSetting = mSettings.getPackageLPr(packageName); + try { + // Shared libraries for the package need to be updated. + mSharedLibraries.updateSharedLibraries(pkg, pkgSetting, null, null, + Collections.unmodifiableMap(mPackages)); + } catch (PackageManagerException e) { + Slog.e(TAG, "updateSharedLibraries failed: " + e.getMessage()); + } + + // Create app data directory + mAppDataHelper.prepareAppDataAfterInstallLIF(pkg); + ApplicationInfo applicationInfo = snapshot().getApplicationInfo(packageName, 0, + UserHandle.getUserId(pkgSetting.getAppId())); + if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) { + // 赋予运行时权限等 + mPermissionManager.onPackageInstalled(pkg, Process.INVALID_UID, + PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT, UserHandle.USER_ALL); + } + synchronized (mPackages) { + writeSettingsLPrTEMP(); + } + + // 发送系统广播 + mHandler.post(() -> { + mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, pkgLite.packageName, null, 0, + null, null, null, null, null, null, null); + }); + mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, + pkgLite.packageName, null, null, null /* instantUserIds */, null, mHandler, null); + } catch (PackageParser.PackageParserException e) { + return null; + } + return p; + } + + @Override + public void scanFast(String path) { + File apkPath = new File(path); + if (!apkPath.exists()) { + return; + } + + // If the path is a file rather than a directory, + // then determine whether the file is an apk file. + // If so, scan and install. + if (apkPath.isDirectory()) { + File files[] = apkPath.listFiles(); + for (File f : files) { + scanAndInstallPackage(f); + } + } else if (isApkFile(apkPath)) { + scanAndInstallPackage(apkPath); + } + } + + @Override + public boolean isAutoRevokeWhitelisted(String packageName) { + int mode = mInjector.getSystemService(AppOpsManager.class).checkOpNoThrow( + AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, + Binder.getCallingUid(), packageName); + return mode == MODE_IGNORED; + } + + @Override + public boolean isPackageStateProtected(@NonNull String packageName, @UserIdInt int userId) { + final int callingUid = Binder.getCallingUid(); + final int callingAppId = UserHandle.getAppId(callingUid); + + final Computer snapshot = snapshotComputer(); + snapshot.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, + true /*checkShell*/, "isPackageStateProtected"); + + if (!PackageManagerServiceUtils.isSystemOrRoot(callingAppId) + && snapshot.checkUidPermission(MANAGE_DEVICE_ADMINS, callingUid) + != PERMISSION_GRANTED) { + throw new SecurityException("Caller must have the " + + MANAGE_DEVICE_ADMINS + " permission."); + } + + return mProtectedPackages.isPackageStateProtected(userId, packageName); + } + + @Override + public boolean isProtectedBroadcast(String actionName) { + 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; + } + } + // allow instant applications + synchronized (mProtectedBroadcasts) { + return mProtectedBroadcasts.contains(actionName); + } + } + + /** + * Logs process start information (including base APK hash) to the security log. + * @hide + */ + @Override + public void logAppProcessStartIfNeeded(String packageName, String processName, int uid, + String seinfo, String apkFile, int pid) { + final Computer snapshot = snapshotComputer(); + if (snapshot.getInstantAppPackageName(Binder.getCallingUid()) != null) { + return; + } + if (!SecurityLog.isLoggingEnabled()) { + return; + } + mProcessLoggingHandler.logAppProcessStart(mContext, + LocalServices.getService(PackageManagerInternal.class), apkFile, packageName, + processName, uid, seinfo, pid); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.MOVE_PACKAGE) + @Override + public int movePackage(final String packageName, final String volumeUuid) { + movePackage_enforcePermission(); + + final int callingUid = Binder.getCallingUid(); + final UserHandle user = new UserHandle(UserHandle.getUserId(callingUid)); + final int moveId = mNextMoveId.getAndIncrement(); + mHandler.post(() -> { + try { + MovePackageHelper movePackageHelper = + new MovePackageHelper(PackageManagerService.this); + movePackageHelper.movePackageInternal( + packageName, volumeUuid, moveId, callingUid, user); + } catch (PackageManagerException e) { + Slog.w(PackageManagerService.TAG, "Failed to move " + packageName, e); + mMoveCallbacks.notifyStatusChanged(moveId, e.error); + } + }); + return moveId; + } + + @android.annotation.EnforcePermission(android.Manifest.permission.MOVE_PACKAGE) + @Override + public int movePrimaryStorage(String volumeUuid) throws RemoteException { + movePrimaryStorage_enforcePermission(); + + final int realMoveId = mNextMoveId.getAndIncrement(); + final Bundle extras = new Bundle(); + extras.putString(VolumeRecord.EXTRA_FS_UUID, volumeUuid); + mMoveCallbacks.notifyCreated(realMoveId, extras); + + final IPackageMoveObserver callback = new IPackageMoveObserver.Stub() { + @Override + public void onCreated(int moveId, Bundle extras) { + // Ignored + } + + @Override + public void onStatusChanged(int moveId, int status, long estMillis) { + mMoveCallbacks.notifyStatusChanged(realMoveId, status, estMillis); + } + }; + + final StorageManager storage = mInjector.getSystemService(StorageManager.class); + storage.setPrimaryStorageUuid(volumeUuid, callback); + return realMoveId; + } + + @Override + public void notifyDexLoad(String loadingPackageName, + Map classLoaderContextMap, + String loaderIsa) { + int callingUid = Binder.getCallingUid(); + Computer snapshot = snapshot(); + + // System server should be able to report dex load on behalf of other apps. E.g., it + // could potentially resend the notifications in order to migrate the existing dex load + // info to ART Service. + if (!PackageManagerServiceUtils.isSystemOrRoot() + && !snapshot.isCallerSameApp( + loadingPackageName, callingUid, true /* resolveIsolatedUid */)) { + Slog.w(PackageManagerService.TAG, + TextUtils.formatSimple( + "Invalid dex load report. loadingPackageName=%s, uid=%d", + loadingPackageName, callingUid)); + return; + } + + UserHandle user = Binder.getCallingUserHandle(); + int userId = user.getIdentifier(); + + // Proxy the call to either ART Service or the legacy implementation. If the + // implementation is switched with the system property, the dex usage info will be + // incomplete, with these effects: + // + // - Shared dex files may temporarily get compiled for private use. + // - Secondary dex files may not get compiled at all. + // - Stale compiled artifacts for secondary dex files may not get cleaned up. + // + // This recovers in the first background dexopt after the depending apps have been + // loaded for the first time. + + DexUseManagerLocal dexUseManager = DexOptHelper.getDexUseManagerLocal(); + if (dexUseManager != null) { + // TODO(chiuwinson): Retrieve filtered snapshot from Computer instance instead. + try (PackageManagerLocal.FilteredSnapshot filteredSnapshot = + LocalManagerRegistry.getManager(PackageManagerLocal.class) + .withFilteredSnapshot(callingUid, user)) { + if (loaderIsa != null) { + // Check that loaderIsa agrees with the ISA that dexUseManager will + // determine. + PackageState loadingPkgState = + filteredSnapshot.getPackageState(loadingPackageName); + // If we don't find the loading package just pass it through and let + // dexUseManager throw on it. + if (loadingPkgState != null) { + String loadingPkgAbi = loadingPkgState.getPrimaryCpuAbi(); + if (loadingPkgAbi == null) { + loadingPkgAbi = Build.SUPPORTED_ABIS[0]; + } + String loadingPkgDexCodeIsa = InstructionSets.getDexCodeInstructionSet( + VMRuntime.getInstructionSet(loadingPkgAbi)); + if (!loaderIsa.equals(loadingPkgDexCodeIsa)) { + // TODO(b/251903639): We make this a wtf to surface any situations + // where this argument doesn't correspond to our expectations. Later + // it should be turned into an IllegalArgumentException, when we can + // assume it's the caller that's wrong rather than us. + Log.wtf(TAG, + "Invalid loaderIsa in notifyDexLoad call from " + + loadingPackageName + ", uid " + callingUid + + ": expected " + loadingPkgDexCodeIsa + ", got " + + loaderIsa); + return; + } + } + } + + // This is called from binder, so exceptions thrown here are caught and handled + // by it. + dexUseManager.notifyDexContainersLoaded( + filteredSnapshot, loadingPackageName, classLoaderContextMap); + } + } else { + ApplicationInfo ai = + snapshot.getApplicationInfo(loadingPackageName, /*flags*/ 0, userId); + if (ai == null) { + Slog.w(PackageManagerService.TAG, + "Loading a package that does not exist for the calling user. package=" + + loadingPackageName + ", user=" + userId); + return; + } + mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId, + Process.isIsolated(callingUid)); + } + } + + @Override + public void notifyPackageUse(String packageName, int reason) { + final int callingUid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getUserId(callingUid); + Computer snapshot = snapshotComputer(); + final boolean notify; + if (snapshot.getInstantAppPackageName(callingUid) != null) { + notify = snapshot.isCallerSameApp(packageName, callingUid); + } else { + notify = !snapshot.isInstantAppInternal(packageName, callingUserId, + Process.SYSTEM_UID); + } + if (!notify) { + return; + } + + notifyPackageUseInternal(packageName, reason); + } + + @Override + public void overrideLabelAndIcon(@NonNull ComponentName componentName, + @NonNull String nonLocalizedLabel, int icon, int userId) { + if (TextUtils.isEmpty(nonLocalizedLabel)) { + throw new IllegalArgumentException("Override label should be a valid String"); + } + updateComponentLabelIcon(componentName, nonLocalizedLabel, icon, userId); + } + + @Override + public ParceledListSlice queryProperty( + String propertyName, @PackageManager.PropertyLocation int componentType) { + Objects.requireNonNull(propertyName); + final int callingUid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getCallingUserId(); + final Computer snapshot = snapshotComputer(); + final List result = + mPackageProperty.queryProperty(propertyName, componentType, + packageName -> snapshot.getPackageStateForInstalledAndFiltered( + packageName, callingUid, callingUserId) == null + ); + if (result == null) { + return ParceledListSlice.emptyList(); + } + return new ParceledListSlice<>(result); + } + + @Override + public void registerDexModule(String packageName, String dexModulePath, + boolean isSharedModule, + IDexModuleRegisterCallback callback) { + // ART Service doesn't support this explicit dexopting and instead relies on background + // dexopt for secondary dex files. For compat parity between ART Service and the legacy + // code it's disabled for both. + // + // Also, this API is problematic anyway since it doesn't provide the correct classloader + // context, so it is hard to produce dexopt artifacts that the runtime can load + // successfully. + Slog.i(TAG, + "Ignored unsupported registerDexModule call for " + dexModulePath + " in " + + packageName); + DexManager.RegisterDexModuleResult result = new DexManager.RegisterDexModuleResult( + false, "registerDexModule call not supported since Android U"); + + if (callback != null) { + mHandler.post(() -> { + try { + callback.onDexModuleRegistered(dexModulePath, result.success, + result.message); + } catch (RemoteException e) { + Slog.w(PackageManagerService.TAG, + "Failed to callback after module registration " + dexModulePath, e); + } + }); + } + } + + @android.annotation.EnforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) + @Override + public void registerMoveCallback(IPackageMoveObserver callback) { + registerMoveCallback_enforcePermission(); + mMoveCallbacks.register(callback); + } + + @Override + public void restoreDomainVerification(byte[] backup, int userId) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Only the system may call restorePreferredActivities()"); + } + + try { + ByteArrayInputStream input = new ByteArrayInputStream(backup); + TypedXmlPullParser parser = Xml.resolvePullParser(input); + + // User ID input isn't necessary here as it assumes the user integers match and that + // the only states inside the backup XML are for the target user. + mDomainVerificationManager.restoreSettings(snapshotComputer(), parser); + input.close(); + } catch (Exception e) { + if (PackageManagerService.DEBUG_BACKUP) { + Slog.e(PackageManagerService.TAG, "Exception restoring domain verification: " + e.getMessage()); + } + } + } + + @Override + public void restoreLabelAndIcon(@NonNull ComponentName componentName, int userId) { + updateComponentLabelIcon(componentName, null, null, userId); + } + + @Override + public void sendDeviceCustomizationReadyBroadcast() { + mContext.enforceCallingPermission(Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY, + "sendDeviceCustomizationReadyBroadcast"); + + final long ident = Binder.clearCallingIdentity(); + try { + BroadcastHelper.sendDeviceCustomizationReadyBroadcast(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void setApplicationCategoryHint(String packageName, int categoryHint, + String callerPackageName) { + final FunctionalUtils.ThrowingBiFunction implementation = (initialState, computer) -> { + if (computer.getInstantAppPackageName(Binder.getCallingUid()) != null) { + throw new SecurityException( + "Instant applications don't have access to this method"); + } + mInjector.getSystemService(AppOpsManager.class) + .checkPackage(Binder.getCallingUid(), callerPackageName); + + PackageStateInternal packageState = computer.getPackageStateForInstalledAndFiltered( + packageName, Binder.getCallingUid(), UserHandle.getCallingUserId()); + if (packageState == null) { + throw new IllegalArgumentException("Unknown target package " + packageName); + } + + if (!Objects.equals(callerPackageName, + packageState.getInstallSource().mInstallerPackageName)) { + throw new IllegalArgumentException("Calling package " + callerPackageName + + " is not installer for " + packageName); + } + + if (packageState.getCategoryOverride() != categoryHint) { + return commitPackageStateMutation(initialState, + packageName, state -> state.setCategoryOverride(categoryHint)); + } else { + return null; + } + }; + + PackageStateMutator.Result result = + implementation.apply(recordInitialState(), snapshotComputer()); + if (result != null && result.isStateChanged() && !result.isSpecificPackageNull()) { + // TODO: Specific return value of what state changed? + // The installer on record might have changed, retry with lock + synchronized (mPackageStateWriteLock) { + result = implementation.apply(recordInitialState(), snapshotComputer()); + } + } + + if (result != null && result.isCommitted()) { + scheduleWriteSettings(); + } + } + + @Override + public void setApplicationEnabledSetting(String appPackageName, + int newState, int flags, int userId, String callingPackage) { + if (!mUserManager.exists(userId)) return; + if (callingPackage == null) { + callingPackage = Integer.toString(Binder.getCallingUid()); + } + + setEnabledSettings(List.of(new PackageManager.ComponentEnabledSetting(appPackageName, newState, flags)), + userId, callingPackage); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_USERS) + @Override + public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, + int userId) { + setApplicationHiddenSettingAsUser_enforcePermission(); + final int callingUid = Binder.getCallingUid(); + final Computer snapshot = snapshotComputer(); + snapshot.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, + true /* checkShell */, "setApplicationHiddenSetting for user " + userId); + + if (hidden && isPackageDeviceAdmin(packageName, userId)) { + Slog.w(TAG, "Not hiding package " + packageName + ": has active device admin"); + return false; + } + + // Do not allow "android" is being disabled + if ("android".equals(packageName)) { + Slog.w(TAG, "Cannot hide package: android"); + return false; + } + + final long callingId = Binder.clearCallingIdentity(); + try { + final PackageStateInternal packageState = snapshot.getPackageStateInternal( + packageName); + if (packageState == null) { + return false; + } + + final PackageUserStateInternal userState = packageState.getUserStateOrDefault( + userId); + if (userState.isHidden() == hidden || !userState.isInstalled() + || snapshot.shouldFilterApplication(packageState, callingUid, userId)) { + return false; + } + + // Cannot hide static shared libs as they are considered + // a part of the using app (emulating static linking). Also + // static libs are installed always on internal storage. + AndroidPackage pkg = packageState.getPkg(); + if (pkg != null) { + // Cannot hide SDK libs as they are controlled by SDK manager. + if (pkg.getSdkLibraryName() != null) { + Slog.w(TAG, "Cannot hide package: " + packageName + + " providing SDK library: " + + pkg.getSdkLibraryName()); + 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. + if (pkg.getStaticSharedLibraryName() != null) { + Slog.w(TAG, "Cannot hide package: " + packageName + + " providing static shared library: " + + pkg.getStaticSharedLibraryName()); + return false; + } + } + // Only allow protected packages to hide themselves. + if (hidden && !UserHandle.isSameApp(callingUid, packageState.getAppId()) + && mProtectedPackages.isPackageStateProtected(userId, packageName)) { + Slog.w(TAG, "Not hiding protected package: " + packageName); + return false; + } + + commitPackageStateMutation(null, packageName, packageState1 -> + packageState1.userState(userId).setHidden(hidden)); + + final Computer newSnapshot = snapshotComputer(); + final PackageStateInternal newPackageState = + newSnapshot.getPackageStateInternal(packageName); + + if (hidden) { + killApplication(packageName, newPackageState.getAppId(), userId, "hiding pkg", + ApplicationExitInfo.REASON_OTHER); + mBroadcastHelper.sendApplicationHiddenForUser( + packageName, newPackageState, userId, + /* packageSender= */ PackageManagerService.this); + } else { + mBroadcastHelper.sendPackageAddedForUser( + newSnapshot, packageName, newPackageState, userId, + false /* isArchived */, DataLoaderType.NONE, + mAppPredictionServicePackage); + } + + scheduleWritePackageRestrictions(userId); + return true; + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + @android.annotation.EnforcePermission(android.Manifest.permission.DELETE_PACKAGES) + @Override + public boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, + int userId) { + setBlockUninstallForUser_enforcePermission(); + final Computer snapshot = snapshotComputer(); + PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); + if (packageState != null && packageState.getPkg() != null) { + AndroidPackage pkg = packageState.getPkg(); + // Cannot block uninstall SDK libs as they are controlled by SDK manager. + if (pkg.getSdkLibraryName() != null) { + Slog.w(PackageManagerService.TAG, "Cannot block uninstall of package: " + packageName + + " providing SDK library: " + pkg.getSdkLibraryName()); + return false; + } + // 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. + if (pkg.getStaticSharedLibraryName() != null) { + Slog.w(PackageManagerService.TAG, "Cannot block uninstall of package: " + packageName + + " providing static shared library: " + pkg.getStaticSharedLibraryName()); + return false; + } + } + synchronized (mLock) { + mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall); + } + + scheduleWritePackageRestrictions(userId); + return true; + } + + @Override + public void setComponentEnabledSetting(ComponentName componentName, + int newState, int flags, int userId, String callingPackage) { + if (!mUserManager.exists(userId)) return; + if (callingPackage == null) { + callingPackage = Integer.toString(Binder.getCallingUid()); + } + + setEnabledSettings(List.of(new PackageManager.ComponentEnabledSetting(componentName, newState, flags)), + userId, callingPackage); + } + + @Override + public void setComponentEnabledSettings( + List settings, int userId, + String callingPackage) { + if (!mUserManager.exists(userId)) return; + if (settings == null || settings.isEmpty()) { + throw new IllegalArgumentException("The list of enabled settings is empty"); + } + if (callingPackage == null) { + callingPackage = Integer.toString(Binder.getCallingUid()); + } + setEnabledSettings(settings, userId, callingPackage); + } + + @Override + public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames, + int restrictionFlags, int userId) { + final int callingUid = Binder.getCallingUid(); + final Computer snapshot = snapshotComputer(); + enforceCanSetDistractingPackageRestrictionsAsUser(callingUid, userId, + "setDistractingPackageRestrictionsAsUser"); + Objects.requireNonNull(packageNames, "packageNames cannot be null"); + return mDistractingPackageHelper.setDistractingPackageRestrictionsAsUser(snapshot, + packageNames, restrictionFlags, userId, callingUid); + } + + @Override + public void setHarmfulAppWarning(@NonNull String packageName, @Nullable CharSequence warning, + int userId) { + final int callingUid = Binder.getCallingUid(); + final int callingAppId = UserHandle.getAppId(callingUid); + + final Computer snapshot = snapshotComputer(); + snapshot.enforceCrossUserPermission(callingUid, userId, true /*requireFullPermission*/, + true /*checkShell*/, "setHarmfulAppInfo"); + + if (!PackageManagerServiceUtils.isSystemOrRoot(callingAppId) + && snapshot.checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) + != PERMISSION_GRANTED) { + throw new SecurityException("Caller must have the " + + SET_HARMFUL_APP_WARNINGS + " permission."); + } + + PackageStateMutator.Result result = commitPackageStateMutation(null, packageName, + packageState -> packageState.userState(userId) + .setHarmfulAppWarning(warning == null ? null : warning.toString())); + if (result.isSpecificPackageNull()) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + scheduleWritePackageRestrictions(userId); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + @Override + public boolean setInstallLocation(int loc) { + setInstallLocation_enforcePermission(); + if (getInstallLocation() == loc) { + return true; + } + if (loc == InstallLocationUtils.APP_INSTALL_AUTO + || loc == InstallLocationUtils.APP_INSTALL_INTERNAL + || loc == InstallLocationUtils.APP_INSTALL_EXTERNAL) { + android.provider.Settings.Global.putInt(mContext.getContentResolver(), + android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION, loc); + return true; + } + return false; + } + + @Override + public void setInstallerPackageName(String targetPackage, + @Nullable String installerPackageName) { + final int callingUid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getUserId(callingUid); + final FunctionalUtils.ThrowingCheckedFunction + implementation = snapshot -> { + if (snapshot.getInstantAppPackageName(callingUid) != null) { + return false; + } + + PackageStateInternal targetPackageState = + snapshot.getPackageStateForInstalledAndFiltered( + targetPackage, callingUid, callingUserId); + if (targetPackageState == null) { + throw new IllegalArgumentException("Unknown target package: " + targetPackage); + } + + PackageStateInternal installerPackageState = null; + if (installerPackageName != null) { + installerPackageState = snapshot.getPackageStateForInstalledAndFiltered( + installerPackageName, callingUid, callingUserId); + if (installerPackageState == null) { + throw new IllegalArgumentException("Unknown installer package: " + + installerPackageName); + } + } + + SigningDetails callerSigningDetails; + final int appId = UserHandle.getAppId(callingUid); + Pair either = + snapshot.getPackageOrSharedUser(appId); + if (either != null) { + if (either.first != null) { + callerSigningDetails = either.first.getSigningDetails(); + } else { + callerSigningDetails = either.second.getSigningDetails(); + } + } else { + throw new SecurityException("Unknown calling UID: " + callingUid); + } + + // Verify: can't set installerPackageName to a package that is + // not signed with the same cert as the caller. + if (installerPackageState != null) { + if (compareSignatures(callerSigningDetails, + installerPackageState.getSigningDetails()) + != PackageManager.SIGNATURE_MATCH) { + throw new SecurityException( + "Caller does not have same cert as new installer package " + + installerPackageName); + } + } + + // Verify: if target already has an installer package, it must + // be signed with the same cert as the caller. + String targetInstallerPackageName = + targetPackageState.getInstallSource().mInstallerPackageName; + PackageStateInternal targetInstallerPkgSetting = targetInstallerPackageName == null + ? null : snapshot.getPackageStateInternal(targetInstallerPackageName); + + if (targetInstallerPkgSetting != null) { + if (compareSignatures(callerSigningDetails, + targetInstallerPkgSetting.getSigningDetails()) + != PackageManager.SIGNATURE_MATCH) { + throw new SecurityException( + "Caller does not have same cert as old installer package " + + targetInstallerPackageName); + } + } else if (mContext.checkCallingOrSelfPermission( + Manifest.permission.INSTALL_PACKAGES) != PERMISSION_GRANTED) { + // This is probably an attempt to exploit vulnerability b/150857253 of taking + // privileged installer permissions when the installer has been uninstalled or + // was never set. + EventLog.writeEvent(0x534e4554, "150857253", callingUid, ""); + + final long binderToken = Binder.clearCallingIdentity(); + try { + if (mInjector.getCompatibility().isChangeEnabledByUid( + PackageManagerService.THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE, + callingUid)) { + throw new SecurityException("Neither user " + callingUid + + " nor current process has " + + Manifest.permission.INSTALL_PACKAGES); + } else { + // If change disabled, fail silently for backwards compatibility + return false; + } + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + + return true; + }; + PackageStateMutator.InitialState initialState = recordInitialState(); + boolean allowed = implementation.apply(snapshotComputer()); + if (allowed) { + // TODO: Need to lock around here to handle mSettings.addInstallerPackageNames, + // should find an alternative which avoids any race conditions + final int installerPackageUid = installerPackageName == null + ? INVALID_UID : snapshotComputer().getPackageUid(installerPackageName, + 0 /* flags */, callingUserId); + PackageStateInternal targetPackageState; + synchronized (mLock) { + PackageStateMutator.Result result = commitPackageStateMutation(initialState, + targetPackage, state -> state.setInstaller(installerPackageName, + installerPackageUid)); + if (result.isPackagesChanged() || result.isStateChanged()) { + synchronized (mPackageStateWriteLock) { + allowed = implementation.apply(snapshotComputer()); + if (allowed) { + commitPackageStateMutation(null, targetPackage, + state -> state.setInstaller(installerPackageName, + installerPackageUid)); + } else { + return; + } + } + } + targetPackageState = snapshotComputer().getPackageStateInternal(targetPackage); + mSettings.addInstallerPackageNames(targetPackageState.getInstallSource()); + } + mAppsFilter.addPackage(snapshotComputer(), targetPackageState); + scheduleWriteSettings(); + } + } + + @Override + public void relinquishUpdateOwnership(String targetPackage) { + final int callingUid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getUserId(callingUid); + final Computer snapshot = snapshotComputer(); + + final PackageStateInternal targetPackageState = + snapshot.getPackageStateForInstalledAndFiltered(targetPackage, callingUid, + callingUserId); + if (targetPackageState == null) { + throw new IllegalArgumentException("Unknown target package: " + targetPackage); + } + + final String targetUpdateOwnerPackageName = + targetPackageState.getInstallSource().mUpdateOwnerPackageName; + final PackageStateInternal targetUpdateOwnerPkgSetting = + targetUpdateOwnerPackageName == null ? null + : snapshot.getPackageStateInternal(targetUpdateOwnerPackageName); + + if (targetUpdateOwnerPkgSetting == null) { + return; + } + + final int callingAppId = UserHandle.getAppId(callingUid); + final int targetUpdateOwnerAppId = targetUpdateOwnerPkgSetting.getAppId(); + if (callingAppId != Process.SYSTEM_UID + && callingAppId != Process.SHELL_UID + && callingAppId != targetUpdateOwnerAppId) { + throw new SecurityException("Caller is not the current update owner."); + } + + commitPackageStateMutation(null /* initialState */, targetPackage, + state -> state.setUpdateOwner(null /* updateOwnerPackageName */)); + scheduleWriteSettings(); + } + + @Override + public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) { + if (HIDE_EPHEMERAL_APIS) { + return true; + } + + final Computer snapshot = snapshotComputer(); + snapshot.enforceCrossUserPermission(Binder.getCallingUid(), userId, + true /* requireFullPermission */, true /* checkShell */, + "setInstantAppCookie"); + if (!snapshot.isCallerSameApp(packageName, Binder.getCallingUid())) { + return false; + } + + PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); + if (packageState == null || packageState.getPkg() == null) { + return false; + } + return mInstantAppRegistry.setInstantAppCookie(packageState.getPkg(), cookie, + mContext.getPackageManager().getInstantAppCookieMaxBytes(), userId); + } + + @Override + public void setKeepUninstalledPackages(List packageList) { + mContext.enforceCallingPermission( + Manifest.permission.KEEP_UNINSTALLED_PACKAGES, + "setKeepUninstalledPackages requires KEEP_UNINSTALLED_PACKAGES permission"); + Objects.requireNonNull(packageList); + + setKeepUninstalledPackagesInternal(snapshot(), packageList); + } + + @Override + public void setMimeGroup(String packageName, String mimeGroup, List mimeTypes) { + final Computer snapshot = snapshotComputer(); + enforceOwnerRights(snapshot, packageName, Binder.getCallingUid()); + mimeTypes = CollectionUtils.emptyIfNull(mimeTypes); + for (int i = 0; i < mimeTypes.size(); i++) { + if (mimeTypes.get(i).length() > 255) { + throw new IllegalArgumentException("MIME type length exceeds 255 characters"); + } + } + final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); + Set existingMimeTypes = packageState.getMimeGroups().get(mimeGroup); + if (existingMimeTypes == null) { + throw new IllegalArgumentException("Unknown MIME group " + mimeGroup + + " for package " + packageName); + } + if (existingMimeTypes.size() == mimeTypes.size() + && existingMimeTypes.containsAll(mimeTypes)) { + return; + } + if (mimeTypes.size() > 500) { + throw new IllegalStateException("Max limit on MIME types for MIME group " + + mimeGroup + " exceeded for package " + packageName); + } + + ArraySet mimeTypesSet = new ArraySet<>(mimeTypes); + commitPackageStateMutation(null, packageName, packageStateWrite -> { + packageStateWrite.setMimeGroup(mimeGroup, mimeTypesSet); + }); + if (mComponentResolver.updateMimeGroup(snapshotComputer(), packageName, mimeGroup)) { + Binder.withCleanCallingIdentity(() -> { + mPreferredActivityHelper.clearPackagePreferredActivities(packageName, + UserHandle.USER_ALL); + // Send the ACTION_PACKAGE_CHANGED when the mimeGroup has changes + final Computer snapShot = snapshotComputer(); + final ArrayList components = new ArrayList<>( + Collections.singletonList(packageName)); + final int appId = packageState.getAppId(); + final int[] userIds = resolveUserIds(UserHandle.USER_ALL); + final String reason = "The mimeGroup is changed"; + for (int i = 0; i < userIds.length; i++) { + final PackageUserStateInternal pkgUserState = + packageState.getUserStates().get(userIds[i]); + if (pkgUserState != null && pkgUserState.isInstalled()) { + final int packageUid = UserHandle.getUid(userIds[i], appId); + mBroadcastHelper.sendPackageChangedBroadcast(snapShot, packageName, + true /* dontKillApp */, components, packageUid, reason); + } + } + }); + } + + scheduleWriteSettings(); + } + + @Override + public void setPackageStoppedState(String packageName, boolean stopped, int userId) { + PackageManagerService.this + .setPackageStoppedState(snapshotComputer(), packageName, stopped, userId); + } + + @Override + public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended, + PersistableBundle appExtras, PersistableBundle launcherExtras, + SuspendDialogInfo dialogInfo, int flags, String suspendingPackage, + int suspendingUserId, int targetUserId) { + final int callingUid = Binder.getCallingUid(); + boolean quarantined = false; + if (Flags.quarantinedEnabled()) { + if ((flags & PackageManager.FLAG_SUSPEND_QUARANTINED) != 0) { + quarantined = true; + } else if (FeatureFlagUtils.isEnabled(mContext, + SETTINGS_TREAT_PAUSE_AS_QUARANTINE)) { + final String wellbeingPkg = mContext.getString(R.string.config_systemWellbeing); + quarantined = suspendingPackage.equals(wellbeingPkg); + } + } + final Computer snapshot = snapshotComputer(); + final UserPackage suspender = UserPackage.of(targetUserId, suspendingPackage); + enforceCanSetPackagesSuspendedAsUser(snapshot, quarantined, suspender, callingUid, + targetUserId, "setPackagesSuspendedAsUser"); + return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended, + appExtras, launcherExtras, dialogInfo, suspender, targetUserId, callingUid, + quarantined); + } + + @Override + public boolean setRequiredForSystemUser(String packageName, boolean requiredForSystemUser) { + PackageManagerServiceUtils.enforceSystemOrRoot( + "setRequiredForSystemUser can only be run by the system or root"); + + PackageStateMutator.Result result = commitPackageStateMutation(null, packageName, + packageState -> packageState.setRequiredForSystemUser(requiredForSystemUser)); + if (!result.isCommitted()) { + return false; + } + + scheduleWriteSettings(); + return true; + } + + @android.annotation.EnforcePermission(android.Manifest.permission.INSTALL_PACKAGES) + @Override + public void setUserMinAspectRatio(@NonNull String packageName, int userId, + @PackageManager.UserMinAspectRatio int aspectRatio) { + setUserMinAspectRatio_enforcePermission(); + final int callingUid = Binder.getCallingUid(); + final Computer snapshot = snapshotComputer(); + snapshot.enforceCrossUserPermission(callingUid, userId, + false /* requireFullPermission */, false /* checkShell */, + "setUserMinAspectRatio"); + enforceOwnerRights(snapshot, packageName, callingUid); + + final PackageStateInternal packageState = snapshot + .getPackageStateForInstalledAndFiltered(packageName, callingUid, userId); + if (packageState == null) { + return; + } + + if (packageState.getUserStateOrDefault(userId).getMinAspectRatio() == aspectRatio) { + return; + } + + commitPackageStateMutation(null, packageName, state -> + state.userState(userId).setMinAspectRatio(aspectRatio)); + } + + @Override + @SuppressWarnings("GuardedBy") + public void setRuntimePermissionsVersion(int version, @UserIdInt int userId) { + Preconditions.checkArgumentNonnegative(version); + Preconditions.checkArgumentNonnegative(userId); + enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions( + "setRuntimePermissionVersion"); + mSettings.setDefaultRuntimePermissionsVersion(version, userId); + } + + @Override + public void setSplashScreenTheme(@NonNull String packageName, @Nullable String themeId, + int userId) { + final int callingUid = Binder.getCallingUid(); + final Computer snapshot = snapshotComputer(); + snapshot.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, + false /* checkShell */, "setSplashScreenTheme"); + enforceOwnerRights(snapshot, packageName, callingUid); + + PackageStateInternal packageState = snapshot.getPackageStateForInstalledAndFiltered( + packageName, callingUid, userId); + if (packageState == null) { + return; + } + + commitPackageStateMutation(null, packageName, state -> + state.userState(userId).setSplashScreenTheme(themeId)); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.INSTALL_PACKAGES) + @Override + public void setUpdateAvailable(String packageName, boolean updateAvailable) { + setUpdateAvailable_enforcePermission(); + commitPackageStateMutation(null, packageName, state -> + state.setUpdateAvailable(updateAvailable)); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) + @Override + public void unregisterMoveCallback(IPackageMoveObserver callback) { + unregisterMoveCallback_enforcePermission(); + mMoveCallbacks.unregister(callback); + } + + @Override + public void verifyPendingInstall(int verificationId, int verificationCode) + throws RemoteException { + // Negative ids correspond to testing verifiers and will be silently enforced in + // the handler thread. + if (verificationId >= 0) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.PACKAGE_VERIFICATION_AGENT, + "Only package verification agents can verify applications"); + } + final int callingUid = Binder.getCallingUid(); + + mHandler.post(() -> { + final int id = verificationId >= 0 ? verificationId : -verificationId; + final PackageVerificationState state = mPendingVerification.get(id); + if (state == null) { + return; + } + if (!state.checkRequiredVerifierUid(callingUid) + && !state.checkSufficientVerifierUid(callingUid)) { + // Only allow calls from verifiers. + return; + } + + final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED); + final PackageVerificationResponse response = new PackageVerificationResponse( + verificationCode, callingUid); + msg.arg1 = id; + msg.obj = response; + mHandler.sendMessage(msg); + }); + } + + @Override + public void registerPackageMonitorCallback(@NonNull IRemoteCallback callback, int userId) { + int uid = Binder.getCallingUid(); + int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), uid, + userId, true, true, "registerPackageMonitorCallback", + mContext.getPackageName()); + mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, targetUserId, + uid); + } + + @Override + public void unregisterPackageMonitorCallback(@NonNull IRemoteCallback callback) { + mPackageMonitorCallbackHelper.unregisterPackageMonitorCallback(callback); + } + + @Override + public void requestPackageChecksums(@NonNull String packageName, boolean includeSplits, + @Checksum.TypeMask int optional, @Checksum.TypeMask int required, + @Nullable List trustedInstallers, + @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId) { + requestChecksumsInternal(snapshotComputer(), packageName, includeSplits, optional, + required, trustedInstallers, onChecksumsReadyListener, userId, + mInjector.getBackgroundExecutor(), mInjector.getBackgroundHandler()); + } + + @Override + public void notifyPackagesReplacedReceived(String[] packages) { + Computer computer = snapshotComputer(); + ArraySet packagesToNotify = computer.getNotifyPackagesForReplacedReceived(packages); + for (int index = 0; index < packagesToNotify.size(); index++) { + notifyInstallObserver(packagesToNotify.valueAt(index), false /* killApp */); + } + } + + @Override + public ArchivedPackageParcel getArchivedPackage(@NonNull String packageName, int userId) { + return getArchivedPackageInternal(packageName, userId); + } + + @Override + public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user, + @NonNull String callingPackageName) { + return mInstallerService.mPackageArchiver.getArchivedAppIcon(packageName, user, + callingPackageName); + } + + @Override + public boolean isAppArchivable(@NonNull String packageName, @NonNull UserHandle user) { + return mInstallerService.mPackageArchiver.isAppArchivable(packageName, user); + } + + /** + * Wait for the handler to finish handling all pending messages. + * @param timeoutMillis Maximum time in milliseconds to wait. + * @param forBackgroundHandler Whether to wait for the background handler instead. + * @return True if all the waiting messages in the handler has been handled. + * False if timeout. + */ + @Override + public boolean waitForHandler(long timeoutMillis, boolean forBackgroundHandler) { + final CountDownLatch latch = new CountDownLatch(1); + if (forBackgroundHandler) { + mBackgroundHandler.post(latch::countDown); + } else { + mHandler.post(latch::countDown); + } + final long endTimeMillis = System.currentTimeMillis() + timeoutMillis; + while (latch.getCount() > 0) { + try { + final long remainingTimeMillis = endTimeMillis - System.currentTimeMillis(); + if (remainingTimeMillis <= 0) { + return false; + } + return latch.await(remainingTimeMillis, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + // ignore and retry + } + } + return true; + } + + @Override + @Nullable + public ComponentName getDomainVerificationAgent() { + final int callerUid = Binder.getCallingUid(); + if (!PackageManagerServiceUtils.isRootOrShell(callerUid)) { + throw new SecurityException("Not allowed to query domain verification agent"); + } + final Computer snapshot = snapshotComputer(); + return getDomainVerificationAgentComponentNameLPr(snapshot); + } + + @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + try { + return super.onTransact(code, data, reply, flags); + } catch (RuntimeException e) { + if (!(e instanceof SecurityException) && !(e instanceof IllegalArgumentException) + && !(e instanceof ParcelableException)) { + Slog.wtf(TAG, "Package Manager Unexpected Exception", e); + } + throw e; + } + } + + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, + FileDescriptor err, String[] args, ShellCallback callback, + ResultReceiver resultReceiver) { + (new PackageManagerShellCommand(this, mContext, + mDomainVerificationManager.getShell())) + .exec(this, in, out, err, args, callback, resultReceiver); + } + + @SuppressWarnings("resource") + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; + final Computer snapshot = snapshotComputer(); + final KnownPackages knownPackages = new KnownPackages( + mDefaultAppProvider, + mRequiredInstallerPackage, + mRequiredUninstallerPackage, + mSetupWizardPackage, + mRequiredVerifierPackages, + mDefaultTextClassifierPackage, + mSystemTextClassifierPackageName, + mRequiredPermissionControllerPackage, + mConfiguratorPackage, + mIncidentReportApproverPackage, + mAmbientContextDetectionPackage, + mWearableSensingPackage, + mAppPredictionServicePackage, + COMPANION_PACKAGE_NAME, + mRetailDemoPackage, + mOverlayConfigSignaturePackage, + mRecentsPackage); + final ArrayMap availableFeatures; + availableFeatures = new ArrayMap<>(mAvailableFeatures); + final ArraySet protectedBroadcasts; + synchronized (mProtectedBroadcasts) { + protectedBroadcasts = new ArraySet<>(mProtectedBroadcasts); + } + new DumpHelper(mPermissionManager, mStorageEventHelper, + mDomainVerificationManager, mInstallerService, mRequiredVerifierPackages, + knownPackages, mChangedPackagesTracker, availableFeatures, protectedBroadcasts, + getPerUidReadTimeouts(snapshot), mSnapshotStatistics + ).doDump(snapshot, fd, pw, args); + } + } + + private class PackageManagerInternalImpl extends PackageManagerInternalBase { + + public PackageManagerInternalImpl() { + super(PackageManagerService.this); + } + + @NonNull + @Override + protected Context getContext() { + return mContext; + } + + @NonNull + @Override + protected PermissionManagerServiceInternal getPermissionManager() { + return mPermissionManager; + } + + @NonNull + @Override + protected AppDataHelper getAppDataHelper() { + return mAppDataHelper; + } + + @NonNull + @Override + protected PackageObserverHelper getPackageObserverHelper() { + return mPackageObserverHelper; + } + + @NonNull + @Override + protected ResolveIntentHelper getResolveIntentHelper() { + return mResolveIntentHelper; + } + + @NonNull + @Override + protected SuspendPackageHelper getSuspendPackageHelper() { + return mSuspendPackageHelper; + } + + @NonNull + @Override + protected DistractingPackageHelper getDistractingPackageHelper() { + return mDistractingPackageHelper; + } + + @NonNull + @Override + protected ProtectedPackages getProtectedPackages() { + return mProtectedPackages; + } + + @NonNull + @Override + protected UserNeedsBadgingCache getUserNeedsBadging() { + return mUserNeedsBadging; + } + + @NonNull + @Override + protected InstantAppRegistry getInstantAppRegistry() { + return mInstantAppRegistry; + } + + @NonNull + @Override + protected ApexManager getApexManager() { + return mApexManager; + } + + @NonNull + @Override + protected DexManager getDexManager() { + return mDexManager; + } + + @NonNull + @Override + public DynamicCodeLogger getDynamicCodeLogger() { + return mDynamicCodeLogger; + } + + @Override + public boolean isPlatformSigned(String packageName) { + PackageStateInternal packageState = snapshot().getPackageStateInternal(packageName); + if (packageState == null) { + return false; + } + SigningDetails signingDetails = packageState.getSigningDetails(); + return signingDetails.hasAncestorOrSelf(mPlatformPackage.getSigningDetails()) + || mPlatformPackage.getSigningDetails().checkCapability(signingDetails, + SigningDetails.CertCapabilities.PERMISSION); + } + + @Override + public boolean isDataRestoreSafe(byte[] restoringFromSigHash, String packageName) { + final Computer snapshot = snapshot(); + SigningDetails sd = snapshot.getSigningDetails(packageName); + if (sd == null) { + return false; + } + return sd.hasSha256Certificate(restoringFromSigHash, + SigningDetails.CertCapabilities.INSTALLED_DATA); + } + + @Override + public boolean isDataRestoreSafe(Signature restoringFromSig, String packageName) { + final Computer snapshot = snapshot(); + SigningDetails sd = snapshot.getSigningDetails(packageName); + if (sd == null) { + return false; + } + return sd.hasCertificate(restoringFromSig, + SigningDetails.CertCapabilities.INSTALLED_DATA); + } + + @Override + public boolean hasSignatureCapability(int serverUid, int clientUid, + @SigningDetails.CertCapabilities int capability) { + final Computer snapshot = snapshot(); + SigningDetails serverSigningDetails = snapshot.getSigningDetails(serverUid); + SigningDetails clientSigningDetails = snapshot.getSigningDetails(clientUid); + return serverSigningDetails.checkCapability(clientSigningDetails, capability) + || clientSigningDetails.hasAncestorOrSelf(serverSigningDetails); + } + + @Override + public PackageList getPackageList(@Nullable PackageListObserver observer) { + final ArrayList list = new ArrayList<>(); + PackageManagerService.this.forEachPackageState(snapshot(), packageState -> { + AndroidPackage pkg = packageState.getPkg(); + if (pkg != null) { + list.add(pkg.getPackageName()); + } + }); + final PackageList packageList = new PackageList(list, observer); + if (observer != null) { + mPackageObserverHelper.addObserver(packageList); + } + return packageList; + } + + @Override + public @Nullable + String getDisabledSystemPackageName(@NonNull String packageName) { + PackageStateInternal disabledPkgSetting = snapshot().getDisabledSystemPackage( + packageName); + AndroidPackage disabledPkg = disabledPkgSetting == null + ? null : disabledPkgSetting.getPkg(); + return disabledPkg == null ? null : disabledPkg.getPackageName(); + } + + @Override + public boolean isResolveActivityComponent(ComponentInfo component) { + return mResolveActivity.packageName.equals(component.packageName) + && mResolveActivity.name.equals(component.name); + } + + @Override + public long getCeDataInode(String packageName, int userId) { + final PackageStateInternal packageState = + snapshot().getPackageStateInternal(packageName); + if (packageState == null) { + return 0; + } else { + return packageState.getUserStateOrDefault(userId).getCeDataInode(); + } + } + + @Override + public void removeAllNonSystemPackageSuspensions(int userId) { + final Computer computer = snapshotComputer(); + final String[] allPackages = computer.getAllAvailablePackageNames(); + mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer, allPackages, + (suspender) -> !PLATFORM_PACKAGE_NAME.equals(suspender.packageName), + userId); + } + + @Override + public void flushPackageRestrictions(int userId) { + synchronized (mLock) { + PackageManagerService.this.flushPackageRestrictionsAsUserInternalLocked(userId); + } + } + + @Override + public String[] setPackagesSuspendedByAdmin( + @UserIdInt int userId, @NonNull String[] packageNames, boolean suspended) { + final int suspendingUserId = userId; + final UserPackage suspender = UserPackage.of( + suspendingUserId, PackageManagerService.PLATFORM_PACKAGE_NAME); + return mSuspendPackageHelper.setPackagesSuspended(snapshotComputer(), packageNames, + suspended, null /* appExtras */, null /* launcherExtras */, + null /* dialogInfo */, suspender, userId, Process.SYSTEM_UID, + false /* quarantined */); + } + + @Override + public void setDeviceAndProfileOwnerPackages( + int deviceOwnerUserId, String deviceOwnerPackage, + SparseArray profileOwnerPackages) { + mProtectedPackages.setDeviceAndProfileOwnerPackages( + deviceOwnerUserId, deviceOwnerPackage, profileOwnerPackages); + final ArraySet usersWithPoOrDo = new ArraySet<>(); + if (deviceOwnerPackage != null) { + usersWithPoOrDo.add(deviceOwnerUserId); + } + final int sz = profileOwnerPackages.size(); + for (int i = 0; i < sz; i++) { + if (profileOwnerPackages.valueAt(i) != null) { + removeAllNonSystemPackageSuspensions(profileOwnerPackages.keyAt(i)); + } + } + } + + @Override + public void setExternalSourcesPolicy(ExternalSourcesPolicy policy) { + if (policy != null) { + mExternalSourcesPolicy = policy; + } + } + + @Override + public boolean isPackagePersistent(String packageName) { + final PackageStateInternal packageState = + snapshot().getPackageStateInternal(packageName); + if (packageState == null) { + return false; + } + + AndroidPackage pkg = packageState.getPkg(); + return pkg != null && packageState.isSystem() && pkg.isPersistent(); + } + + @Override + public List getOverlayPackages(int userId) { + final Computer snapshot = snapshotComputer(); + final ArrayList overlayPackages = new ArrayList<>(); + final ArrayMap packageStates = + snapshot.getPackageStates(); + for (int index = 0; index < packageStates.size(); index++) { + final PackageStateInternal packageState = packageStates.valueAt(index); + final AndroidPackage pkg = packageState.getPkg(); + if (pkg != null && pkg.getOverlayTarget() != null) { + PackageInfo pkgInfo = snapshot.generatePackageInfo(packageState, 0, userId); + if (pkgInfo != null) { + overlayPackages.add(pkgInfo); + } + } + } + + return overlayPackages; + } + + @Override + public List getTargetPackageNames(int userId) { + List targetPackages = new ArrayList<>(); + PackageManagerService.this.forEachPackageState(snapshot(), packageState -> { + final AndroidPackage pkg = packageState.getPkg(); + if (pkg != null && !pkg.isResourceOverlay()) { + targetPackages.add(pkg.getPackageName()); + } + }); + return targetPackages; + } + + @Override + public void setEnabledOverlayPackages(int userId, + @NonNull ArrayMap pendingChanges, + @NonNull Set outUpdatedPackageNames, + @NonNull Set outInvalidPackageNames) { + PackageManagerService.this.setEnabledOverlayPackages(userId, + pendingChanges, outUpdatedPackageNames, outInvalidPackageNames); + } + + @Override + public void addIsolatedUid(int isolatedUid, int ownerUid) { + synchronized (mLock) { + mIsolatedOwners.put(isolatedUid, ownerUid); + } + } + + @Override + public void removeIsolatedUid(int isolatedUid) { + synchronized (mLock) { + mIsolatedOwners.delete(isolatedUid); + } + } + + @Override + public void notifyPackageUse(String packageName, int reason) { + PackageManagerService.this.notifyPackageUseInternal(packageName, reason); + } + + @Nullable + @Override + public String removeLegacyDefaultBrowserPackageName(int userId) { + synchronized (mLock) { + return mSettings.removePendingDefaultBrowserLPw(userId); + } + } + + @Override + public void uninstallApex(String packageName, long versionCode, int userId, + IntentSender intentSender, int flags) { + final int callerUid = Binder.getCallingUid(); + if (!PackageManagerServiceUtils.isRootOrShell(callerUid)) { + throw new SecurityException("Not allowed to uninstall apexes"); + } + PackageInstallerService.PackageDeleteObserverAdapter adapter = + new PackageInstallerService.PackageDeleteObserverAdapter( + PackageManagerService.this.mContext, intentSender, packageName, + false, userId); + if ((flags & PackageManager.DELETE_ALL_USERS) == 0) { + adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED, + "Can't uninstall an apex for a single user"); + return; + } + final ApexManager am = PackageManagerService.this.mApexManager; + PackageInfo activePackage = snapshot().getPackageInfo( + packageName, PackageManager.MATCH_APEX, UserHandle.USER_SYSTEM); + if (activePackage == null) { + adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED, + packageName + " is not an apex package"); + return; + } + if (versionCode != PackageManager.VERSION_CODE_HIGHEST + && activePackage.getLongVersionCode() != versionCode) { + adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED, + "Active version " + activePackage.getLongVersionCode() + + " is not equal to " + versionCode + "]"); + return; + } + if (!am.uninstallApex(activePackage.applicationInfo.sourceDir)) { + adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED, + "Failed to uninstall apex " + packageName); + } else { + adapter.onPackageDeleted(packageName, PackageManager.DELETE_SUCCEEDED, + null); + } + } + + /** @deprecated For legacy shell command only. */ + @Override + @Deprecated + public void legacyDumpProfiles(String packageName, boolean dumpClassesAndMethods) + throws LegacyDexoptDisabledException { + final Computer snapshot = snapshotComputer(); + AndroidPackage pkg = snapshot.getPackage(packageName); + if (pkg == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + + synchronized (mInstallLock) { + Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "dump profiles"); + mArtManagerService.dumpProfiles(pkg, dumpClassesAndMethods); + Trace.traceEnd(Trace.TRACE_TAG_DALVIK); + } + } + + /** @deprecated For legacy shell command only. */ + @Override + @Deprecated + public void legacyForceDexOpt(String packageName) throws LegacyDexoptDisabledException { + mDexOptHelper.forceDexOpt(snapshotComputer(), packageName); + } + + /** @deprecated For legacy shell command only. */ + @Override + @Deprecated + public void legacyReconcileSecondaryDexFiles(String packageName) + throws LegacyDexoptDisabledException { + final Computer snapshot = snapshotComputer(); + if (snapshot.getInstantAppPackageName(Binder.getCallingUid()) != null) { + return; + } else if (snapshot.isInstantAppInternal( + packageName, UserHandle.getCallingUserId(), Process.SYSTEM_UID)) { + return; + } + mDexManager.reconcileSecondaryDexFiles(packageName); + } + + @Override + @SuppressWarnings("GuardedBy") + public void updateRuntimePermissionsFingerprint(@UserIdInt int userId) { + mSettings.updateRuntimePermissionsFingerprint(userId); + } + + @Override + public void migrateLegacyObbData() { + try { + mInstaller.migrateLegacyObbData(); + } catch (Exception e) { + Slog.wtf(TAG, e); + } + } + + @Override + public void writeSettings(boolean async) { + synchronized (mLock) { + if (async) { + scheduleWriteSettings(); + } else { + writeSettingsLPrTEMP(); + } + } + } + + @Override + public void writePermissionSettings(int[] userIds, boolean async) { + synchronized (mLock) { + for (int userId : userIds) { + mSettings.writePermissionStateForUserLPr(userId, !async); + } + } + } + + @Override + public LegacyPermissionSettings getLegacyPermissions() { + synchronized (mLock) { + return mSettings.mPermissions; + } + } + + /** + * Read legacy permission states for permissions migration to new permission subsystem. + */ + @Override + public RuntimePermissionsState getLegacyPermissionsState(int userId) { + synchronized (mLock) { + return mSettings.getLegacyPermissionsState(userId); + } + } + + @Override + public int getLegacyPermissionsVersion(@UserIdInt int userId) { + synchronized (mLock) { + return mSettings.getDefaultRuntimePermissionsVersion(userId); + } + } + + @Override + @SuppressWarnings("GuardedBy") + public boolean isPermissionUpgradeNeeded(int userId) { + return mSettings.isPermissionUpgradeNeeded(userId); + } + + @Override + public void setIntegrityVerificationResult(int verificationId, int verificationResult) { + final Message msg = mHandler.obtainMessage(INTEGRITY_VERIFICATION_COMPLETE); + msg.arg1 = verificationId; + msg.obj = verificationResult; + mHandler.sendMessage(msg); + } + + @Override + public void setVisibilityLogging(String packageName, boolean enable) { + PackageManagerServiceUtils.enforceSystemOrRootOrShell( + "Only the system or shell can set visibility logging."); + final PackageStateInternal packageState = + snapshot().getPackageStateInternal(packageName); + if (packageState == null) { + throw new IllegalStateException("No package found for " + packageName); + } + mAppsFilter.getFeatureConfig().enableLogging(packageState.getAppId(), enable); + } + + @Override + public void clearBlockUninstallForUser(@UserIdInt int userId) { + synchronized (mLock) { + mSettings.clearBlockUninstallLPw(userId); + mSettings.writePackageRestrictionsLPr(userId); + } + } + + @Override + public boolean registerInstalledLoadingProgressCallback(String packageName, + PackageManagerInternal.InstalledLoadingProgressCallback callback, int userId) { + final Computer snapshot = snapshotComputer(); + final PackageStateInternal ps = snapshot.getPackageStateForInstalledAndFiltered( + packageName, Binder.getCallingUid(), userId); + if (ps == null) { + return false; + } + if (!ps.isLoading()) { + Slog.w(TAG, + "Failed registering loading progress callback. Package is fully loaded."); + return false; + } + if (mIncrementalManager == null) { + Slog.w(TAG, + "Failed registering loading progress callback. Incremental is not enabled"); + return false; + } + return mIncrementalManager.registerLoadingProgressCallback(ps.getPathString(), + (IPackageLoadingProgressCallback) callback.getBinder()); + } + + @Override + public IncrementalStatesInfo getIncrementalStatesInfo( + @NonNull String packageName, int filterCallingUid, int userId) { + final Computer snapshot = snapshotComputer(); + final PackageStateInternal ps = snapshot.getPackageStateForInstalledAndFiltered( + packageName, filterCallingUid, userId); + if (ps == null) { + return null; + } + return new IncrementalStatesInfo(ps.isLoading(), ps.getLoadingProgress(), + ps.getLoadingCompletedTime()); + } + + @Override + public boolean isSameApp(@Nullable String packageName, int callingUid, int userId) { + return isSameApp(packageName, /*flags=*/0, callingUid, userId); + } + + @Override + public boolean isSameApp(@Nullable String packageName, + @PackageManager.PackageInfoFlagsBits long flags, int callingUid, int userId) { + if (packageName == null) { + return false; + } + + if (Process.isSdkSandboxUid(callingUid)) { + return packageName.equals(mRequiredSdkSandboxPackage); + } + Computer snapshot = snapshot(); + int uid = snapshot.getPackageUid(packageName, flags, userId); + return UserHandle.isSameApp(uid, callingUid); + } + + @Override + public void onPackageProcessKilledForUninstall(String packageName) { + mHandler.post(() -> PackageManagerService.this.notifyInstallObserver(packageName, + true /* killApp */)); + } + + @Override + public int[] getDistractingPackageRestrictionsAsUser( + @NonNull String[] packageNames, int userId) { + final int callingUid = Binder.getCallingUid(); + final Computer snapshot = snapshotComputer(); + Objects.requireNonNull(packageNames, "packageNames cannot be null"); + return mDistractingPackageHelper.getDistractingPackageRestrictionsAsUser(snapshot, + packageNames, userId, callingUid); + } + + @Override + public ParceledListSlice getHistoricalSessions(int userId) { + return mInstallerService.getHistoricalSessions(userId); + } + + @Override + public PackageArchiver getPackageArchiver() { + return mInstallerService.mPackageArchiver; + } + + @Override + public void sendPackageRestartedBroadcast(@NonNull String packageName, + int uid, @Intent.Flags int flags) { + final int userId = UserHandle.getUserId(uid); + final int [] userIds = resolveUserIds(userId); + final SparseArray broadcastAllowList = + snapshotComputer().getVisibilityAllowLists(packageName, userIds); + final Bundle extras = new Bundle(); + extras.putInt(Intent.EXTRA_UID, uid); + extras.putInt(Intent.EXTRA_USER_HANDLE, userId); + if (android.content.pm.Flags.stayStopped()) { + extras.putLong(Intent.EXTRA_TIME, SystemClock.elapsedRealtime()); + // Sent async using the PM handler, to maintain ordering with PACKAGE_UNSTOPPED + mHandler.post(() -> { + mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED, + packageName, extras, + flags, null, null, + userIds, null, broadcastAllowList, null, + null); + }); + } else { + mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED, + packageName, extras, + flags, null, null, + userIds, null, broadcastAllowList, null, + null); + } + mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_RESTARTED, + packageName, extras, userIds, null /* instantUserIds */, + broadcastAllowList, mHandler, null /* filterExtras */); + } + + @Override + public void sendPackageDataClearedBroadcast(@NonNull String packageName, + int uid, int userId, boolean isRestore, boolean isInstantApp) { + int[] visibilityAllowList = + snapshotComputer().getVisibilityAllowList(packageName, userId); + final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED, + Uri.fromParts("package", packageName, null /* fragment */)); + intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND + | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + intent.putExtra(Intent.EXTRA_UID, uid); + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + if (isRestore) { + intent.putExtra(Intent.EXTRA_IS_RESTORE, true); + } + if (isInstantApp) { + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); + } + mBroadcastHelper.sendPackageBroadcastWithIntent(intent, userId, isInstantApp, + 0 /* flags */, visibilityAllowList, null /* finishedReceiver */, + null /* filterExtrasForReceiver */, null /* bOptions */); + mPackageMonitorCallbackHelper.notifyPackageMonitorWithIntent(intent, userId, + visibilityAllowList, mHandler); + } + + @Override + public boolean isUpgradingFromLowerThan(int sdkVersion) { + final boolean isUpgrading = mPriorSdkVersion != -1; + return isUpgrading && mPriorSdkVersion < sdkVersion; + } + } + + private void setEnabledOverlayPackages(@UserIdInt int userId, + @NonNull ArrayMap pendingChanges, + @NonNull Set outUpdatedPackageNames, + @NonNull Set outInvalidPackageNames) { + final ArrayMap>> + targetPkgToLibNameToModifiedDependents = new ArrayMap<>(); + final int numberOfPendingChanges = pendingChanges.size(); + + synchronized (mOverlayPathsLock) { + Computer computer = snapshotComputer(); + for (int i = 0; i < numberOfPendingChanges; i++) { + final String targetPackageName = pendingChanges.keyAt(i); + final OverlayPaths newOverlayPaths = pendingChanges.valueAt(i); + final PackageStateInternal packageState = computer.getPackageStateInternal( + targetPackageName); + final AndroidPackage targetPkg = + packageState == null ? null : packageState.getPkg(); + if (targetPackageName == null || targetPkg == null) { + Slog.e(TAG, "failed to find package " + targetPackageName); + outInvalidPackageNames.add(targetPackageName); + continue; + } + + if (Objects.equals(packageState.getUserStateOrDefault(userId).getOverlayPaths(), + newOverlayPaths)) { + continue; + } + + if (targetPkg.getLibraryNames() != null) { + // Set the overlay paths for dependencies of the shared library. + List libraryNames = targetPkg.getLibraryNames(); + for (int j = 0; j < libraryNames.size(); j++) { + final String libName = libraryNames.get(j); + ArraySet modifiedDependents = null; + + final SharedLibraryInfo info = computer.getSharedLibraryInfo(libName, + SharedLibraryInfo.VERSION_UNDEFINED); + if (info == null) { + continue; + } + var usingSharedLibraryPair = computer.getPackagesUsingSharedLibrary(info, 0, + Process.SYSTEM_UID, userId); + final List dependents = usingSharedLibraryPair.first; + if (dependents == null) { + continue; + } + for (int k = 0; k < dependents.size(); k++) { + final VersionedPackage dependent = dependents.get(k); + final PackageStateInternal dependentState = + computer.getPackageStateInternal(dependent.getPackageName()); + if (dependentState == null) { + continue; + } + if (canSetOverlayPaths(dependentState.getUserStateOrDefault(userId) + .getSharedLibraryOverlayPaths() + .get(libName), newOverlayPaths)) { + String dependentPackageName = dependent.getPackageName(); + modifiedDependents = ArrayUtils.add(modifiedDependents, + dependentPackageName); + outUpdatedPackageNames.add(dependentPackageName); + } + } + + if (modifiedDependents != null) { + ArrayMap> libNameToModifiedDependents = + targetPkgToLibNameToModifiedDependents.get( + targetPackageName); + if (libNameToModifiedDependents == null) { + libNameToModifiedDependents = new ArrayMap<>(); + targetPkgToLibNameToModifiedDependents.put(targetPackageName, + libNameToModifiedDependents); + } + libNameToModifiedDependents.put(libName, modifiedDependents); + } + } + } + + if (canSetOverlayPaths(packageState.getUserStateOrDefault(userId).getOverlayPaths(), + newOverlayPaths)) { + outUpdatedPackageNames.add(targetPackageName); + } + } + + commitPackageStateMutation(null, mutator -> { + for (int i = 0; i < numberOfPendingChanges; i++) { + final String targetPackageName = pendingChanges.keyAt(i); + final OverlayPaths newOverlayPaths = pendingChanges.valueAt(i); + + if (!outUpdatedPackageNames.contains(targetPackageName)) { + continue; + } + + mutator.forPackage(targetPackageName) + .userState(userId) + .setOverlayPaths(newOverlayPaths); + + final ArrayMap> libNameToModifiedDependents = + targetPkgToLibNameToModifiedDependents.get( + targetPackageName); + if (libNameToModifiedDependents == null) { + continue; + } + + for (int mapIndex = 0; mapIndex < libNameToModifiedDependents.size(); + mapIndex++) { + String libName = libNameToModifiedDependents.keyAt(mapIndex); + ArraySet modifiedDependents = + libNameToModifiedDependents.valueAt(mapIndex); + for (int setIndex = 0; setIndex < modifiedDependents.size(); setIndex++) { + mutator.forPackage(modifiedDependents.valueAt(setIndex)) + .userState(userId) + .setOverlayPathsForLibrary(libName, newOverlayPaths); + } + } + } + }); + } + + if (userId == UserHandle.USER_SYSTEM) { + // Keep the overlays in the system application info (and anything special cased as well) + // up to date to make sure system ui is themed correctly. + for (int i = 0; i < numberOfPendingChanges; i++) { + final String targetPackageName = pendingChanges.keyAt(i); + final OverlayPaths newOverlayPaths = pendingChanges.valueAt(i); + maybeUpdateSystemOverlays(targetPackageName, newOverlayPaths); + } + } + + invalidatePackageInfoCache(); + } + + private boolean canSetOverlayPaths(OverlayPaths origPaths, OverlayPaths newPaths) { + if (Objects.equals(origPaths, newPaths)) { + return false; + } + if ((origPaths == null && newPaths.isEmpty()) + || (newPaths == null && origPaths.isEmpty())) { + return false; + } + return true; + } + + private void maybeUpdateSystemOverlays(String targetPackageName, OverlayPaths newOverlayPaths) { + if (!mResolverReplaced) { + if (targetPackageName.equals("android")) { + if (newOverlayPaths == null) { + mPlatformPackageOverlayPaths = null; + mPlatformPackageOverlayResourceDirs = null; + } else { + mPlatformPackageOverlayPaths = newOverlayPaths.getOverlayPaths().toArray( + new String[0]); + mPlatformPackageOverlayResourceDirs = newOverlayPaths.getResourceDirs().toArray( + new String[0]); + } + applyUpdatedSystemOverlayPaths(); + } + } else { + if (targetPackageName.equals(mResolveActivity.applicationInfo.packageName)) { + if (newOverlayPaths == null) { + mReplacedResolverPackageOverlayPaths = null; + mReplacedResolverPackageOverlayResourceDirs = null; + } else { + mReplacedResolverPackageOverlayPaths = + newOverlayPaths.getOverlayPaths().toArray(new String[0]); + mReplacedResolverPackageOverlayResourceDirs = + newOverlayPaths.getResourceDirs().toArray(new String[0]); + } + applyUpdatedSystemOverlayPaths(); + } + } + } + + private void applyUpdatedSystemOverlayPaths() { + if (mAndroidApplication == null) { + Slog.i(TAG, "Skipped the AndroidApplication overlay paths update - no app yet"); + } else { + mAndroidApplication.overlayPaths = mPlatformPackageOverlayPaths; + mAndroidApplication.resourceDirs = mPlatformPackageOverlayResourceDirs; + } + if (mResolverReplaced) { + mResolveActivity.applicationInfo.overlayPaths = mReplacedResolverPackageOverlayPaths; + mResolveActivity.applicationInfo.resourceDirs = + mReplacedResolverPackageOverlayResourceDirs; + } + } + + private void enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions( + @NonNull String message) { + if (mContext.checkCallingOrSelfPermission( + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) + != PackageManager.PERMISSION_GRANTED + && mContext.checkCallingOrSelfPermission( + Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException(message + " requires " + + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " or " + + Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS); + } + } + + // TODO: Remove + @Deprecated + @Nullable + @GuardedBy("mLock") + PackageSetting getPackageSettingForMutation(String packageName) { + return mSettings.getPackageLPr(packageName); + } + + // TODO: Remove + @Deprecated + @Nullable + @GuardedBy("mLock") + PackageSetting getDisabledPackageSettingForMutation(String packageName) { + return mSettings.getDisabledSystemPkgLPr(packageName); + } + + @Deprecated + void forEachPackageSetting(Consumer actionLocked) { + synchronized (mLock) { + int size = mSettings.getPackagesLocked().size(); + for (int index = 0; index < size; index++) { + actionLocked.accept(mSettings.getPackagesLocked().valueAt(index)); + } + } + } + + void forEachPackageState(@NonNull Computer snapshot, Consumer consumer) { + forEachPackageState(snapshot.getPackageStates(), consumer); + } + + void forEachPackage(@NonNull Computer snapshot, Consumer consumer) { + final ArrayMap packageStates = + snapshot.getPackageStates(); + int size = packageStates.size(); + for (int index = 0; index < size; index++) { + PackageStateInternal packageState = packageStates.valueAt(index); + if (packageState.getPkg() != null) { + consumer.accept(packageState.getPkg()); + } + } + } + + void forEachPackageInternal(@NonNull Computer snapshot, + @NonNull Consumer consumer) { + final ArrayMap packageStates = + snapshot.getPackageStates(); + int size = packageStates.size(); + for (int index = 0; index < size; index++) { + PackageStateInternal packageState = packageStates.valueAt(index); + if (packageState.getPkg() != null) { + consumer.accept(packageState.getPkg()); + } + } + } + + private void forEachPackageState( + @NonNull ArrayMap packageStates, + @NonNull Consumer consumer) { + int size = packageStates.size(); + for (int index = 0; index < size; index++) { + PackageStateInternal packageState = packageStates.valueAt(index); + consumer.accept(packageState); + } + } + + void forEachInstalledPackage(@NonNull Computer snapshot, @NonNull Consumer action, + @UserIdInt int userId) { + Consumer actionWrapped = packageState -> { + if (packageState.getPkg() != null + && packageState.getUserStateOrDefault(userId).isInstalled()) { + action.accept(packageState.getPkg()); + } + }; + forEachPackageState(snapshot.getPackageStates(), actionWrapped); + } + + boolean isHistoricalPackageUsageAvailable() { + return mPackageUsage.isHistoricalPackageUsageAvailable(); + } + + public CompilerStats.PackageStats getOrCreateCompilerPackageStats(AndroidPackage pkg) { + return getOrCreateCompilerPackageStats(pkg.getPackageName()); + } + + public CompilerStats.PackageStats getOrCreateCompilerPackageStats(String pkgName) { + return mCompilerStats.getOrCreatePackageStats(pkgName); + } + + void grantImplicitAccess(@NonNull Computer snapshot, @UserIdInt int userId, + Intent intent, @AppIdInt int recipientAppId, int visibleUid, boolean direct, + boolean retainOnUpdate) { + final AndroidPackage visiblePackage = snapshot.getPackage(visibleUid); + final int recipientUid = UserHandle.getUid(userId, recipientAppId); + if (visiblePackage == null || snapshot.getPackage(recipientUid) == null) { + return; + } + + final boolean instantApp = snapshot.isInstantAppInternal( + visiblePackage.getPackageName(), userId, visibleUid); + final boolean accessGranted; + if (instantApp) { + if (!direct) { + // if the interaction that lead to this granting access to an instant app + // was indirect (i.e.: URI permission grant), do not actually execute the + // grant. + return; + } + accessGranted = mInstantAppRegistry.grantInstantAccess(userId, intent, + recipientAppId, UserHandle.getAppId(visibleUid) /*instantAppId*/); + } else { + accessGranted = mAppsFilter.grantImplicitAccess(recipientUid, visibleUid, + retainOnUpdate); + } + + if (accessGranted) { + ApplicationPackageManager.invalidateGetPackagesForUidCache(); + } + } + + boolean canHaveOatDir(@NonNull Computer snapshot, String packageName) { + final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); + if (packageState == null || packageState.getPkg() == null) { + return false; + } + return AndroidPackageUtils.canHaveOatDir(packageState, packageState.getPkg()); + } + + long deleteOatArtifactsOfPackage(@NonNull Computer snapshot, String packageName) { + PackageManagerServiceUtils.enforceSystemOrRootOrShell( + "Only the system or shell can delete oat artifacts"); + + if (DexOptHelper.useArtService()) { + // TODO(chiuwinson): Retrieve filtered snapshot from Computer instance instead. + try (PackageManagerLocal.FilteredSnapshot filteredSnapshot = + PackageManagerServiceUtils.getPackageManagerLocal() + .withFilteredSnapshot()) { + try { + DeleteResult res = DexOptHelper.getArtManagerLocal().deleteDexoptArtifacts( + filteredSnapshot, packageName); + return res.getFreedBytes(); + } catch (IllegalArgumentException e) { + Log.e(TAG, e.toString()); + return -1; + } catch (IllegalStateException e) { + Slog.wtfStack(TAG, e.toString()); + return -1; + } + } + } else { + PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); + if (packageState == null || packageState.getPkg() == null) { + return -1; // error code of deleteOptimizedFiles + } + try { + return mDexManager.deleteOptimizedFiles( + ArtUtils.createArtPackageInfo(packageState.getPkg(), packageState)); + } catch (LegacyDexoptDisabledException e) { + throw new RuntimeException(e); + } + } + } + + List getMimeGroupInternal(@NonNull Computer snapshot, String packageName, + String mimeGroup) { + final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); + if (packageState == null) { + return Collections.emptyList(); + } + + final Map> mimeGroups = packageState.getMimeGroups(); + Set mimeTypes = mimeGroups != null ? mimeGroups.get(mimeGroup) : null; + if (mimeTypes == null) { + throw new IllegalArgumentException("Unknown MIME group " + mimeGroup + + " for package " + packageName); + } + return new ArrayList<>(mimeTypes); + } + + /** + * Temporary method that wraps mSettings.writeLPr() and calls mPermissionManager's + * writeLegacyPermissionsTEMP() beforehand. + * + * TODO: In the meantime, can this be moved to a schedule call? + * TODO(b/182523293): This should be removed once we finish migration of permission storage. + */ + @SuppressWarnings("GuardedBy") + void writeSettingsLPrTEMP(boolean sync) { + snapshotComputer(false); + mPermissionManager.writeLegacyPermissionsTEMP(mSettings.mPermissions); + mSettings.writeLPr(mLiveComputer, sync); + } + + // Default async version. + void writeSettingsLPrTEMP() { + writeSettingsLPrTEMP(/*sync=*/false); + } + + @Override + public void verifyHoldLockToken(IBinder token) { + if (!Build.IS_DEBUGGABLE) { + throw new SecurityException("holdLock requires a debuggable build"); + } + + if (token == null) { + throw new SecurityException("null holdLockToken"); + } + + if (token.queryLocalInterface("holdLock:" + Binder.getCallingUid()) != this) { + throw new SecurityException("Invalid holdLock() token"); + } + } + + static String getDefaultTimeouts() { + final long token = Binder.clearCallingIdentity(); + try { + return DeviceConfig.getString(NAMESPACE_PACKAGE_MANAGER_SERVICE, + PROPERTY_INCFS_DEFAULT_TIMEOUTS, ""); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + static String getKnownDigestersList() { + final long token = Binder.clearCallingIdentity(); + try { + return DeviceConfig.getString(NAMESPACE_PACKAGE_MANAGER_SERVICE, + PROPERTY_KNOWN_DIGESTERS_LIST, ""); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + static boolean isPreapprovalRequestAvailable() { + final long token = Binder.clearCallingIdentity(); + try { + if (!Resources.getSystem().getBoolean( + com.android.internal.R.bool.config_isPreApprovalRequestAvailable)) { + return false; + } + return DeviceConfig.getBoolean(NAMESPACE_PACKAGE_MANAGER_SERVICE, + PROPERTY_IS_PRE_APPROVAL_REQUEST_AVAILABLE, true /* defaultValue */); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + static boolean isUpdateOwnershipEnforcementAvailable() { + final long token = Binder.clearCallingIdentity(); + try { + return DeviceConfig.getBoolean(NAMESPACE_PACKAGE_MANAGER_SERVICE, + PROPERTY_IS_UPDATE_OWNERSHIP_ENFORCEMENT_AVAILABLE, true /* defaultValue */); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Returns the array containing per-uid timeout configuration. + * This is derived from DeviceConfig flags. + */ + public @NonNull PerUidReadTimeouts[] getPerUidReadTimeouts(@NonNull Computer snapshot) { + PerUidReadTimeouts[] result = mPerUidReadTimeoutsCache; + if (result == null) { + result = parsePerUidReadTimeouts(snapshot); + mPerUidReadTimeoutsCache = result; + } + return result; + } + + private @NonNull PerUidReadTimeouts[] parsePerUidReadTimeouts(@NonNull Computer snapshot) { + final String defaultTimeouts = getDefaultTimeouts(); + final String knownDigestersList = getKnownDigestersList(); + final List perPackageReadTimeouts = + PerPackageReadTimeouts.parseDigestersList(defaultTimeouts, knownDigestersList); + + if (perPackageReadTimeouts.size() == 0) { + return EMPTY_PER_UID_READ_TIMEOUTS_ARRAY; + } + + final int[] allUsers = mInjector.getUserManagerService().getUserIds(); + final List result = new ArrayList<>(perPackageReadTimeouts.size()); + for (int i = 0, size = perPackageReadTimeouts.size(); i < size; ++i) { + final PerPackageReadTimeouts perPackage = perPackageReadTimeouts.get(i); + final PackageStateInternal ps = + snapshot.getPackageStateInternal(perPackage.packageName); + if (ps == null) { + if (DEBUG_PER_UID_READ_TIMEOUTS) { + Slog.i(TAG, "PerUidReadTimeouts: package not found = " + + perPackage.packageName); + } + continue; + } + if (ps.getAppId() < Process.FIRST_APPLICATION_UID) { + if (DEBUG_PER_UID_READ_TIMEOUTS) { + Slog.i(TAG, "PerUidReadTimeouts: package is system, appId=" + + ps.getAppId()); + } + continue; + } + + final AndroidPackage pkg = ps.getPkg(); + if (pkg.getLongVersionCode() < perPackage.versionCodes.minVersionCode + || pkg.getLongVersionCode() > perPackage.versionCodes.maxVersionCode) { + if (DEBUG_PER_UID_READ_TIMEOUTS) { + Slog.i(TAG, "PerUidReadTimeouts: version code is not in range = " + + perPackage.packageName + ":" + pkg.getLongVersionCode()); + } + continue; + } + if (perPackage.sha256certificate != null + && !pkg.getSigningDetails().hasSha256Certificate( + perPackage.sha256certificate)) { + if (DEBUG_PER_UID_READ_TIMEOUTS) { + Slog.i(TAG, "PerUidReadTimeouts: invalid certificate = " + + perPackage.packageName + ":" + pkg.getLongVersionCode()); + } + continue; + } + for (int userId : allUsers) { + if (!ps.getUserStateOrDefault(userId).isInstalled()) { + continue; + } + final int uid = UserHandle.getUid(userId, ps.getAppId()); + final PerUidReadTimeouts perUid = new PerUidReadTimeouts(); + perUid.uid = uid; + perUid.minTimeUs = perPackage.timeouts.minTimeUs; + perUid.minPendingTimeUs = perPackage.timeouts.minPendingTimeUs; + perUid.maxPendingTimeUs = perPackage.timeouts.maxPendingTimeUs; + result.add(perUid); + } + } + return result.toArray(new PerUidReadTimeouts[result.size()]); + } + + void setKeepUninstalledPackagesInternal(@NonNull Computer snapshot, List packageList) { + Preconditions.checkNotNull(packageList); + synchronized (mKeepUninstalledPackages) { + List toRemove = new ArrayList<>(mKeepUninstalledPackages); + toRemove.removeAll(packageList); // Do not remove anything still in the list + + mKeepUninstalledPackages.clear(); + mKeepUninstalledPackages.addAll(packageList); + + for (int i = 0; i < toRemove.size(); i++) { + deletePackageIfUnused(snapshot, toRemove.get(i)); + } + } + } + + boolean shouldKeepUninstalledPackageLPr(String packageName) { + synchronized (mKeepUninstalledPackages) { + return mKeepUninstalledPackages.contains(packageName); + } + } + + boolean getSafeMode() { + return mSafeMode; + } + + ComponentName getResolveComponentName() { + return mResolveComponentName; + } + + DefaultAppProvider getDefaultAppProvider() { + return mDefaultAppProvider; + } + + File getCacheDir() { + return mCacheDir; + } + + PackageProperty getPackageProperty() { + return mPackageProperty; + } + + WatchedArrayMap getInstrumentation() { + return mInstrumentation; + } + + int getSdkVersion() { + return mSdkVersion; + } + + void addAllPackageProperties(@NonNull AndroidPackage pkg) { + mPackageProperty.addAllProperties(pkg); + } + + void addInstrumentation(ComponentName name, ParsedInstrumentation instrumentation) { + mInstrumentation.put(name, instrumentation); + } + + String[] getKnownPackageNamesInternal(@NonNull Computer snapshot, int knownPackage, + int userId) { + return new KnownPackages( + mDefaultAppProvider, + mRequiredInstallerPackage, + mRequiredUninstallerPackage, + mSetupWizardPackage, + mRequiredVerifierPackages, + mDefaultTextClassifierPackage, + mSystemTextClassifierPackageName, + mRequiredPermissionControllerPackage, + mConfiguratorPackage, + mIncidentReportApproverPackage, + mAmbientContextDetectionPackage, + mWearableSensingPackage, + mAppPredictionServicePackage, + COMPANION_PACKAGE_NAME, + mRetailDemoPackage, + mOverlayConfigSignaturePackage, + mRecentsPackage) + .getKnownPackageNames(snapshot, knownPackage, userId); + } + + String getActiveLauncherPackageName(int userId) { + return mDefaultAppProvider.getDefaultHome(userId); + } + + boolean setActiveLauncherPackage(@NonNull String packageName, @UserIdInt int userId, + @NonNull Consumer callback) { + return mDefaultAppProvider.setDefaultHome(packageName, userId, mContext.getMainExecutor(), + callback); + } + + @Nullable + String getDefaultBrowser(@UserIdInt int userId) { + return mDefaultAppProvider.getDefaultBrowser(userId); + } + + void setDefaultBrowser(@Nullable String packageName, @UserIdInt int userId) { + mDefaultAppProvider.setDefaultBrowser(packageName, userId); + } + + PackageUsage getPackageUsage() { + return mPackageUsage; + } + + String getModuleMetadataPackageName() { + return mModuleInfoProvider.getPackageName(); + } + + File getAppInstallDir() { + return mAppInstallDir; + } + + boolean isExpectingBetter(String packageName) { + return mInitAppsHelper.isExpectingBetter(packageName); + } + + int getDefParseFlags() { + return mDefParseFlags; + } + + void setUpCustomResolverActivity(AndroidPackage pkg, PackageSetting pkgSetting) { + synchronized (mLock) { + mResolverReplaced = true; + + // The instance created in PackageManagerService is special cased to be non-user + // specific, so initialize all the needed fields here. + ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(pkg, 0, + PackageUserStateInternal.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting); + + // Set up information for custom user intent resolution activity. + mResolveActivity.applicationInfo = appInfo; + mResolveActivity.name = mCustomResolverComponentName.getClassName(); + mResolveActivity.packageName = pkg.getPackageName(); + mResolveActivity.processName = pkg.getProcessName(); + mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; + mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS + | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS + | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES + | ActivityInfo.FLAG_HARDWARE_ACCELERATED; + mResolveActivity.theme = 0; + mResolveActivity.exported = true; + mResolveActivity.enabled = true; + mResolveInfo.activityInfo = mResolveActivity; + mResolveInfo.priority = 0; + mResolveInfo.preferredOrder = 0; + mResolveInfo.match = 0; + mResolveComponentName = mCustomResolverComponentName; + PackageManagerService.onChanged(); + Slog.i(TAG, "Replacing default ResolverActivity with custom activity: " + + mResolveComponentName); + } + } + + void setPlatformPackage(AndroidPackage pkg, PackageSetting pkgSetting) { + synchronized (mLock) { + // Set up information for our fall-back user intent resolution activity. + mPlatformPackage = pkg; + + // The instance stored in PackageManagerService is special cased to be non-user + // specific, so initialize all the needed fields here. + mAndroidApplication = PackageInfoUtils.generateApplicationInfo(pkg, 0, + PackageUserStateInternal.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting); + + 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 + | ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY + | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES + | ActivityInfo.FLAG_HARDWARE_ACCELERATED; + 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); + } + PackageManagerService.onChanged(); + } + applyUpdatedSystemOverlayPaths(); + } + + ApplicationInfo getCoreAndroidApplication() { + return mAndroidApplication; + } + + boolean isSystemReady() { + return mSystemReady; + } + + AndroidPackage getPlatformPackage() { + return mPlatformPackage; + } + + boolean isPreNMR1Upgrade() { + return mIsPreNMR1Upgrade; + } + + boolean isOverlayMutable(String packageName) { + return mOverlayConfig.isMutable(packageName); + } + + @ScanFlags int getSystemPackageScanFlags(File codePath) { + List dirsToScanAsSystem = + mInitAppsHelper.getDirsToScanAsSystem(); + @PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM; + for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) { + ScanPartition partition = dirsToScanAsSystem.get(i); + if (partition.containsFile(codePath)) { + scanFlags |= partition.scanFlag; + if (partition.containsPrivApp(codePath)) { + scanFlags |= SCAN_AS_PRIVILEGED; + } + break; + } + } + return scanFlags; + } + + Pair getSystemPackageRescanFlagsAndReparseFlags(File scanFile, + int systemScanFlags, int systemParseFlags) { + List dirsToScanAsSystem = + mInitAppsHelper.getDirsToScanAsSystem(); + @ParsingPackageUtils.ParseFlags int reparseFlags = 0; + @PackageManagerService.ScanFlags int rescanFlags = 0; + for (int i1 = dirsToScanAsSystem.size() - 1; i1 >= 0; i1--) { + final ScanPartition partition = dirsToScanAsSystem.get(i1); + if (partition.containsPrivApp(scanFile)) { + reparseFlags = systemParseFlags; + rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED + | partition.scanFlag; + break; + } + if (partition.containsApp(scanFile)) { + reparseFlags = systemParseFlags; + rescanFlags = systemScanFlags | partition.scanFlag; + break; + } + } + return new Pair<>(rescanFlags, reparseFlags); + } + + + /** + * @see PackageManagerInternal#recordInitialState() + */ + @NonNull + public PackageStateMutator.InitialState recordInitialState() { + return mPackageStateMutator.initialState(mChangedPackagesTracker.getSequenceNumber()); + } + + /** + * @see PackageManagerInternal#commitPackageStateMutation(PackageStateMutator.InitialState, + * Consumer) + */ + @NonNull + public PackageStateMutator.Result commitPackageStateMutation( + @Nullable PackageStateMutator.InitialState initialState, + @NonNull Consumer consumer) { + synchronized (mPackageStateWriteLock) { + final PackageStateMutator.Result result = mPackageStateMutator.generateResult( + initialState, mChangedPackagesTracker.getSequenceNumber()); + if (result != PackageStateMutator.Result.SUCCESS) { + return result; + } + + consumer.accept(mPackageStateMutator); + mPackageStateMutator.onFinished(); + } + + return PackageStateMutator.Result.SUCCESS; + } + + /** + * @see PackageManagerInternal#commitPackageStateMutation(PackageStateMutator.InitialState, + * Consumer) + */ + @NonNull + public PackageStateMutator.Result commitPackageStateMutation( + @Nullable PackageStateMutator.InitialState initialState, @NonNull String packageName, + @NonNull Consumer consumer) { + PackageStateMutator.Result result = null; + if (Thread.holdsLock(mPackageStateWriteLock)) { + // If the thread is already holding the lock, this is likely a retry based on a prior + // failure, and re-calculating whether a state change occurred can be skipped. + result = PackageStateMutator.Result.SUCCESS; + } + synchronized (mPackageStateWriteLock) { + if (result == null) { + // If the thread wasn't previously holding, this is a first-try commit and so a + // state change may have happened. + result = mPackageStateMutator.generateResult( + initialState, mChangedPackagesTracker.getSequenceNumber()); + } + if (result != PackageStateMutator.Result.SUCCESS) { + return result; + } + + PackageStateWrite state = mPackageStateMutator.forPackage(packageName); + if (state == null) { + return PackageStateMutator.Result.SPECIFIC_PACKAGE_NULL; + } else { + consumer.accept(state); + } + + state.onChanged(); + } + + return PackageStateMutator.Result.SUCCESS; + } + + void notifyInstantAppPackageInstalled(String packageName, int[] newUsers) { + mInstantAppRegistry.onPackageInstalled(snapshotComputer(), packageName, newUsers); + } + + void addInstallerPackageName(InstallSource installSource) { + synchronized (mLock) { + mSettings.addInstallerPackageNames(installSource); + } + } + + public void reconcileSdkData(@Nullable String volumeUuid, @NonNull String packageName, + @NonNull List subDirNames, int userId, int appId, int previousAppId, + @NonNull String seInfo, int flags) throws IOException { + synchronized (mInstallLock) { + ReconcileSdkDataArgs args = mInstaller.buildReconcileSdkDataArgs(volumeUuid, + packageName, subDirNames, userId, appId, seInfo, + flags); + args.previousAppId = previousAppId; + try { + mInstaller.reconcileSdkData(args); + } catch (InstallerException e) { + throw new IOException(e.getMessage()); + } + } + } + + void removeCodePath(@Nullable File codePath) { + mRemovePackageHelper.removeCodePath(codePath); + } + + void cleanUpResources(@NonNull String packageName, @NonNull File codeFile, + @NonNull String[] instructionSets) { + mRemovePackageHelper.cleanUpResources(packageName, codeFile, instructionSets); + } + + void cleanUpForMoveInstall(String volumeUuid, String packageName, String fromCodePath) { + mRemovePackageHelper.cleanUpForMoveInstall(volumeUuid, packageName, fromCodePath); + } + + void sendPendingBroadcasts() { + mInstallPackageHelper.sendPendingBroadcasts(); + } + + void handlePackagePostInstall(@NonNull InstallRequest request, boolean launchedForRestore) { + mInstallPackageHelper.handlePackagePostInstall(request, launchedForRestore); + } + + Pair installExistingPackageAsUser( + @Nullable String packageName, + @UserIdInt int userId, @PackageManager.InstallFlags int installFlags, + @PackageManager.InstallReason int installReason, + @Nullable List allowlistedRestrictedPermissions, + @Nullable IntentSender intentSender) { + return mInstallPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags, + installReason, allowlistedRestrictedPermissions, intentSender); + } + AndroidPackage initPackageTracedLI(File scanFile, final int parseFlags, int scanFlags) + throws PackageManagerException { + return mInstallPackageHelper.initPackageTracedLI(scanFile, parseFlags, scanFlags); + } + + void restoreDisabledSystemPackageLIF(@NonNull DeletePackageAction action, + @NonNull int[] allUserHandles, + boolean writeSettings) throws SystemDeleteException { + mInstallPackageHelper.restoreDisabledSystemPackageLIF( + action, allUserHandles, writeSettings); + } + boolean enableCompressedPackage(@NonNull AndroidPackage stubPkg, + @NonNull PackageSetting stubPs) { + return mInstallPackageHelper.enableCompressedPackage(stubPkg, stubPs); + } + + void installPackagesTraced(List requests) { + mInstallPackageHelper.installPackagesTraced(requests); + } + + void restoreAndPostInstall(InstallRequest request) { + mInstallPackageHelper.restoreAndPostInstall(request); + } + + Pair verifyReplacingVersionCode(@NonNull PackageInfoLite pkgLite, + long requiredInstalledVersionCode, + int installFlags) { + return mInstallPackageHelper.verifyReplacingVersionCode( + pkgLite, requiredInstalledVersionCode, installFlags); + } + + int getUidForVerifier(VerifierInfo verifierInfo) { + return mInstallPackageHelper.getUidForVerifier(verifierInfo); + } + + int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags, + boolean removedBySystem) { + return mDeletePackageHelper.deletePackageX(packageName, + PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM, + PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/); + } +} diff --git a/aosp/frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/aosp/frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..3e840589b58a1ba5c6f6363fd23bc5ac5f849176 --- /dev/null +++ b/aosp/frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -0,0 +1,5361 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.Manifest.permission.GET_APP_METADATA; +import static android.content.pm.PackageInstaller.LOCATION_DATA_APP; +import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; +import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT; +import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; +import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; +import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; +import static android.content.pm.PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS; +import static android.content.pm.PackageManager.RESTRICTION_HIDE_NOTIFICATIONS; +import static android.content.pm.PackageManager.RESTRICTION_NONE; + +import static com.android.server.LocalManagerRegistry.ManagerNotFoundException; +import static com.android.server.pm.PackageManagerService.DEFAULT_FILE_ACCESS_MODE; +import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; + +import android.accounts.IAccountManager; +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.app.ActivityManager; +import android.app.ActivityManagerInternal; +import android.app.role.RoleManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.IIntentReceiver; +import android.content.IIntentSender; +import android.content.Intent; +import android.content.IntentSender; +import android.content.pm.ApplicationInfo; +import android.content.pm.ArchivedPackageParcel; +import android.content.pm.FeatureInfo; +import android.content.pm.IPackageDataObserver; +import android.content.pm.IPackageInstaller; +import android.content.pm.IPackageManager; +import android.content.pm.InstrumentationInfo; +import android.content.pm.ModuleInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageInstaller.SessionInfo; +import android.content.pm.PackageInstaller.SessionParams; +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageManagerInternal; +import android.content.pm.ParceledListSlice; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.SharedLibraryInfo; +import android.content.pm.SuspendDialogInfo; +import android.content.pm.UserInfo; +import android.content.pm.VersionedPackage; +import android.content.pm.dex.ArtManager; +import android.content.pm.dex.DexMetadataHelper; +import android.content.pm.dex.ISnapshotRuntimeProfileCallback; +import android.content.pm.parsing.ApkLite; +import android.content.pm.parsing.ApkLiteParseUtils; +import android.content.pm.parsing.PackageLite; +import android.content.pm.parsing.result.ParseResult; +import android.content.pm.parsing.result.ParseTypeImpl; +import android.content.res.AssetManager; +import android.content.res.Resources; +import android.content.rollback.PackageRollbackInfo; +import android.content.rollback.RollbackInfo; +import android.content.rollback.RollbackManager; +import android.net.Uri; +import android.os.Binder; +import android.os.Build; +import android.os.Bundle; +import android.os.IBinder; +import android.os.IUserManager; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.ParcelFileDescriptor.AutoCloseInputStream; +import android.os.PersistableBundle; +import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceSpecificException; +import android.os.ShellCommand; +import android.os.SystemClock; +import android.os.Trace; +import android.os.UserHandle; +import android.os.UserManager; +import android.os.incremental.V4Signature; +import android.os.storage.StorageManager; +import android.permission.PermissionManager; +import android.system.ErrnoException; +import android.system.Os; +import android.text.TextUtils; +import android.text.format.DateUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.IntArray; +import android.util.Pair; +import android.util.PrintWriterPrinter; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.content.InstallLocationUtils; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.Preconditions; +import com.android.server.FgThread; +import com.android.server.LocalManagerRegistry; +import com.android.server.LocalServices; +import com.android.server.SystemConfig; +import com.android.server.art.ArtManagerLocal; +import com.android.server.pm.Installer.LegacyDexoptDisabledException; +import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata; +import com.android.server.pm.permission.LegacyPermissionManagerInternal; +import com.android.server.pm.permission.PermissionAllowlist; +import com.android.server.pm.verify.domain.DomainVerificationShell; + +import dalvik.system.DexFile; + +import libcore.io.IoUtils; +import libcore.io.Streams; +import libcore.util.HexEncoding; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.net.URISyntaxException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +class PackageManagerShellCommand extends ShellCommand { + /** Path for streaming APK content */ + private static final String STDIN_PATH = "-"; + /** Path where ART profiles snapshots are dumped for the shell user */ + private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/"; + private static final int DEFAULT_STAGED_READY_TIMEOUT_MS = 60 * 1000; + private static final String TAG = "PackageManagerShellCommand"; + private static final Set UNSUPPORTED_INSTALL_CMD_OPTS = Set.of( + "--multi-package" + ); + private static final Set UNSUPPORTED_SESSION_CREATE_OPTS = Collections.emptySet(); + private static final Map SUPPORTED_PERMISSION_FLAGS = new ArrayMap<>(); + private static final List SUPPORTED_PERMISSION_FLAGS_LIST; + static { + SUPPORTED_PERMISSION_FLAGS_LIST = List.of("review-required", "revoked-compat", + "revoke-when-requested", "user-fixed", "user-set"); + SUPPORTED_PERMISSION_FLAGS.put("user-set", FLAG_PERMISSION_USER_SET); + SUPPORTED_PERMISSION_FLAGS.put("user-fixed", FLAG_PERMISSION_USER_FIXED); + SUPPORTED_PERMISSION_FLAGS.put("revoked-compat", FLAG_PERMISSION_REVOKED_COMPAT); + SUPPORTED_PERMISSION_FLAGS.put("review-required", FLAG_PERMISSION_REVIEW_REQUIRED); + SUPPORTED_PERMISSION_FLAGS.put("revoke-when-requested", + FLAG_PERMISSION_REVOKE_WHEN_REQUESTED); + } + // For backward compatibility. DO NOT add new commands here. New ART Service commands should be + // added under the "art" namespace. + private static final Set ART_SERVICE_COMMANDS = Set.of("compile", + "reconcile-secondary-dex-files", "force-dex-opt", "bg-dexopt-job", + "cancel-bg-dexopt-job", "delete-dexopt", "dump-profiles", "snapshot-profile", "art"); + + final IPackageManager mInterface; + private final PackageManagerInternal mPm; + final LegacyPermissionManagerInternal mLegacyPermissionManager; + final PermissionManager mPermissionManager; + final Context mContext; + final DomainVerificationShell mDomainVerificationShell; + final private WeakHashMap mResourceCache = + new WeakHashMap(); + int mTargetUser; + boolean mBrief; + boolean mComponents; + int mQueryFlags; + + private static final SecureRandom RANDOM = new SecureRandom(); + + private static final int DELETE_UPDATE_SHARE_APP = 0x00000010; + + PackageManagerShellCommand(@NonNull IPackageManager packageManager, + @NonNull Context context, @NonNull DomainVerificationShell domainVerificationShell) { + mInterface = packageManager; + mPm = LocalServices.getService(PackageManagerInternal.class); + mLegacyPermissionManager = LocalServices.getService(LegacyPermissionManagerInternal.class); + mPermissionManager = context.getSystemService(PermissionManager.class); + mContext = context; + mDomainVerificationShell = domainVerificationShell; + } + + @Override + public int onCommand(String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); + } + + final PrintWriter pw = getOutPrintWriter(); + try { + switch (cmd) { + case "help": + onHelp(); + return 0; + case "path": + return runPath(); + case "dump": + return runDump(); + case "dump-package": + return runDumpPackage(); + case "list": + return runList(); + case "gc": + return runGc(); + case "resolve-activity": + return runResolveActivity(); + case "query-activities": + return runQueryIntentActivities(); + case "query-services": + return runQueryIntentServices(); + case "query-receivers": + return runQueryIntentReceivers(); + case "install": + return runInstall(); + case "scan-fast": + return runScanFast(); + case "install-streaming": + return runStreamingInstall(); + case "install-incremental": + return runIncrementalInstall(); + case "install-abandon": + case "install-destroy": + return runInstallAbandon(); + case "install-commit": + return runInstallCommit(); + case "install-create": + return runInstallCreate(); + case "install-remove": + return runInstallRemove(); + case "install-write": + return runInstallWrite(); + case "install-existing": + return runInstallExisting(); + case "set-install-location": + return runSetInstallLocation(); + case "get-install-location": + return runGetInstallLocation(); + case "install-add-session": + return runInstallAddSession(); + case "install-set-pre-verified-domains": + return runInstallSetPreVerifiedDomains(); + case "install-get-pre-verified-domains": + return runInstallGetPreVerifiedDomains(); + case "move-package": + return runMovePackage(); + case "move-primary-storage": + return runMovePrimaryStorage(); + case "uninstall": + return runUninstall(); + case "clear": + return runClear(); + case "get-archived-package-metadata": + return runGetArchivedPackageMetadata(); + case "install-archived": + return runArchivedInstall(); + case "enable": + return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); + case "disable": + return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED); + case "disable-user": + return runSetEnabledSetting( + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER); + case "disable-until-used": + return runSetEnabledSetting( + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED); + case "default-state": + return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT); + case "hide": + return runSetHiddenSetting(true); + case "unhide": + return runSetHiddenSetting(false); + case "unstop": + return runSetStoppedState(false); + case "suspend": + return runSuspend(true, 0); + case "suspend-quarantine": + return runSuspend(true, PackageManager.FLAG_SUSPEND_QUARANTINED); + case "unsuspend": + return runSuspend(false, 0); + case "set-distracting-restriction": + return runSetDistractingRestriction(); + case "get-distracting-restriction": + return runGetDistractingRestriction(); + case "grant": + return runGrantRevokePermission(true); + case "revoke": + return runGrantRevokePermission(false); + case "reset-permissions": + return runResetPermissions(); + case "set-permission-flags": + return setOrClearPermissionFlags(true); + case "clear-permission-flags": + return setOrClearPermissionFlags(false); + case "set-permission-enforced": + return runSetPermissionEnforced(); + case "get-privapp-permissions": + return runGetPrivappPermissions(); + case "get-privapp-deny-permissions": + return runGetPrivappDenyPermissions(); + case "get-oem-permissions": + return runGetOemPermissions(); + case "trim-caches": + return runTrimCaches(); + case "create-user": + return runCreateUser(); + case "remove-user": + return runRemoveUser(); + case "mark-guest-for-deletion": + return runMarkGuestForDeletion(); + case "rename-user": + return runRenameUser(); + case "set-user-restriction": + return runSetUserRestriction(); + case "get-user-restriction": + return runGetUserRestriction(); + case "supports-multiple-users": + return runSupportsMultipleUsers(); + case "get-max-users": + return runGetMaxUsers(); + case "get-max-running-users": + return runGetMaxRunningUsers(); + case "set-home-activity": + return runSetHomeActivity(); + case "set-installer": + return runSetInstaller(); + case "get-instantapp-resolver": + return runGetInstantAppResolver(); + case "has-feature": + return runHasFeature(); + case "set-harmful-app-warning": + return runSetHarmfulAppWarning(); + case "get-harmful-app-warning": + return runGetHarmfulAppWarning(); + case "get-stagedsessions": + return runListStagedSessions(); + case "uninstall-system-updates": + String packageName = getNextArg(); + return uninstallSystemUpdates(packageName); + case "rollback-app": + return runRollbackApp(); + case "get-moduleinfo": + return runGetModuleInfo(); + case "log-visibility": + return runLogVisibility(); + case "bypass-staged-installer-check": + return runBypassStagedInstallerCheck(); + case "bypass-allowed-apex-update-check": + return runBypassAllowedApexUpdateCheck(); + case "disable-verification-for-uid": + return runDisableVerificationForUid(); + case "set-silent-updates-policy": + return runSetSilentUpdatesPolicy(); + case "get-app-metadata": + return runGetAppMetadata(); + case "clear-package-preferred-activities": + return runClearPackagePreferredActivities(); + case "wait-for-handler": + return runWaitForHandler(/* forBackgroundHandler= */ false); + case "wait-for-background-handler": + return runWaitForHandler(/* forBackgroundHandler= */ true); + case "archive": + return runArchive(); + case "request-unarchive": + return runUnarchive(); + case "get-domain-verification-agent": + return runGetDomainVerificationAgent(); + default: { + if (ART_SERVICE_COMMANDS.contains(cmd)) { + if (DexOptHelper.useArtService()) { + return runArtServiceCommand(); + } else { + try { + return runLegacyDexoptCommand(cmd); + } catch (LegacyDexoptDisabledException e) { + throw new RuntimeException(e); + } + } + } + + Boolean domainVerificationResult = + mDomainVerificationShell.runCommand(this, cmd); + if (domainVerificationResult != null) { + return domainVerificationResult ? 0 : 1; + } + + String nextArg = getNextArg(); + if (nextArg == null) { + if (cmd.equalsIgnoreCase("-l")) { + return runListPackages(false); + } else if (cmd.equalsIgnoreCase("-lf")) { + return runListPackages(true); + } + } else if (getNextArg() == null) { + if (cmd.equalsIgnoreCase("-p")) { + return displayPackageFilePath(nextArg, UserHandle.USER_SYSTEM); + } + } + return handleDefaultCommands(cmd); + } + } + } catch (RemoteException e) { + pw.println("Remote exception: " + e); + } + return -1; + } + + private int runLegacyDexoptCommand(@NonNull String cmd) + throws RemoteException, LegacyDexoptDisabledException { + Installer.checkLegacyDexoptDisabled(); + + if (!PackageManagerServiceUtils.isRootOrShell(Binder.getCallingUid())) { + throw new SecurityException("Dexopt shell commands need root or shell access"); + } + + switch (cmd) { + case "compile": + return runCompile(); + case "reconcile-secondary-dex-files": + return runreconcileSecondaryDexFiles(); + case "force-dex-opt": + return runForceDexOpt(); + case "bg-dexopt-job": + return runBgDexOpt(); + case "cancel-bg-dexopt-job": + return cancelBgDexOptJob(); + case "delete-dexopt": + return runDeleteDexOpt(); + case "dump-profiles": + return runDumpProfiles(); + case "snapshot-profile": + return runSnapshotProfile(); + case "art": + getOutPrintWriter().println("ART Service not enabled"); + return -1; + default: + // Can't happen. + throw new IllegalArgumentException(); + } + } + + /** + * Shows module info + * + * Usage: get-moduleinfo [--all | --installed] [module-name] + * Example: get-moduleinfo, get-moduleinfo --all, get-moduleinfo xyz + */ + private int runGetModuleInfo() { + final PrintWriter pw = getOutPrintWriter(); + int flags = 0; + + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--all": + flags |= PackageManager.MATCH_ALL; + break; + case "--installed": + break; + default: + pw.println("Error: Unknown option: " + opt); + return -1; + } + } + + String moduleName = getNextArg(); + try { + if (moduleName != null) { + ModuleInfo m = mInterface.getModuleInfo(moduleName, flags); + pw.println(m.toString() + " packageName: " + m.getPackageName()); + + } else { + List modules = mInterface.getInstalledModules(flags); + for (ModuleInfo m: modules) { + pw.println(m.toString() + " packageName: " + m.getPackageName()); + } + } + } catch (RemoteException e) { + pw.println("Failure [" + e.getClass().getName() + " - " + e.getMessage() + "]"); + return -1; + } + return 1; + } + + private int runLogVisibility() { + final PrintWriter pw = getOutPrintWriter(); + boolean enable = true; + + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--disable": + enable = false; + break; + case "--enable": + enable = true; + break; + default: + pw.println("Error: Unknown option: " + opt); + return -1; + } + } + + String packageName = getNextArg(); + if (packageName != null) { + LocalServices.getService(PackageManagerInternal.class) + .setVisibilityLogging(packageName, enable); + } else { + getErrPrintWriter().println("Error: no package specified"); + return -1; + } + return 1; + } + + private int runBypassStagedInstallerCheck() { + final PrintWriter pw = getOutPrintWriter(); + try { + mInterface.getPackageInstaller() + .bypassNextStagedInstallerCheck(Boolean.parseBoolean(getNextArg())); + return 0; + } catch (RemoteException e) { + pw.println("Failure [" + + e.getClass().getName() + " - " + + e.getMessage() + "]"); + return -1; + } + } + + private int runBypassAllowedApexUpdateCheck() { + final PrintWriter pw = getOutPrintWriter(); + try { + mInterface.getPackageInstaller() + .bypassNextAllowedApexUpdateCheck(Boolean.parseBoolean(getNextArg())); + return 0; + } catch (RemoteException e) { + pw.println("Failure [" + + e.getClass().getName() + " - " + + e.getMessage() + "]"); + return -1; + } + } + + private int runDisableVerificationForUid() { + final PrintWriter pw = getOutPrintWriter(); + try { + int uid = Integer.parseInt(getNextArgRequired()); + var amInternal = LocalServices.getService(ActivityManagerInternal.class); + boolean isInstrumented = + amInternal.getInstrumentationSourceUid(uid) != Process.INVALID_UID; + if (isInstrumented) { + mInterface.getPackageInstaller().disableVerificationForUid(uid); + return 0; + } else { + // Only available for testing + pw.println("Error: must specify an instrumented uid"); + return -1; + } + } catch (RemoteException e) { + pw.println("Failure [" + + e.getClass().getName() + " - " + + e.getMessage() + "]"); + return -1; + } + } + + private int uninstallSystemUpdates(String packageName) { + final PrintWriter pw = getOutPrintWriter(); + boolean failedUninstalls = false; + try { + final IPackageInstaller installer = mInterface.getPackageInstaller(); + final List list; + if (packageName == null) { + final ParceledListSlice packages = + mInterface.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY + | PackageManager.MATCH_UNINSTALLED_PACKAGES, + UserHandle.USER_SYSTEM); + list = packages.getList(); + } else { + list = new ArrayList<>(1); + list.add(mInterface.getApplicationInfo(packageName, PackageManager.MATCH_SYSTEM_ONLY + | PackageManager.MATCH_UNINSTALLED_PACKAGES, + UserHandle.USER_SYSTEM)); + } + for (ApplicationInfo info : list) { + if (info.isUpdatedSystemApp()) { + pw.println("Uninstalling updates to " + info.packageName + "..."); + final LocalIntentReceiver receiver = new LocalIntentReceiver(); + installer.uninstall(new VersionedPackage(info.packageName, + info.versionCode), null /*callerPackageName*/, 0 /* flags */, + receiver.getIntentSender(), 0); + + final Intent result = receiver.getResult(); + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + if (status != PackageInstaller.STATUS_SUCCESS) { + failedUninstalls = true; + pw.println("Couldn't uninstall package: " + info.packageName); + } + } + } + } catch (RemoteException e) { + pw.println("Failure [" + + e.getClass().getName() + " - " + + e.getMessage() + "]"); + return 0; + } + if (failedUninstalls) { + return 0; + } + pw.println("Success"); + return 1; + } + + private int runRollbackApp() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + + String opt; + long stagedReadyTimeoutMs = DEFAULT_STAGED_READY_TIMEOUT_MS; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--staged-ready-timeout": + stagedReadyTimeoutMs = Long.parseLong(getNextArgRequired()); + break; + default: + throw new IllegalArgumentException("Unknown option: " + opt); + } + } + final String packageName = getNextArgRequired(); + if (packageName == null) { + pw.println("Error: package name not specified"); + return 1; + } + + final Context shellPackageContext; + try { + shellPackageContext = mContext.createPackageContextAsUser( + "com.android.shell", 0, Binder.getCallingUserHandle()); + } catch (NameNotFoundException e) { + // should not happen + throw new RuntimeException(e); + } + + final LocalIntentReceiver receiver = new LocalIntentReceiver(); + RollbackManager rm = shellPackageContext.getSystemService(RollbackManager.class); + RollbackInfo rollback = null; + for (RollbackInfo r : rm.getAvailableRollbacks()) { + for (PackageRollbackInfo info : r.getPackages()) { + if (packageName.equals(info.getPackageName())) { + rollback = r; + break; + } + } + } + + if (rollback == null) { + pw.println("No available rollbacks for: " + packageName); + return 1; + } + + rm.commitRollback(rollback.getRollbackId(), + Collections.emptyList(), receiver.getIntentSender()); + + final Intent result = receiver.getResult(); + final int status = result.getIntExtra(RollbackManager.EXTRA_STATUS, + RollbackManager.STATUS_FAILURE); + + if (status != RollbackManager.STATUS_SUCCESS) { + pw.println("Failure [" + + result.getStringExtra(RollbackManager.EXTRA_STATUS_MESSAGE) + "]"); + return 1; + } + + if (rollback.isStaged() && stagedReadyTimeoutMs > 0) { + final int committedSessionId = rollback.getCommittedSessionId(); + return doWaitForStagedSessionReady(committedSessionId, stagedReadyTimeoutMs, pw); + } + + pw.println("Success"); + return 0; + + } + + private void setParamsSize(InstallParams params, List inPaths) { + if (params.sessionParams.sizeBytes != -1 || STDIN_PATH.equals(inPaths.get(0))) { + return; + } + + long sessionSize = 0; + + ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); + for (String inPath : inPaths) { + final ParcelFileDescriptor fd = openFileForSystem(inPath, "r"); + if (fd == null) { + getErrPrintWriter().println("Error: Can't open file: " + inPath); + throw new IllegalArgumentException("Error: Can't open file: " + inPath); + } + try { + ParseResult apkLiteResult = ApkLiteParseUtils.parseApkLite( + input.reset(), fd.getFileDescriptor(), inPath, 0); + if (apkLiteResult.isError()) { + throw new IllegalArgumentException( + "Error: Failed to parse APK file: " + inPath + ": " + + apkLiteResult.getErrorMessage(), + apkLiteResult.getException()); + } + final ApkLite apkLite = apkLiteResult.getResult(); + final PackageLite pkgLite = new PackageLite(null, apkLite.getPath(), apkLite, + null /* splitNames */, null /* isFeatureSplits */, + null /* usesSplitNames */, null /* configForSplit */, + null /* splitApkPaths */, null /* splitRevisionCodes */, + apkLite.getTargetSdkVersion(), null /* requiredSplitTypes */, + null /* splitTypes */); + sessionSize += InstallLocationUtils.calculateInstalledSize(pkgLite, + params.sessionParams.abiOverride, fd.getFileDescriptor()); + } catch (IOException e) { + getErrPrintWriter().println("Error: Failed to parse APK file: " + inPath); + throw new IllegalArgumentException( + "Error: Failed to parse APK file: " + inPath, e); + } finally { + try { + fd.close(); + } catch (IOException e) { + } + } + } + + params.sessionParams.setSize(sessionSize); + } + /** + * Displays the package file for a package. + * @param pckg + */ + private int displayPackageFilePath(String pckg, int userId) throws RemoteException { + PackageInfo info = mInterface.getPackageInfo(pckg, PackageManager.MATCH_APEX, userId); + if (info != null && info.applicationInfo != null) { + final PrintWriter pw = getOutPrintWriter(); + pw.print("package:"); + pw.println(info.applicationInfo.sourceDir); + if (!ArrayUtils.isEmpty(info.applicationInfo.splitSourceDirs)) { + for (String splitSourceDir : info.applicationInfo.splitSourceDirs) { + pw.print("package:"); + pw.println(splitSourceDir); + } + } + return 0; + } + return 1; + } + + private int runPath() throws RemoteException { + int userId = UserHandle.USER_SYSTEM; + String option = getNextOption(); + if (option != null && option.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } + + String pkg = getNextArgRequired(); + if (pkg == null) { + getErrPrintWriter().println("Error: no package specified"); + return 1; + } + final int translatedUserId = + translateUserId(userId, UserHandle.USER_NULL, "runPath"); + return displayPackageFilePath(pkg, translatedUserId); + } + + private int runList() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final String type = getNextArg(); + if (type == null) { + pw.println("Error: didn't specify type of data to list"); + return -1; + } + switch(type) { + case "features": + return runListFeatures(); + case "instrumentation": + return runListInstrumentation(); + case "libraries": + return runListLibraries(); + case "package": + case "packages": + return runListPackages(false /*showSourceDir*/); + case "permission-groups": + return runListPermissionGroups(); + case "permissions": + return runListPermissions(); + case "staged-sessions": + return runListStagedSessions(); + case "sdks": + return runListSdks(); + case "users": + ServiceManager.getService("user").shellCommand( + getInFileDescriptor(), getOutFileDescriptor(), getErrFileDescriptor(), + new String[] { "list" }, getShellCallback(), adoptResultReceiver()); + return 0; + case "initial-non-stopped-system-packages": + return runListInitialNonStoppedSystemPackages(); + } + pw.println("Error: unknown list type '" + type + "'"); + return -1; + } + + private int runGc() throws RemoteException { + Runtime.getRuntime().gc(); + final PrintWriter pw = getOutPrintWriter(); + pw.println("Ok"); + return 0; + } + + private int runListInitialNonStoppedSystemPackages() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final List list = mInterface.getInitialNonStoppedSystemPackages(); + + Collections.sort(list); + + for (String pkgName : list) { + pw.print("package:"); + pw.print(pkgName); + pw.println(); + } + + return 0; + } + + private int runListFeatures() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final List list = mInterface.getSystemAvailableFeatures().getList(); + + // sort by name + Collections.sort(list, new Comparator() { + public int compare(FeatureInfo o1, FeatureInfo o2) { + if (o1.name == o2.name) return 0; + if (o1.name == null) return -1; + if (o2.name == null) return 1; + return o1.name.compareTo(o2.name); + } + }); + + final int count = (list != null) ? list.size() : 0; + for (int p = 0; p < count; p++) { + FeatureInfo fi = list.get(p); + pw.print("feature:"); + if (fi.name != null) { + pw.print(fi.name); + if (fi.version > 0) { + pw.print("="); + pw.print(fi.version); + } + pw.println(); + } else { + pw.println("reqGlEsVersion=0x" + + Integer.toHexString(fi.reqGlEsVersion)); + } + } + return 0; + } + + private int runListInstrumentation() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + boolean showSourceDir = false; + String targetPackage = null; + + try { + String opt; + while ((opt = getNextArg()) != null) { + switch (opt) { + case "-f": + showSourceDir = true; + break; + default: + if (opt.charAt(0) != '-') { + targetPackage = opt; + } else { + pw.println("Error: Unknown option: " + opt); + return -1; + } + break; + } + } + } catch (RuntimeException ex) { + pw.println("Error: " + ex.toString()); + return -1; + } + + final List list = + mInterface.queryInstrumentationAsUser( + targetPackage, PackageManager.MATCH_KNOWN_PACKAGES, UserHandle.USER_SYSTEM) + .getList(); + + // sort by target package + Collections.sort(list, new Comparator() { + public int compare(InstrumentationInfo o1, InstrumentationInfo o2) { + return o1.targetPackage.compareTo(o2.targetPackage); + } + }); + + final int count = (list != null) ? list.size() : 0; + for (int p = 0; p < count; p++) { + final InstrumentationInfo ii = list.get(p); + pw.print("instrumentation:"); + if (showSourceDir) { + pw.print(ii.sourceDir); + pw.print("="); + } + final ComponentName cn = new ComponentName(ii.packageName, ii.name); + pw.print(cn.flattenToShortString()); + pw.print(" (target="); + pw.print(ii.targetPackage); + pw.println(")"); + } + return 0; + } + + private int runListLibraries() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + boolean verbose = false; + String opt; + while ((opt = getNextArg()) != null) { + switch (opt) { + case "-v": + verbose = true; + break; + default: + pw.println("Error: Unknown option: " + opt); + return -1; + } + } + + final Map namesAndPaths = mInterface.getSystemSharedLibraryNamesAndPaths(); + if (namesAndPaths.isEmpty()) { + return 0; + } + + // sort by name + final List libs = new ArrayList<>(namesAndPaths.keySet()); + Collections.sort(libs, (o1, o2) -> { + if (o1 == o2) return 0; + if (o1 == null) return -1; + if (o2 == null) return 1; + return o1.compareTo(o2); + }); + + for (int i = 0; i < libs.size(); i++) { + String lib = libs.get(i); + pw.print("library:"); + pw.print(lib); + if (verbose) { + pw.print(" path:"); + pw.print(namesAndPaths.get(lib)); + } + pw.println(); + } + return 0; + } + + private int runListPackages(boolean showSourceDir) throws RemoteException { + return runListPackages(showSourceDir, false); + } + + private int runListSdks() throws RemoteException { + return runListPackages(false, true); + } + + private int runListPackages(boolean showSourceDir, boolean showSdks) throws RemoteException { + final String prefix = showSdks ? "sdk:" : "package:"; + final PrintWriter pw = getOutPrintWriter(); + int getFlags = 0; + boolean listDisabled = false, listEnabled = false; + boolean listSystem = false, listThirdParty = false; + boolean listInstaller = false; + boolean showUid = false; + boolean showVersionCode = false; + boolean listQuarantinedOnly = false; + boolean listApexOnly = false; + boolean showStopped = false; + int uid = -1; + int defaultUserId = UserHandle.USER_ALL; + try { + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "-d": + listDisabled = true; + break; + case "-e": + listEnabled = true; + break; + case "-a": + getFlags |= PackageManager.MATCH_KNOWN_PACKAGES; + getFlags |= PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS; + break; + case "-f": + showSourceDir = true; + break; + case "-i": + listInstaller = true; + break; + case "-l": + // old compat + break; + case "-s": + listSystem = true; + break; + case "-q": + listQuarantinedOnly = true; + break; + case "-U": + showUid = true; + break; + case "-u": + getFlags |= PackageManager.MATCH_UNINSTALLED_PACKAGES; + break; + case "-3": + listThirdParty = true; + break; + case "--show-versioncode": + showVersionCode = true; + break; + case "--apex-only": + getFlags |= PackageManager.MATCH_APEX; + listApexOnly = true; + break; + case "--factory-only": + getFlags |= PackageManager.MATCH_FACTORY_ONLY; + break; + case "--user": + defaultUserId = UserHandle.parseUserArg(getNextArgRequired()); + break; + case "--uid": + showUid = true; + uid = Integer.parseInt(getNextArgRequired()); + break; + case "--match-libraries": + getFlags |= PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES; + break; + case "--show-stopped": + showStopped = true; + break; + default: + pw.println("Error: Unknown option: " + opt); + return -1; + } + } + } catch (RuntimeException ex) { + pw.println("Error: " + ex.toString()); + return -1; + } + + final String filter = getNextArg(); + + int[] userIds = {defaultUserId}; + if (defaultUserId == UserHandle.USER_ALL) { + final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class); + userIds = umi.getUserIds(); + } + if (showSdks) { + getFlags |= PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES; + } + + // Build a map of packages to a list of corresponding uids. Keys are strings containing + // the sdk or package name along with optional additional information based on opt. + final Map> out = new HashMap<>(); + for (int userId : userIds) { + final int translatedUserId; + try { + translatedUserId = + translateUserId(userId, UserHandle.USER_SYSTEM, "runListPackages"); + } catch (RuntimeException ex) { + getErrPrintWriter().println("Error: " + ex.toString()); + continue; + } + @SuppressWarnings("unchecked") final ParceledListSlice slice = + mInterface.getInstalledPackages(getFlags, translatedUserId); + final List packages = slice.getList(); + + final int count = packages.size(); + for (int p = 0; p < count; p++) { + final PackageInfo info = packages.get(p); + final StringBuilder stringBuilder = new StringBuilder(); + if (filter != null && !info.packageName.contains(filter)) { + continue; + } + final boolean isApex = info.isApex; + if (uid != -1 && !isApex && info.applicationInfo.uid != uid) { + continue; + } + + final boolean isSystem = !isApex + && (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + final boolean isEnabled = !isApex && info.applicationInfo.enabled; + if ((listDisabled && isEnabled) + || (listEnabled && !isEnabled) + || (listSystem && !isSystem) + || (listThirdParty && isSystem) + || (listApexOnly && !isApex)) { + continue; + } + if (listQuarantinedOnly && !mInterface.isPackageQuarantinedForUser(info.packageName, + translatedUserId)) { + continue; + } + + String name = null; + if (showSdks) { + final ParceledListSlice libsSlice = + mInterface.getDeclaredSharedLibraries( + info.packageName, getFlags, userId + ); + if (libsSlice == null) { + continue; + } + final List libs = libsSlice.getList(); + for (int l = 0, lsize = libs.size(); l < lsize; ++l) { + SharedLibraryInfo lib = libs.get(l); + if (lib.getType() == SharedLibraryInfo.TYPE_SDK_PACKAGE) { + name = lib.getName() + ":" + lib.getLongVersion(); + break; + } + } + if (name == null) { + continue; + } + } else { + name = info.packageName; + } + + stringBuilder.append(prefix); + if (showSourceDir) { + stringBuilder.append(info.applicationInfo.sourceDir); + stringBuilder.append("="); + } + stringBuilder.append(name); + if (showVersionCode) { + stringBuilder.append(" versionCode:"); + if (info.applicationInfo != null) { + stringBuilder.append(info.applicationInfo.longVersionCode); + } else { + stringBuilder.append(info.getLongVersionCode()); + } + } + if (showStopped) { + stringBuilder.append(" stopped="); + stringBuilder.append( + ((info.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) + ? "true" : "false"); + } + if (listInstaller) { + stringBuilder.append(" installer="); + stringBuilder.append(mInterface.getInstallerPackageName(info.packageName)); + } + List uids = out.computeIfAbsent( + stringBuilder.toString(), k -> new ArrayList<>() + ); + if (showUid && !isApex) { + uids.add(String.valueOf(info.applicationInfo.uid)); + } + } + } + for (Map.Entry> entry : out.entrySet()) { + pw.print(entry.getKey()); + List uids = entry.getValue(); + if (!uids.isEmpty()) { + pw.print(" uid:"); + pw.print(String.join(",", uids)); + } + pw.println(); + } + return 0; + } + + private int runListPermissionGroups() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final List pgs = mPermissionManager.getAllPermissionGroups(0); + + final int count = pgs.size(); + for (int p = 0; p < count ; p++) { + final PermissionGroupInfo pgi = pgs.get(p); + pw.print("permission group:"); + pw.println(pgi.name); + } + return 0; + } + + private int runListPermissions() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + boolean labels = false; + boolean groups = false; + boolean userOnly = false; + boolean summary = false; + boolean dangerousOnly = false; + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "-d": + dangerousOnly = true; + break; + case "-f": + labels = true; + break; + case "-g": + groups = true; + break; + case "-s": + groups = true; + labels = true; + summary = true; + break; + case "-u": + userOnly = true; + break; + default: + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + + final ArrayList groupList = new ArrayList(); + if (groups) { + final List infos = + mPermissionManager.getAllPermissionGroups(0 /*flags*/); + final int count = infos.size(); + for (int i = 0; i < count; i++) { + groupList.add(infos.get(i).name); + } + groupList.add(null); + } else { + final String grp = getNextArg(); + groupList.add(grp); + } + + if (dangerousOnly) { + pw.println("Dangerous Permissions:"); + pw.println(""); + doListPermissions(groupList, groups, labels, summary, + PermissionInfo.PROTECTION_DANGEROUS, + PermissionInfo.PROTECTION_DANGEROUS); + if (userOnly) { + pw.println("Normal Permissions:"); + pw.println(""); + doListPermissions(groupList, groups, labels, summary, + PermissionInfo.PROTECTION_NORMAL, + PermissionInfo.PROTECTION_NORMAL); + } + } else if (userOnly) { + pw.println("Dangerous and Normal Permissions:"); + pw.println(""); + doListPermissions(groupList, groups, labels, summary, + PermissionInfo.PROTECTION_NORMAL, + PermissionInfo.PROTECTION_DANGEROUS); + } else { + pw.println("All Permissions:"); + pw.println(""); + doListPermissions(groupList, groups, labels, summary, + -10000, 10000); + } + return 0; + } + + private static class SessionDump { + boolean onlyParent; // Show parent sessions only + boolean onlyReady; // Show only staged sessions that are in ready state + boolean onlySessionId; // Show sessionId only + } + + // Returns true if the provided flag is a session flag and given SessionDump was updated + private boolean setSessionFlag(String flag, SessionDump sessionDump) { + switch (flag) { + case "--only-parent": + sessionDump.onlyParent = true; + break; + case "--only-ready": + sessionDump.onlyReady = true; + break; + case "--only-sessionid": + sessionDump.onlySessionId = true; + break; + default: + return false; + } + return true; + } + + private int runListStagedSessions() { + try (IndentingPrintWriter pw = new IndentingPrintWriter( + getOutPrintWriter(), /* singleIndent */ " ", /* wrapLength */ 120)) { + final SessionDump sessionDump = new SessionDump(); + String opt; + while ((opt = getNextOption()) != null) { + if (!setSessionFlag(opt, sessionDump)) { + pw.println("Error: Unknown option: " + opt); + return -1; + } + } + + try { + final List stagedSessions = + mInterface.getPackageInstaller().getStagedSessions().getList(); + printSessionList(pw, stagedSessions, sessionDump); + } catch (RemoteException e) { + pw.println("Failure [" + + e.getClass().getName() + " - " + + e.getMessage() + "]"); + return -1; + } + return 1; + } + } + + private void printSessionList(IndentingPrintWriter pw, List stagedSessions, + SessionDump sessionDump) { + final SparseArray sessionById = new SparseArray<>(stagedSessions.size()); + for (SessionInfo session : stagedSessions) { + sessionById.put(session.getSessionId(), session); + } + for (SessionInfo session: stagedSessions) { + if (sessionDump.onlyReady && !session.isStagedSessionReady()) { + continue; + } + if (session.getParentSessionId() != SessionInfo.INVALID_ID) { + continue; + } + printSession(pw, session, sessionDump); + if (session.isMultiPackage() && !sessionDump.onlyParent) { + pw.increaseIndent(); + final int[] childIds = session.getChildSessionIds(); + for (int i = 0; i < childIds.length; i++) { + final SessionInfo childSession = sessionById.get(childIds[i]); + if (childSession == null) { + if (sessionDump.onlySessionId) { + pw.println(childIds[i]); + } else { + pw.println("sessionId = " + childIds[i] + "; not found"); + } + } else { + printSession(pw, childSession, sessionDump); + } + } + pw.decreaseIndent(); + } + } + } + + private static void printSession(PrintWriter pw, SessionInfo session, SessionDump sessionDump) { + if (sessionDump.onlySessionId) { + pw.println(session.getSessionId()); + return; + } + pw.println("sessionId = " + session.getSessionId() + + "; appPackageName = " + session.getAppPackageName() + + "; isStaged = " + session.isStaged() + + "; isReady = " + session.isStagedSessionReady() + + "; isApplied = " + session.isStagedSessionApplied() + + "; isFailed = " + session.isStagedSessionFailed() + + "; errorMsg = " + session.getStagedSessionErrorMessage() + + ";"); + } + + private Intent parseIntentAndUser() throws URISyntaxException { + mTargetUser = UserHandle.USER_CURRENT; + mBrief = false; + mComponents = false; + Intent intent = Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() { + @Override + public boolean handleOption(String opt, ShellCommand cmd) { + if ("--user".equals(opt)) { + mTargetUser = UserHandle.parseUserArg(cmd.getNextArgRequired()); + return true; + } else if ("--brief".equals(opt)) { + mBrief = true; + return true; + } else if ("--components".equals(opt)) { + mComponents = true; + return true; + } else if ("--query-flags".equals(opt)) { + mQueryFlags = Integer.decode(cmd.getNextArgRequired()); + return true; + } + return false; + } + }); + mTargetUser = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), mTargetUser, false, false, null, null); + return intent; + } + + private void printResolveInfo(PrintWriterPrinter pr, String prefix, ResolveInfo ri, + boolean brief, boolean components) { + if (brief || components) { + final ComponentName comp; + if (ri.activityInfo != null) { + comp = new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); + } else if (ri.serviceInfo != null) { + comp = new ComponentName(ri.serviceInfo.packageName, ri.serviceInfo.name); + } else if (ri.providerInfo != null) { + comp = new ComponentName(ri.providerInfo.packageName, ri.providerInfo.name); + } else { + comp = null; + } + if (comp != null) { + if (!components) { + pr.println(prefix + "priority=" + ri.priority + + " preferredOrder=" + ri.preferredOrder + + " match=0x" + Integer.toHexString(ri.match) + + " specificIndex=" + ri.specificIndex + + " isDefault=" + ri.isDefault); + } + pr.println(prefix + comp.flattenToShortString()); + return; + } + } + ri.dump(pr, prefix); + } + + private int runResolveActivity() { + Intent intent; + try { + intent = parseIntentAndUser(); + } catch (URISyntaxException e) { + throw new RuntimeException(e.getMessage(), e); + } + try { + ResolveInfo ri = mInterface.resolveIntent(intent, intent.getType(), mQueryFlags, + mTargetUser); + PrintWriter pw = getOutPrintWriter(); + if (ri == null) { + pw.println("No activity found"); + } else { + PrintWriterPrinter pr = new PrintWriterPrinter(pw); + printResolveInfo(pr, "", ri, mBrief, mComponents); + } + } catch (RemoteException e) { + throw new RuntimeException("Failed calling service", e); + } + return 0; + } + + private int runQueryIntentActivities() { + Intent intent; + try { + intent = parseIntentAndUser(); + } catch (URISyntaxException e) { + throw new RuntimeException(e.getMessage(), e); + } + try { + List result = mInterface.queryIntentActivities(intent, intent.getType(), + mQueryFlags, mTargetUser).getList(); + PrintWriter pw = getOutPrintWriter(); + if (result == null || result.size() <= 0) { + pw.println("No activities found"); + } else { + if (!mComponents) { + pw.print(result.size()); pw.println(" activities found:"); + PrintWriterPrinter pr = new PrintWriterPrinter(pw); + for (int i = 0; i < result.size(); i++) { + pw.print(" Activity #"); pw.print(i); pw.println(":"); + printResolveInfo(pr, " ", result.get(i), mBrief, mComponents); + } + } else { + PrintWriterPrinter pr = new PrintWriterPrinter(pw); + for (int i = 0; i < result.size(); i++) { + printResolveInfo(pr, "", result.get(i), mBrief, mComponents); + } + } + } + } catch (RemoteException e) { + throw new RuntimeException("Failed calling service", e); + } + return 0; + } + + private int runQueryIntentServices() { + Intent intent; + try { + intent = parseIntentAndUser(); + } catch (URISyntaxException e) { + throw new RuntimeException(e.getMessage(), e); + } + try { + List result = mInterface.queryIntentServices(intent, intent.getType(), + mQueryFlags, mTargetUser).getList(); + PrintWriter pw = getOutPrintWriter(); + if (result == null || result.size() <= 0) { + pw.println("No services found"); + } else { + if (!mComponents) { + pw.print(result.size()); pw.println(" services found:"); + PrintWriterPrinter pr = new PrintWriterPrinter(pw); + for (int i = 0; i < result.size(); i++) { + pw.print(" Service #"); pw.print(i); pw.println(":"); + printResolveInfo(pr, " ", result.get(i), mBrief, mComponents); + } + } else { + PrintWriterPrinter pr = new PrintWriterPrinter(pw); + for (int i = 0; i < result.size(); i++) { + printResolveInfo(pr, "", result.get(i), mBrief, mComponents); + } + } + } + } catch (RemoteException e) { + throw new RuntimeException("Failed calling service", e); + } + return 0; + } + + private int runQueryIntentReceivers() { + Intent intent; + try { + intent = parseIntentAndUser(); + } catch (URISyntaxException e) { + throw new RuntimeException(e.getMessage(), e); + } + try { + List result = mInterface.queryIntentReceivers(intent, intent.getType(), + mQueryFlags, mTargetUser).getList(); + PrintWriter pw = getOutPrintWriter(); + if (result == null || result.size() <= 0) { + pw.println("No receivers found"); + } else { + if (!mComponents) { + pw.print(result.size()); pw.println(" receivers found:"); + PrintWriterPrinter pr = new PrintWriterPrinter(pw); + for (int i = 0; i < result.size(); i++) { + pw.print(" Receiver #"); pw.print(i); pw.println(":"); + printResolveInfo(pr, " ", result.get(i), mBrief, mComponents); + } + } else { + PrintWriterPrinter pr = new PrintWriterPrinter(pw); + for (int i = 0; i < result.size(); i++) { + printResolveInfo(pr, "", result.get(i), mBrief, mComponents); + } + } + } + } catch (RemoteException e) { + throw new RuntimeException("Failed calling service", e); + } + return 0; + } + + private int runStreamingInstall() throws RemoteException { + final InstallParams params = makeInstallParams(UNSUPPORTED_INSTALL_CMD_OPTS); + if (params.sessionParams.dataLoaderParams == null) { + params.sessionParams.setDataLoaderParams( + PackageManagerShellCommandDataLoader.getStreamingDataLoaderParams(this)); + } + return doRunInstall(params); + } + + private int runArchivedInstall() throws RemoteException { + final InstallParams params = makeInstallParams(UNSUPPORTED_INSTALL_CMD_OPTS); + params.sessionParams.installFlags |= PackageManager.INSTALL_ARCHIVED; + if (params.sessionParams.dataLoaderParams == null) { + params.sessionParams.setDataLoaderParams( + PackageManagerShellCommandDataLoader.getStreamingDataLoaderParams(this)); + } + return doRunInstall(params); + } + + private int runIncrementalInstall() throws RemoteException { + final InstallParams params = makeInstallParams(UNSUPPORTED_INSTALL_CMD_OPTS); + if (params.sessionParams.dataLoaderParams == null) { + params.sessionParams.setDataLoaderParams( + PackageManagerShellCommandDataLoader.getIncrementalDataLoaderParams(this)); + } + return doRunInstall(params); + } + + private int runInstall() throws RemoteException { + return doRunInstall(makeInstallParams(UNSUPPORTED_INSTALL_CMD_OPTS)); + } + + private int runScanFast() throws RemoteException { + final String inPath = getNextArg(); + File file = new File(inPath); + if (!file.exists()) { + return 1; + } + + try { + mInterface.scanFast(inPath); + return 0; + } catch (Exception e) { + e.printStackTrace(); + getErrPrintWriter().println(e.toString()); + return 1; + } + } + + private int doRunInstall(final InstallParams params) throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + + int requestUserId = params.userId; + if (requestUserId != UserHandle.USER_ALL && requestUserId != UserHandle.USER_CURRENT) { + UserManagerInternal umi = + LocalServices.getService(UserManagerInternal.class); + UserInfo userInfo = umi.getUserInfo(requestUserId); + if (userInfo == null) { + pw.println("Failure [user " + requestUserId + " doesn't exist]"); + return 1; + } + } + + final boolean isStreaming = params.sessionParams.dataLoaderParams != null; + final boolean isApex = + (params.sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0; + final boolean installArchived = + (params.sessionParams.installFlags & PackageManager.INSTALL_ARCHIVED) != 0; + + ArrayList args = getRemainingArgs(); + + final boolean fromStdIn = args.isEmpty() || STDIN_PATH.equals(args.get(0)); + final boolean hasSplits = args.size() > 1; + + if (fromStdIn && params.sessionParams.sizeBytes == -1) { + pw.println("Error: must either specify a package size or an APK file"); + return 1; + } + + if (isApex && hasSplits) { + pw.println("Error: can't specify SPLIT(s) for APEX"); + return 1; + } + + if (installArchived) { + if (hasSplits) { + pw.println("Error: can't have SPLIT(s) for Archival install"); + return 1; + } + } + + if (!isStreaming) { + if (fromStdIn && hasSplits) { + pw.println("Error: can't specify SPLIT(s) along with STDIN"); + return 1; + } + + if (args.isEmpty()) { + args.add(STDIN_PATH); + } else { + setParamsSize(params, args); + } + } + + final int sessionId = doCreateSession(params.sessionParams, + params.installerPackageName, params.userId); + boolean abandonSession = true; + try { + if (isStreaming) { + if (doAddFiles(sessionId, args, params.sessionParams.sizeBytes, isApex, + installArchived) != PackageInstaller.STATUS_SUCCESS) { + return 1; + } + } else { + if (doWriteSplits(sessionId, args, params.sessionParams.sizeBytes, isApex) + != PackageInstaller.STATUS_SUCCESS) { + return 1; + } + } + if (doCommitSession(sessionId, false /*logSuccess*/) + != PackageInstaller.STATUS_SUCCESS) { + return 1; + } + abandonSession = false; + + if (params.sessionParams.isStaged && params.stagedReadyTimeoutMs > 0) { + return doWaitForStagedSessionReady(sessionId, params.stagedReadyTimeoutMs, pw); + } + + pw.println("Success"); + return 0; + } finally { + if (abandonSession) { + try { + doAbandonSession(sessionId, false /*logSuccess*/); + } catch (Exception ignore) { + } + } + } + } + + private int doWaitForStagedSessionReady(int sessionId, long timeoutMs, PrintWriter pw) + throws RemoteException { + Preconditions.checkArgument(timeoutMs > 0); + PackageInstaller.SessionInfo si = mInterface.getPackageInstaller() + .getSessionInfo(sessionId); + if (si == null) { + pw.println("Failure [Unknown session " + sessionId + "]"); + return 1; + } + if (!si.isStaged()) { + pw.println("Failure [Session " + sessionId + " is not a staged session]"); + return 1; + } + long currentTime = System.currentTimeMillis(); + long endTime = currentTime + timeoutMs; + // Using a loop instead of BroadcastReceiver since we can receive session update + // broadcast only if packageInstallerName is "android". We can't always force + // "android" as packageIntallerName, e.g, rollback auto implies + // "-i com.android.shell". + while (si != null && currentTime < endTime) { + if (si.isStagedSessionReady() || si.isStagedSessionFailed()) { + break; + } + SystemClock.sleep(Math.min(endTime - currentTime, 100)); + currentTime = System.currentTimeMillis(); + si = mInterface.getPackageInstaller().getSessionInfo(sessionId); + } + if (si == null) { + pw.println("Failure [failed to retrieve SessionInfo]"); + return 1; + } + if (!si.isStagedSessionReady() && !si.isStagedSessionFailed()) { + pw.println("Failure [timed out after " + timeoutMs + " ms]"); + return 1; + } + if (!si.isStagedSessionReady()) { + pw.println("Error [" + si.getStagedSessionErrorCode() + "] [" + + si.getStagedSessionErrorMessage() + "]"); + return 1; + } + pw.println("Success. Reboot device to apply staged session"); + return 0; + } + + private int runInstallAbandon() throws RemoteException { + final int sessionId = Integer.parseInt(getNextArg()); + return doAbandonSession(sessionId, true /*logSuccess*/); + } + + private int runInstallCommit() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + String opt; + long stagedReadyTimeoutMs = DEFAULT_STAGED_READY_TIMEOUT_MS; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--staged-ready-timeout": + stagedReadyTimeoutMs = Long.parseLong(getNextArgRequired()); + break; + default: + throw new IllegalArgumentException("Unknown option: " + opt); + } + } + final int sessionId = Integer.parseInt(getNextArg()); + if (doCommitSession(sessionId, false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { + return 1; + } + final PackageInstaller.SessionInfo si = mInterface.getPackageInstaller() + .getSessionInfo(sessionId); + if (si != null && si.isStaged() && stagedReadyTimeoutMs > 0) { + return doWaitForStagedSessionReady(sessionId, stagedReadyTimeoutMs, pw); + } + pw.println("Success"); + return 0; + } + + private int runInstallCreate() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final InstallParams installParams = makeInstallParams(UNSUPPORTED_SESSION_CREATE_OPTS); + final int sessionId = doCreateSession(installParams.sessionParams, + installParams.installerPackageName, installParams.userId); + + // NOTE: adb depends on parsing this string + pw.println("Success: created install session [" + sessionId + "]"); + return 0; + } + + private int runInstallWrite() throws RemoteException { + long sizeBytes = -1; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("-S")) { + sizeBytes = Long.parseLong(getNextArg()); + } else { + throw new IllegalArgumentException("Unknown option: " + opt); + } + } + + final int sessionId = Integer.parseInt(getNextArg()); + final String splitName = getNextArg(); + final String path = getNextArg(); + return doWriteSplit(sessionId, path, sizeBytes, splitName, true /*logSuccess*/); + } + + private int runInstallAddSession() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final int parentSessionId = Integer.parseInt(getNextArg()); + + IntArray otherSessionIds = new IntArray(); + String opt; + while ((opt = getNextArg()) != null) { + otherSessionIds.add(Integer.parseInt(opt)); + } + if (otherSessionIds.size() == 0) { + pw.println("Error: At least two sessions are required."); + return 1; + } + return doInstallAddSession(parentSessionId, otherSessionIds.toArray(), + true /*logSuccess*/); + } + + private int runInstallSetPreVerifiedDomains() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final int sessionId = Integer.parseInt(getNextArg()); + final String preVerifiedDomainsStr = getNextArg(); + final String[] preVerifiedDomains = preVerifiedDomainsStr.split(","); + PackageInstaller.Session session = null; + try { + session = new PackageInstaller.Session( + mInterface.getPackageInstaller().openSession(sessionId)); + session.setPreVerifiedDomains(new ArraySet<>(preVerifiedDomains)); + } finally { + IoUtils.closeQuietly(session); + } + return 0; + } + + private int runInstallGetPreVerifiedDomains() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final int sessionId = Integer.parseInt(getNextArg()); + PackageInstaller.Session session = null; + try { + session = new PackageInstaller.Session( + mInterface.getPackageInstaller().openSession(sessionId)); + Set preVerifiedDomains = session.getPreVerifiedDomains(); + if (preVerifiedDomains.isEmpty()) { + pw.println("The session doesn't have any pre-verified domains specified."); + } else { + pw.println(String.join(",", preVerifiedDomains)); + } + } finally { + IoUtils.closeQuietly(session); + } + return 0; + } + + private int runInstallRemove() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + + final int sessionId = Integer.parseInt(getNextArg()); + + ArrayList splitNames = getRemainingArgs(); + if (splitNames.isEmpty()) { + pw.println("Error: split name not specified"); + return 1; + } + return doRemoveSplits(sessionId, splitNames, true /*logSuccess*/); + } + + private int runGetArchivedPackageMetadata() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + int userId = UserHandle.USER_CURRENT; + + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--user": + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + default: + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + + final String packageName = getNextArg(); + if (packageName == null) { + pw.println("Error: package name not specified"); + return 1; + } + final int translatedUserId = translateUserId(userId, UserHandle.USER_NULL, + "runGetArchivedPackageMetadata"); + + try { + var archivedPackage = mInterface.getArchivedPackage(packageName, translatedUserId); + if (archivedPackage == null) { + pw.write("Package not found " + packageName); + return -1; + } + + Parcel parcel = Parcel.obtain(); + byte[] bytes; + try { + parcel.writeParcelable(archivedPackage, 0); + bytes = parcel.marshall(); + } finally { + parcel.recycle(); + } + + String encoded = HexEncoding.encodeToString(bytes); + pw.write(encoded); + } catch (Exception e) { + getErrPrintWriter().println("Failed to get archived package, reason: " + e); + pw.println("Failure [failed to get archived package], reason: " + e); + return -1; + } + return 0; + } + + private int runInstallExisting() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + int userId = UserHandle.USER_CURRENT; + int installFlags = PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS; + String opt; + boolean waitTillComplete = false; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--user": + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + case "--ephemeral": + case "--instant": + installFlags |= PackageManager.INSTALL_INSTANT_APP; + installFlags &= ~PackageManager.INSTALL_FULL_APP; + break; + case "--full": + installFlags &= ~PackageManager.INSTALL_INSTANT_APP; + installFlags |= PackageManager.INSTALL_FULL_APP; + break; + case "--wait": + waitTillComplete = true; + break; + case "--restrict-permissions": + installFlags &= ~PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS; + break; + default: + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + + final String packageName = getNextArg(); + if (packageName == null) { + pw.println("Error: package name not specified"); + return 1; + } + final int translatedUserId = + translateUserId(userId, UserHandle.USER_NULL, "runInstallExisting"); + + int installReason = PackageManager.INSTALL_REASON_UNKNOWN; + try { + if (waitTillComplete) { + final LocalIntentReceiver receiver = new LocalIntentReceiver(); + final IPackageInstaller installer = mInterface.getPackageInstaller(); + pw.println("Installing package " + packageName + " for user: " + translatedUserId); + installer.installExistingPackage(packageName, installFlags, installReason, + receiver.getIntentSender(), translatedUserId, null); + final Intent result = receiver.getResult(); + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + pw.println("Received intent for package install"); + return status == PackageInstaller.STATUS_SUCCESS ? 0 : 1; + } + + final int res = mInterface.installExistingPackageAsUser(packageName, translatedUserId, + installFlags, installReason, null); + if (res == PackageManager.INSTALL_FAILED_INVALID_URI) { + throw new NameNotFoundException("Package " + packageName + " doesn't exist"); + } + pw.println("Package " + packageName + " installed for user: " + translatedUserId); + return 0; + } catch (RemoteException | NameNotFoundException e) { + pw.println(e.toString()); + return 1; + } + } + + private int runSetInstallLocation() throws RemoteException { + int loc; + + String arg = getNextArg(); + if (arg == null) { + getErrPrintWriter().println("Error: no install location specified."); + return 1; + } + try { + loc = Integer.parseInt(arg); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: install location has to be a number."); + return 1; + } + if (!mInterface.setInstallLocation(loc)) { + getErrPrintWriter().println("Error: install location has to be a number."); + return 1; + } + return 0; + } + + private int runGetInstallLocation() throws RemoteException { + int loc = mInterface.getInstallLocation(); + String locStr = "invalid"; + if (loc == InstallLocationUtils.APP_INSTALL_AUTO) { + locStr = "auto"; + } else if (loc == InstallLocationUtils.APP_INSTALL_INTERNAL) { + locStr = "internal"; + } else if (loc == InstallLocationUtils.APP_INSTALL_EXTERNAL) { + locStr = "external"; + } + getOutPrintWriter().println(loc + "[" + locStr + "]"); + return 0; + } + + public int runMovePackage() throws RemoteException { + final String packageName = getNextArg(); + if (packageName == null) { + getErrPrintWriter().println("Error: package name not specified"); + return 1; + } + String volumeUuid = getNextArg(); + if ("internal".equals(volumeUuid)) { + volumeUuid = null; + } + + final int moveId = mInterface.movePackage(packageName, volumeUuid); + + int status = mInterface.getMoveStatus(moveId); + while (!PackageManager.isMoveStatusFinished(status)) { + SystemClock.sleep(DateUtils.SECOND_IN_MILLIS); + status = mInterface.getMoveStatus(moveId); + } + + if (status == PackageManager.MOVE_SUCCEEDED) { + getOutPrintWriter().println("Success"); + return 0; + } else { + getErrPrintWriter().println("Failure [" + status + "]"); + return 1; + } + } + + public int runMovePrimaryStorage() throws RemoteException { + String volumeUuid = getNextArg(); + if ("internal".equals(volumeUuid)) { + volumeUuid = null; + } + + final int moveId = mInterface.movePrimaryStorage(volumeUuid); + + int status = mInterface.getMoveStatus(moveId); + while (!PackageManager.isMoveStatusFinished(status)) { + SystemClock.sleep(DateUtils.SECOND_IN_MILLIS); + status = mInterface.getMoveStatus(moveId); + } + + if (status == PackageManager.MOVE_SUCCEEDED) { + getOutPrintWriter().println("Success"); + return 0; + } else { + getErrPrintWriter().println("Failure [" + status + "]"); + return 1; + } + } + + private int runCompile() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + boolean forceCompilation = false; + boolean allPackages = false; + boolean clearProfileData = false; + String compilerFilter = null; + String compilationReason = null; + boolean secondaryDex = false; + String split = null; + + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "-a": + allPackages = true; + break; + case "-c": + clearProfileData = true; + break; + case "-f": + forceCompilation = true; + break; + case "-m": + compilerFilter = getNextArgRequired(); + break; + case "-r": + compilationReason = getNextArgRequired(); + break; + case "--check-prof": + getNextArgRequired(); + pw.println("Warning: Ignoring obsolete flag --check-prof " + + "- it is unconditionally enabled now"); + break; + case "--reset": + forceCompilation = true; + clearProfileData = true; + compilationReason = "install"; + break; + case "--secondary-dex": + secondaryDex = true; + break; + case "--split": + split = getNextArgRequired(); + break; + default: + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + + final boolean compilerFilterGiven = compilerFilter != null; + final boolean compilationReasonGiven = compilationReason != null; + // Make sure exactly one of -m, or -r is given. + if (compilerFilterGiven && compilationReasonGiven) { + pw.println("Cannot use compilation filter (\"-m\") and compilation reason (\"-r\") " + + "at the same time"); + return 1; + } + if (!compilerFilterGiven && !compilationReasonGiven) { + pw.println("Cannot run without any of compilation filter (\"-m\") and compilation " + + "reason (\"-r\")"); + return 1; + } + + if (allPackages && split != null) { + pw.println("-a cannot be specified together with --split"); + return 1; + } + + if (secondaryDex && split != null) { + pw.println("--secondary-dex cannot be specified together with --split"); + return 1; + } + + String targetCompilerFilter = null; + if (compilerFilterGiven) { + if (!DexFile.isValidCompilerFilter(compilerFilter)) { + pw.println("Error: \"" + compilerFilter + + "\" is not a valid compilation filter."); + return 1; + } + targetCompilerFilter = compilerFilter; + } + if (compilationReasonGiven) { + int reason = -1; + for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) { + if (PackageManagerServiceCompilerMapping.REASON_STRINGS[i].equals( + compilationReason)) { + reason = i; + break; + } + } + if (reason == -1) { + pw.println("Error: Unknown compilation reason: " + compilationReason); + return 1; + } + targetCompilerFilter = + PackageManagerServiceCompilerMapping.getCompilerFilterForReason(reason); + } + + + List packageNames = null; + if (allPackages) { + packageNames = mInterface.getAllPackages(); + // Compiling the system server is only supported from odrefresh, so skip it. + packageNames.removeIf(packageName -> PLATFORM_PACKAGE_NAME.equals(packageName)); + } else { + String packageName = getNextArg(); + if (packageName == null) { + pw.println("Error: package name not specified"); + return 1; + } + packageNames = Collections.singletonList(packageName); + } + + List failedPackages = new ArrayList<>(); + int index = 0; + for (String packageName : packageNames) { + if (clearProfileData) { + mInterface.clearApplicationProfileData(packageName); + } + + if (allPackages) { + pw.println(++index + "/" + packageNames.size() + ": " + packageName); + pw.flush(); + } + + final boolean result = secondaryDex + ? mInterface.performDexOptSecondary( + packageName, targetCompilerFilter, forceCompilation) + : mInterface.performDexOptMode(packageName, true /* checkProfiles */, + targetCompilerFilter, forceCompilation, true /* bootComplete */, split); + if (!result) { + failedPackages.add(packageName); + } + } + + if (failedPackages.isEmpty()) { + pw.println("Success"); + return 0; + } else if (failedPackages.size() == 1) { + pw.println("Failure: package " + failedPackages.get(0) + " could not be compiled"); + return 1; + } else { + pw.print("Failure: the following packages could not be compiled: "); + boolean is_first = true; + for (String packageName : failedPackages) { + if (is_first) { + is_first = false; + } else { + pw.print(", "); + } + pw.print(packageName); + } + pw.println(); + return 1; + } + } + + private int runreconcileSecondaryDexFiles() + throws RemoteException, LegacyDexoptDisabledException { + String packageName = getNextArg(); + mPm.legacyReconcileSecondaryDexFiles(packageName); + return 0; + } + + public int runForceDexOpt() throws RemoteException, LegacyDexoptDisabledException { + mPm.legacyForceDexOpt(getNextArgRequired()); + return 0; + } + + private int runBgDexOpt() throws RemoteException, LegacyDexoptDisabledException { + String opt = getNextOption(); + + if (opt == null) { + List packageNames = new ArrayList<>(); + String arg; + while ((arg = getNextArg()) != null) { + packageNames.add(arg); + } + if (!BackgroundDexOptService.getService().runBackgroundDexoptJob( + packageNames.isEmpty() ? null : packageNames)) { + getOutPrintWriter().println("Failure"); + return -1; + } + } else { + String extraArg = getNextArg(); + if (extraArg != null) { + getErrPrintWriter().println("Invalid argument: " + extraArg); + return -1; + } + + switch (opt) { + case "--cancel": + return cancelBgDexOptJob(); + + case "--disable": + BackgroundDexOptService.getService().setDisableJobSchedulerJobs(true); + break; + + case "--enable": + BackgroundDexOptService.getService().setDisableJobSchedulerJobs(false); + break; + + default: + getErrPrintWriter().println("Unknown option: " + opt); + return -1; + } + } + + getOutPrintWriter().println("Success"); + return 0; + } + + private int cancelBgDexOptJob() throws RemoteException, LegacyDexoptDisabledException { + BackgroundDexOptService.getService().cancelBackgroundDexoptJob(); + getOutPrintWriter().println("Success"); + return 0; + } + + private int runDeleteDexOpt() throws RemoteException { + PrintWriter pw = getOutPrintWriter(); + String packageName = getNextArg(); + if (TextUtils.isEmpty(packageName)) { + pw.println("Error: no package name"); + return 1; + } + long freedBytes = mPm.deleteOatArtifactsOfPackage(packageName); + if (freedBytes < 0) { + pw.println("Error: delete failed"); + return 1; + } + pw.println("Success: freed " + freedBytes + " bytes"); + Slog.i(TAG, "delete-dexopt " + packageName + " ,freed " + freedBytes + " bytes"); + return 0; + } + + private int runDumpProfiles() throws RemoteException, LegacyDexoptDisabledException { + final PrintWriter pw = getOutPrintWriter(); + boolean dumpClassesAndMethods = false; + + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--dump-classes-and-methods": + dumpClassesAndMethods = true; + break; + default: + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + + String packageName = getNextArg(); + mPm.legacyDumpProfiles(packageName, dumpClassesAndMethods); + return 0; + } + + private int runSnapshotProfile() throws RemoteException { + PrintWriter pw = getOutPrintWriter(); + + // Parse the arguments + final String packageName = getNextArg(); + final boolean isBootImage = "android".equals(packageName); + + String codePath = null; + String opt; + while ((opt = getNextArg()) != null) { + switch (opt) { + case "--code-path": + if (isBootImage) { + pw.write("--code-path cannot be used for the boot image."); + return -1; + } + codePath = getNextArg(); + break; + default: + pw.write("Unknown arg: " + opt); + return -1; + } + } + + // If no code path was explicitly requested, select the base code path. + String baseCodePath = null; + if (!isBootImage) { + PackageInfo packageInfo = mInterface.getPackageInfo(packageName, /* flags */ 0, + /* userId */0); + if (packageInfo == null) { + pw.write("Package not found " + packageName); + return -1; + } + baseCodePath = packageInfo.applicationInfo.getBaseCodePath(); + if (codePath == null) { + codePath = baseCodePath; + } + } + + // Create the profile snapshot. + final SnapshotRuntimeProfileCallback callback = new SnapshotRuntimeProfileCallback(); + // The calling package is needed to debug permission access. + final String callingPackage = (Binder.getCallingUid() == Process.ROOT_UID) + ? "root" : "com.android.shell"; + final int profileType = isBootImage + ? ArtManager.PROFILE_BOOT_IMAGE : ArtManager.PROFILE_APPS; + if (!mInterface.getArtManager().isRuntimeProfilingEnabled(profileType, callingPackage)) { + pw.println("Error: Runtime profiling is not enabled"); + return -1; + } + mInterface.getArtManager().snapshotRuntimeProfile(profileType, packageName, + codePath, callback, callingPackage); + if (!callback.waitTillDone()) { + pw.println("Error: callback not called"); + return callback.mErrCode; + } + + // Copy the snapshot profile to the output profile file. + try (InputStream inStream = new AutoCloseInputStream(callback.mProfileReadFd)) { + final String outputFileSuffix = isBootImage || Objects.equals(baseCodePath, codePath) + ? "" : ("-" + new File(codePath).getName()); + final String outputProfilePath = + ART_PROFILE_SNAPSHOT_DEBUG_LOCATION + packageName + outputFileSuffix + ".prof"; + try (OutputStream outStream = new FileOutputStream(outputProfilePath)) { + Streams.copy(inStream, outStream); + } + // Give read permissions to the other group. + Os.chmod(outputProfilePath, /*mode*/ DEFAULT_FILE_ACCESS_MODE); + } catch (IOException | ErrnoException e) { + pw.println("Error when reading the profile fd: " + e.getMessage()); + e.printStackTrace(pw); + return -1; + } + return 0; + } + + private ArrayList getRemainingArgs() { + ArrayList args = new ArrayList<>(); + String arg; + while ((arg = getNextArg()) != null) { + args.add(arg); + } + return args; + } + + private static class SnapshotRuntimeProfileCallback + extends ISnapshotRuntimeProfileCallback.Stub { + private boolean mSuccess = false; + private int mErrCode = -1; + private ParcelFileDescriptor mProfileReadFd = null; + private final CountDownLatch mDoneSignal = new CountDownLatch(1); + + @Override + public void onSuccess(ParcelFileDescriptor profileReadFd) { + mSuccess = true; + try { + // We need to dup the descriptor. We are in the same process as system server + // and we will be receiving the same object (which will be closed on the + // server side). + mProfileReadFd = profileReadFd.dup(); + } catch (IOException e) { + e.printStackTrace(); + } + mDoneSignal.countDown(); + } + + @Override + public void onError(int errCode) { + mSuccess = false; + mErrCode = errCode; + mDoneSignal.countDown(); + } + + boolean waitTillDone() { + boolean done = false; + try { + // The time-out is an arbitrary large value. Since this is a local call the result + // will come very fast. + done = mDoneSignal.await(10000000, TimeUnit.MILLISECONDS); + } catch (InterruptedException ignored) { + } + return done && mSuccess; + } + } + + private int runUninstall() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + int flags = 0; + int userId = UserHandle.USER_ALL; + long versionCode = PackageManager.VERSION_CODE_HIGHEST; + + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "-k": + flags |= PackageManager.DELETE_KEEP_DATA; + break; + case "--user": + userId = UserHandle.parseUserArg(getNextArgRequired()); + if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT) { + UserManagerInternal umi = + LocalServices.getService(UserManagerInternal.class); + UserInfo userInfo = umi.getUserInfo(userId); + if (userInfo == null) { + pw.println("Failure [user " + userId + " doesn't exist]"); + return 1; + } + } + break; + case "--versionCode": + versionCode = Long.parseLong(getNextArgRequired()); + break; + case "--update": + flags |= DELETE_UPDATE_SHARE_APP; + break; + default: + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + + final String packageName = getNextArg(); + if (packageName == null) { + pw.println("Error: package name not specified"); + return 1; + } + + // if a split is specified, just remove it and not the whole package + ArrayList splitNames = getRemainingArgs(); + if (!splitNames.isEmpty()) { + return runRemoveSplits(packageName, splitNames); + } + + if (userId == UserHandle.USER_ALL) { + flags |= PackageManager.DELETE_ALL_USERS; + } + final int translatedUserId = + translateUserId(userId, UserHandle.USER_SYSTEM, "runUninstall"); + final LocalIntentReceiver receiver = new LocalIntentReceiver(); + final PackageManagerInternal internal = + LocalServices.getService(PackageManagerInternal.class); + + if (internal.isApexPackage(packageName)) { + internal.uninstallApex( + packageName, versionCode, translatedUserId, receiver.getIntentSender(), flags); + } else { + if ((flags & PackageManager.DELETE_ALL_USERS) == 0) { + final PackageInfo info = mInterface.getPackageInfo(packageName, + PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES, translatedUserId); + if (info == null) { + pw.println("Failure [not installed for " + translatedUserId + "]"); + return 1; + } + final boolean isSystem = + (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + // If we are being asked to delete a system app for just one + // user set flag so it disables rather than reverting to system + // version of the app. + if (isSystem) { + flags |= PackageManager.DELETE_SYSTEM_APP; + } + } + mInterface.getPackageInstaller().uninstall(new VersionedPackage(packageName, + versionCode), null /*callerPackageName*/, flags, + receiver.getIntentSender(), translatedUserId); + } + + final Intent result = receiver.getResult(); + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + if (status == PackageInstaller.STATUS_SUCCESS) { + pw.println("Success"); + return 0; + } else { + pw.println("Failure [" + + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]"); + return 1; + } + } + + private int runRemoveSplits(String packageName, Collection splitNames) + throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final SessionParams sessionParams = new SessionParams(SessionParams.MODE_INHERIT_EXISTING); + sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; + sessionParams.appPackageName = packageName; + final int sessionId = + doCreateSession(sessionParams, null /*installerPackageName*/, UserHandle.USER_ALL); + boolean abandonSession = true; + try { + if (doRemoveSplits(sessionId, splitNames, false /*logSuccess*/) + != PackageInstaller.STATUS_SUCCESS) { + return 1; + } + if (doCommitSession(sessionId, false /*logSuccess*/) + != PackageInstaller.STATUS_SUCCESS) { + return 1; + } + abandonSession = false; + pw.println("Success"); + return 0; + } finally { + if (abandonSession) { + try { + doAbandonSession(sessionId, false /*logSuccess*/); + } catch (RuntimeException ignore) { + } + } + } + } + + static class ClearDataObserver extends IPackageDataObserver.Stub { + boolean finished; + boolean result; + + @Override + public void onRemoveCompleted(String packageName, boolean succeeded) throws RemoteException { + synchronized (this) { + finished = true; + result = succeeded; + notifyAll(); + } + } + } + + private int runClear() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + int userId = UserHandle.USER_SYSTEM; + boolean cacheOnly = false; + + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--user": + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + case "--cache-only": + cacheOnly = true; + break; + default: + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + + String pkg = getNextArg(); + if (pkg == null) { + getErrPrintWriter().println("Error: no package specified"); + return 1; + } + + final int translatedUserId = + translateUserId(userId, UserHandle.USER_NULL, "runClear"); + final ClearDataObserver obs = new ClearDataObserver(); + if (!cacheOnly) { + ActivityManager.getService() + .clearApplicationUserData(pkg, false, obs, translatedUserId); + } else { + mInterface.deleteApplicationCacheFilesAsUser(pkg, translatedUserId, obs); + } + synchronized (obs) { + while (!obs.finished) { + try { + obs.wait(); + } catch (InterruptedException e) { + } + } + } + + if (obs.result) { + getOutPrintWriter().println("Success"); + return 0; + } else { + getErrPrintWriter().println("Failed"); + return 1; + } + } + + private static String enabledSettingToString(int state) { + switch (state) { + case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT: + return "default"; + case PackageManager.COMPONENT_ENABLED_STATE_ENABLED: + return "enabled"; + case PackageManager.COMPONENT_ENABLED_STATE_DISABLED: + return "disabled"; + case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER: + return "disabled-user"; + case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED: + return "disabled-until-used"; + } + return "unknown"; + } + + private int runSetEnabledSetting(int state) throws RemoteException { + int userId = UserHandle.USER_SYSTEM; + String option = getNextOption(); + if (option != null && option.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } + + final String pkg = getNextArg(); + if (pkg == null) { + getErrPrintWriter().println("Error: no package or component specified"); + return 1; + } + final int translatedUserId = + translateUserId(userId, UserHandle.USER_NULL, "runSetEnabledSetting"); + final ComponentName cn = ComponentName.unflattenFromString(pkg); + if (cn == null) { + mInterface.setApplicationEnabledSetting(pkg, state, 0, translatedUserId, + "shell:" + android.os.Process.myUid()); + getOutPrintWriter().println("Package " + pkg + " new state: " + + enabledSettingToString( + mInterface.getApplicationEnabledSetting(pkg, translatedUserId))); + return 0; + } else { + mInterface.setComponentEnabledSetting(cn, state, 0, translatedUserId, "shell"); + getOutPrintWriter().println("Component " + cn.toShortString() + " new state: " + + enabledSettingToString( + mInterface.getComponentEnabledSetting(cn, translatedUserId))); + return 0; + } + } + + private int runSetHiddenSetting(boolean state) throws RemoteException { + int userId = UserHandle.USER_SYSTEM; + String option = getNextOption(); + if (option != null && option.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } + + String pkg = getNextArg(); + if (pkg == null) { + getErrPrintWriter().println("Error: no package or component specified"); + return 1; + } + final int translatedUserId = + translateUserId(userId, UserHandle.USER_NULL, "runSetHiddenSetting"); + mInterface.setApplicationHiddenSettingAsUser(pkg, state, translatedUserId); + getOutPrintWriter().println("Package " + pkg + " new hidden state: " + + mInterface.getApplicationHiddenSettingAsUser(pkg, translatedUserId)); + return 0; + } + + private int runSetStoppedState(boolean state) throws RemoteException { + int userId = UserHandle.USER_SYSTEM; + String option = getNextOption(); + if (option != null && option.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } + + String pkg = getNextArg(); + if (pkg == null) { + getErrPrintWriter().println("Error: no package specified"); + return 1; + } + final int translatedUserId = + translateUserId(userId, UserHandle.USER_NULL, "runSetStoppedState"); + mInterface.setPackageStoppedState(pkg, state, translatedUserId); + getOutPrintWriter().println("Package " + pkg + " new stopped state: " + + mInterface.isPackageStoppedForUser(pkg, translatedUserId)); + return 0; + } + + private int runSetDistractingRestriction() { + final PrintWriter pw = getOutPrintWriter(); + int userId = UserHandle.USER_SYSTEM; + String opt; + int flags = 0; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--user": + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + case "--flag": + final String flag = getNextArgRequired(); + switch (flag) { + case "hide-notifications": + flags |= PackageManager.RESTRICTION_HIDE_NOTIFICATIONS; + break; + case "hide-from-suggestions": + flags |= PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS; + break; + default: + pw.println("Unrecognized flag: " + flag); + return 1; + } + break; + default: + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + + final List packageNames = getRemainingArgs(); + if (packageNames.isEmpty()) { + pw.println("Error: package name not specified"); + return 1; + } + try { + final int translatedUserId = translateUserId(userId, UserHandle.USER_NULL, + "set-distracting"); + final String[] errored = mInterface.setDistractingPackageRestrictionsAsUser( + packageNames.toArray(new String[]{}), flags, translatedUserId); + if (errored.length > 0) { + pw.println("Could not set restriction for: " + Arrays.toString(errored)); + return 1; + } + return 0; + } catch (RemoteException | IllegalArgumentException e) { + pw.println(e.toString()); + return 1; + } + } + + private int runGetDistractingRestriction() { + final PrintWriter pw = getOutPrintWriter(); + int userId = UserHandle.USER_SYSTEM; + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--user": + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + default: + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + + final List packageNames = getRemainingArgs(); + if (packageNames.isEmpty()) { + pw.println("Error: package name not specified"); + return 1; + } + pw.println("Distracting restrictions state for user " + userId); + + final int translatedUserId = translateUserId(userId, UserHandle.USER_NULL, + "get-distracting"); + final String[] packages = packageNames.toArray(new String[]{}); + int[] res = mPm.getDistractingPackageRestrictionsAsUser(packages, translatedUserId); + + for (int i = 0; i < res.length; i++) { + final int state = res[i]; + if (state == -1) { + pw.println(packages[i] + " not found ..."); + } else { + pw.println(packages[i] + " state: " + stateToString(state)); + } + } + + return 0; + } + + private static String stateToString(@PackageManager.DistractionRestriction int flag) { + switch (flag) { + case RESTRICTION_NONE: + return "NONE"; + case RESTRICTION_HIDE_FROM_SUGGESTIONS: + return "HIDE_FROM_SUGGESTIONS"; + case RESTRICTION_HIDE_NOTIFICATIONS: + return "HIDE_NOTIFICATIONS"; + default: + return "UNKNOWN"; + } + } + + private int runSuspend(boolean suspendedState, int flags) { + final PrintWriter pw = getOutPrintWriter(); + int userId = UserHandle.USER_SYSTEM; + String dialogMessage = null; + final PersistableBundle appExtras = new PersistableBundle(); + final PersistableBundle launcherExtras = new PersistableBundle(); + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--user": + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + case "--dialogMessage": + dialogMessage = getNextArgRequired(); + break; + case "--ael": + case "--aes": + case "--aed": + case "--lel": + case "--les": + case "--led": + final String key = getNextArgRequired(); + final String val = getNextArgRequired(); + if (!suspendedState) { + break; + } + final PersistableBundle bundleToInsert = + opt.startsWith("--a") ? appExtras : launcherExtras; + switch (opt.charAt(4)) { + case 'l': + bundleToInsert.putLong(key, Long.valueOf(val)); + break; + case 'd': + bundleToInsert.putDouble(key, Double.valueOf(val)); + break; + case 's': + bundleToInsert.putString(key, val); + break; + } + break; + default: + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + + final List packageNames = getRemainingArgs(); + if (packageNames.isEmpty()) { + pw.println("Error: package name not specified"); + return 1; + } + final String callingPackage = + (Binder.getCallingUid() == Process.ROOT_UID) ? "root" : "com.android.shell"; + + final SuspendDialogInfo info; + if (!TextUtils.isEmpty(dialogMessage)) { + info = new SuspendDialogInfo.Builder() + .setMessage(dialogMessage) + .build(); + } else { + info = null; + } + try { + final int translatedUserId = + translateUserId(userId, UserHandle.USER_NULL, "runSuspend"); + mInterface.setPackagesSuspendedAsUser(packageNames.toArray(new String[] {}), + suspendedState, ((appExtras.size() > 0) ? appExtras : null), + ((launcherExtras.size() > 0) ? launcherExtras : null), + info, flags, callingPackage, UserHandle.USER_SYSTEM, translatedUserId); + for (int i = 0; i < packageNames.size(); i++) { + final String packageName = packageNames.get(i); + pw.println("Package " + packageName + " new suspended state: " + + mInterface.isPackageSuspendedForUser(packageName, translatedUserId)); + } + return 0; + } catch (RemoteException | IllegalArgumentException e) { + pw.println(e.toString()); + return 1; + } + } + + private int runGrantRevokePermission(boolean grant) throws RemoteException { + int userId = UserHandle.USER_SYSTEM; + + String opt; + boolean allPermissions = false; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } + if (opt.equals("--all-permissions")) { + allPermissions = true; + } + } + + String pkg = getNextArg(); + if (!allPermissions && pkg == null) { + getErrPrintWriter().println("Error: no package specified"); + return 1; + } + String perm = getNextArg(); + if (!allPermissions && perm == null) { + getErrPrintWriter().println("Error: no permission specified"); + return 1; + } + if (allPermissions && perm != null) { + getErrPrintWriter().println("Error: permission specified but not expected"); + return 1; + } + final UserHandle translatedUser = UserHandle.of(translateUserId(userId, + UserHandle.USER_NULL, "runGrantRevokePermission")); + + List packageInfos; + PackageManager pm = mContext.createContextAsUser(translatedUser, 0).getPackageManager(); + if (pkg == null) { + packageInfos = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS); + } else { + try { + packageInfos = Collections.singletonList(pm.getPackageInfo(pkg, + PackageManager.GET_PERMISSIONS)); + } catch (NameNotFoundException e) { + getErrPrintWriter().println("Error: package not found"); + getOutPrintWriter().println("Failure [package not found]"); + return 1; + } + } + + for (PackageInfo packageInfo : packageInfos) { + List permissions = Collections.singletonList(perm); + if (allPermissions) { + permissions = getRequestedRuntimePermissions(packageInfo); + } + for (String permission : permissions) { + if (grant) { + try { + mPermissionManager.grantRuntimePermission(packageInfo.packageName, + permission, + translatedUser); + } catch (Exception e) { + if (!allPermissions) { + throw e; + } else { + Slog.w(TAG, "Could not grant permission " + permission, e); + } + } + } else { + try { + mPermissionManager.revokeRuntimePermission(packageInfo.packageName, + permission, + translatedUser, null); + } catch (Exception e) { + if (!allPermissions) { + throw e; + } else { + Slog.w(TAG, "Could not grant permission " + permission, e); + } + } + } + } + } + return 0; + } + + private List getRequestedRuntimePermissions(PackageInfo info) { + // No requested permissions + if (info.requestedPermissions == null) { + return new ArrayList<>(); + } + List result = new ArrayList<>(); + PackageManager pm = mContext.getPackageManager(); + // Iterate through requested permissions for denied ones + for (String permission : info.requestedPermissions) { + PermissionInfo pi = null; + try { + pi = pm.getPermissionInfo(permission, 0); + } catch (NameNotFoundException nnfe) { + // ignore + } + if (pi == null) { + continue; + } + if (pi.getProtection() != PermissionInfo.PROTECTION_DANGEROUS) { + continue; + } + result.add(permission); + } + return result; + } + + private int runResetPermissions() throws RemoteException { + mLegacyPermissionManager.resetRuntimePermissions(); + return 0; + } + + private int setOrClearPermissionFlags(boolean setFlags) { + int userId = UserHandle.USER_SYSTEM; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } + } + + String pkg = getNextArg(); + if (pkg == null) { + getErrPrintWriter().println("Error: no package specified"); + return 1; + } + String perm = getNextArg(); + if (perm == null) { + getErrPrintWriter().println("Error: no permission specified"); + return 1; + } + + int flagMask = 0; + String flagName = getNextArg(); + if (flagName == null) { + getErrPrintWriter().println("Error: no permission flags specified"); + return 1; + } + while (flagName != null) { + if (!SUPPORTED_PERMISSION_FLAGS.containsKey(flagName)) { + getErrPrintWriter().println("Error: specified flag " + flagName + " is not one of " + + SUPPORTED_PERMISSION_FLAGS_LIST); + return 1; + } + flagMask |= SUPPORTED_PERMISSION_FLAGS.get(flagName); + flagName = getNextArg(); + } + + final UserHandle translatedUser = UserHandle.of(translateUserId(userId, + UserHandle.USER_NULL, "runGrantRevokePermission")); + int flagSet = setFlags ? flagMask : 0; + mPermissionManager.updatePermissionFlags(pkg, perm, flagMask, flagSet, translatedUser); + return 0; + } + + private int runSetPermissionEnforced() throws RemoteException { + final String permission = getNextArg(); + if (permission == null) { + getErrPrintWriter().println("Error: no permission specified"); + return 1; + } + final String enforcedRaw = getNextArg(); + if (enforcedRaw == null) { + getErrPrintWriter().println("Error: no enforcement specified"); + return 1; + } + // Permissions are always enforced now. + return 0; + } + + private boolean isVendorApp(String pkg) { + try { + final PackageInfo info = mInterface.getPackageInfo( + pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM); + return info != null && info.applicationInfo.isVendor(); + } catch (RemoteException e) { + return false; + } + } + + private boolean isProductApp(String pkg) { + try { + final PackageInfo info = mInterface.getPackageInfo( + pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM); + return info != null && info.applicationInfo.isProduct(); + } catch (RemoteException e) { + return false; + } + } + + private boolean isSystemExtApp(String pkg) { + try { + final PackageInfo info = mInterface.getPackageInfo( + pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM); + return info != null && info.applicationInfo.isSystemExt(); + } catch (RemoteException e) { + return false; + } + } + + private String getApexPackageNameContainingPackage(String pkg) { + ApexManager apexManager = ApexManager.getInstance(); + return apexManager.getActiveApexPackageNameContainingPackage(pkg); + } + + private boolean isApexApp(String pkg) { + return getApexPackageNameContainingPackage(pkg) != null; + } + + private int runGetPrivappPermissions() { + final String pkg = getNextArg(); + if (pkg == null) { + getErrPrintWriter().println("Error: no package specified."); + return 1; + } + getOutPrintWriter().println(getPrivAppPermissionsString(pkg, true)); + return 0; + } + + private int runGetPrivappDenyPermissions() { + final String pkg = getNextArg(); + if (pkg == null) { + getErrPrintWriter().println("Error: no package specified."); + return 1; + } + getOutPrintWriter().println(getPrivAppPermissionsString(pkg, false)); + return 0; + } + + @NonNull + private String getPrivAppPermissionsString(@NonNull String packageName, boolean allowed) { + final PermissionAllowlist permissionAllowlist = + SystemConfig.getInstance().getPermissionAllowlist(); + final ArrayMap> privAppPermissions; + if (isVendorApp(packageName)) { + privAppPermissions = permissionAllowlist.getVendorPrivilegedAppAllowlist(); + } else if (isProductApp(packageName)) { + privAppPermissions = permissionAllowlist.getProductPrivilegedAppAllowlist(); + } else if (isSystemExtApp(packageName)) { + privAppPermissions = permissionAllowlist.getSystemExtPrivilegedAppAllowlist(); + } else if (isApexApp(packageName)) { + final String moduleName = ApexManager.getInstance().getApexModuleNameForPackageName( + getApexPackageNameContainingPackage(packageName)); + privAppPermissions = permissionAllowlist.getApexPrivilegedAppAllowlists() + .get(moduleName); + } else { + privAppPermissions = permissionAllowlist.getPrivilegedAppAllowlist(); + } + final ArrayMap permissions = privAppPermissions != null + ? privAppPermissions.get(packageName) : null; + if (permissions == null) { + return "{}"; + } + final StringBuilder result = new StringBuilder("{"); + boolean isFirstPermission = true; + final int permissionsSize = permissions.size(); + for (int i = 0; i < permissionsSize; i++) { + boolean permissionAllowed = permissions.valueAt(i); + if (permissionAllowed != allowed) { + continue; + } + if (isFirstPermission) { + isFirstPermission = false; + } else { + result.append(", "); + } + String permissionName = permissions.keyAt(i); + result.append(permissionName); + } + result.append("}"); + return result.toString(); + } + + private int runGetOemPermissions() { + final String pkg = getNextArg(); + if (pkg == null) { + getErrPrintWriter().println("Error: no package specified."); + return 1; + } + final Map oemPermissions = SystemConfig.getInstance() + .getPermissionAllowlist().getOemAppAllowlist().get(pkg); + if (oemPermissions == null || oemPermissions.isEmpty()) { + getOutPrintWriter().println("{}"); + } else { + oemPermissions.forEach((permission, granted) -> + getOutPrintWriter().println(permission + " granted:" + granted) + ); + } + return 0; + } + + private int runTrimCaches() throws RemoteException { + String size = getNextArg(); + if (size == null) { + getErrPrintWriter().println("Error: no size specified"); + return 1; + } + long multiplier = 1; + int len = size.length(); + char c = size.charAt(len - 1); + if (c < '0' || c > '9') { + if (c == 'K' || c == 'k') { + multiplier = 1024L; + } else if (c == 'M' || c == 'm') { + multiplier = 1024L*1024L; + } else if (c == 'G' || c == 'g') { + multiplier = 1024L*1024L*1024L; + } else { + getErrPrintWriter().println("Invalid suffix: " + c); + return 1; + } + size = size.substring(0, len-1); + } + long sizeVal; + try { + sizeVal = Long.parseLong(size) * multiplier; + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: expected number at: " + size); + return 1; + } + String volumeUuid = getNextArg(); + if ("internal".equals(volumeUuid)) { + volumeUuid = null; + } + ClearDataObserver obs = new ClearDataObserver(); + mInterface.freeStorageAndNotify(volumeUuid, sizeVal, + StorageManager.FLAG_ALLOCATE_DEFY_ALL_RESERVED, obs); + synchronized (obs) { + while (!obs.finished) { + try { + obs.wait(); + } catch (InterruptedException e) { + } + } + } + return 0; + } + + private static boolean isNumber(String s) { + try { + Integer.parseInt(s); + } catch (NumberFormatException nfe) { + return false; + } + return true; + } + + public int runCreateUser() throws RemoteException { + String name; + int userId = -1; + int flags = 0; + String userType = null; + String opt; + boolean preCreateOnly = false; + while ((opt = getNextOption()) != null) { + String newUserType = null; + if ("--profileOf".equals(opt)) { + userId = translateUserId(UserHandle.parseUserArg(getNextArgRequired()), + UserHandle.USER_ALL, "runCreateUser"); + } else if ("--managed".equals(opt)) { + newUserType = UserManager.USER_TYPE_PROFILE_MANAGED; + } else if ("--restricted".equals(opt)) { + newUserType = UserManager.USER_TYPE_FULL_RESTRICTED; + } else if ("--guest".equals(opt)) { + newUserType = UserManager.USER_TYPE_FULL_GUEST; + } else if ("--demo".equals(opt)) { + newUserType = UserManager.USER_TYPE_FULL_DEMO; + } else if ("--ephemeral".equals(opt)) { + flags |= UserInfo.FLAG_EPHEMERAL; + } else if ("--for-testing".equals(opt)) { + flags |= UserInfo.FLAG_FOR_TESTING; + } else if ("--pre-create-only".equals(opt)) { + preCreateOnly = true; + } else if ("--user-type".equals(opt)) { + newUserType = getNextArgRequired(); + } else { + getErrPrintWriter().println("Error: unknown option " + opt); + return 1; + } + // Ensure only one user-type was specified. + if (newUserType != null) { + if (userType != null && !userType.equals(newUserType)) { + getErrPrintWriter().println("Error: more than one user type was specified (" + + userType + " and " + newUserType + ")"); + return 1; + } + userType = newUserType; + } + } + String arg = getNextArg(); + if (arg == null && !preCreateOnly) { + getErrPrintWriter().println("Error: no user name specified."); + return 1; + } + if (arg != null && preCreateOnly) { + getErrPrintWriter().println("Warning: name is ignored for pre-created users"); + } + + name = arg; + UserInfo info = null; + IUserManager um = IUserManager.Stub.asInterface( + ServiceManager.getService(Context.USER_SERVICE)); + IAccountManager accm = IAccountManager.Stub.asInterface( + ServiceManager.getService(Context.ACCOUNT_SERVICE)); + if (userType == null) { + userType = UserInfo.getDefaultUserType(flags); + } + Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "shell_runCreateUser"); + try { + if (UserManager.isUserTypeRestricted(userType)) { + // In non-split user mode, userId can only be SYSTEM + int parentUserId = userId >= 0 ? userId : UserHandle.USER_SYSTEM; + info = um.createRestrictedProfileWithThrow(name, parentUserId); + accm.addSharedAccountsFromParentUser(parentUserId, userId, + (Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell"); + } else if (userId < 0) { + info = preCreateOnly ? + um.preCreateUserWithThrow(userType) : + um.createUserWithThrow(name, userType, flags); + } else { + info = um.createProfileForUserWithThrow(name, userType, flags, userId, null); + } + } catch (ServiceSpecificException e) { + getErrPrintWriter().println("Error: " + e); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER); + } + + if (info != null) { + getOutPrintWriter().println("Success: created user id " + info.id); + return 0; + } else { + getErrPrintWriter().println("Error: couldn't create User."); + return 1; + } + } + + // pm remove-user [--set-ephemeral-if-in-use][--wait] USER_ID + public int runRemoveUser() throws RemoteException { + int userId; + String arg; + boolean setEphemeralIfInUse = false; + boolean wait = false; + + while ((arg = getNextOption()) != null) { + switch (arg) { + case "--set-ephemeral-if-in-use": + setEphemeralIfInUse = true; + break; + case "--wait": // fallthrough + case "-w": + wait = true; + break; + default: + getErrPrintWriter().println("Error: unknown option: " + arg); + return -1; + } + } + + arg = getNextArg(); + if (arg == null) { + getErrPrintWriter().println("Error: no user id specified."); + return 1; + } + userId = UserHandle.parseUserArg(arg); + IUserManager um = IUserManager.Stub.asInterface( + ServiceManager.getService(Context.USER_SERVICE)); + if (setEphemeralIfInUse) { + return removeUserWhenPossible(um, userId); + } else { + final boolean success = wait ? removeUserAndWait(um, userId) : removeUser(um, userId); + if (success) { + getOutPrintWriter().println("Success: removed user"); + return 0; + } else { + // Error message should already have been printed. + return 1; + } + } + } + + private boolean removeUser(IUserManager um, @UserIdInt int userId) throws RemoteException { + Slog.i(TAG, "Removing user " + userId); + if (um.removeUser(userId)) { + return true; + } else { + getErrPrintWriter().println("Error: couldn't remove user id " + userId); + return false; + } + } + + private boolean removeUserAndWait(IUserManager um, @UserIdInt int userId) + throws RemoteException { + Slog.i(TAG, "Removing (and waiting for completion) user " + userId); + + final CountDownLatch waitLatch = new CountDownLatch(1); + final UserManagerInternal.UserLifecycleListener listener = + new UserManagerInternal.UserLifecycleListener() { + @Override + public void onUserRemoved(UserInfo user) { + if (userId == user.id) { + waitLatch.countDown(); + } + } + }; + + final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class); + umi.addUserLifecycleListener(listener); + + try { + if (um.removeUser(userId)) { + final boolean awaitSuccess = waitLatch.await(10, TimeUnit.MINUTES); + if (!awaitSuccess) { + getErrPrintWriter().printf("Error: Remove user %d timed out\n", userId); + return false; + } + // Success! + return true; + } else { + getErrPrintWriter().println("Error: couldn't remove user id " + userId); + return false; + } + } catch (InterruptedException e) { + getErrPrintWriter().printf("Error: Remove user %d wait interrupted: %s\n", userId, e); + Thread.currentThread().interrupt(); + return false; + } finally { + umi.removeUserLifecycleListener(listener); + } + } + + private int removeUserWhenPossible(IUserManager um, @UserIdInt int userId) + throws RemoteException { + Slog.i(TAG, "Removing " + userId + " or set as ephemeral if in use."); + int result = um.removeUserWhenPossible(userId, /* overrideDevicePolicy= */ false); + switch (result) { + case UserManager.REMOVE_RESULT_REMOVED: + getOutPrintWriter().printf("Success: user %d removed\n", userId); + return 0; + case UserManager.REMOVE_RESULT_DEFERRED: + getOutPrintWriter().printf("Success: user %d set as ephemeral\n", userId); + return 0; + case UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED: + getOutPrintWriter().printf("Success: user %d is already being removed\n", userId); + return 0; + case UserManager.REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN: + getErrPrintWriter().printf("Error: user %d is a permanent admin main user\n", + userId); + return 1; + default: + getErrPrintWriter().printf("Error: couldn't remove or mark ephemeral user id %d\n", + userId); + return 1; + } + } + + private int runMarkGuestForDeletion() throws RemoteException { + String arg = getNextArg(); + if (arg == null) { + getErrPrintWriter().println("Error: no user id specified."); + return 1; + } + int userId = resolveUserId(UserHandle.parseUserArg(arg)); + + IUserManager um = IUserManager.Stub.asInterface( + ServiceManager.getService(Context.USER_SERVICE)); + if (!um.markGuestForDeletion(userId)) { + getErrPrintWriter().println("Error: could not mark guest for deletion"); + return 1; + } + + return 0; + } + + private int runRenameUser() throws RemoteException { + String arg = getNextArg(); + if (arg == null) { + getErrPrintWriter().println("Error: no user id specified."); + return 1; + } + int userId = resolveUserId(UserHandle.parseUserArg(arg)); + + String name = getNextArg(); + if (name == null) { + Slog.i(TAG, "Resetting name of user " + userId); + } else { + Slog.i(TAG, "Renaming user " + userId + " to '" + name + "'"); + } + + IUserManager um = IUserManager.Stub.asInterface( + ServiceManager.getService(Context.USER_SERVICE)); + um.setUserName(userId, name); + + return 0; + } + + public int runSetUserRestriction() throws RemoteException { + int userId = UserHandle.USER_SYSTEM; + String opt = getNextOption(); + if (opt != null && "--user".equals(opt)) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } + + String restriction = getNextArg(); + String arg = getNextArg(); + boolean value; + if ("1".equals(arg)) { + value = true; + } else if ("0".equals(arg)) { + value = false; + } else { + getErrPrintWriter().println("Error: valid value not specified"); + return 1; + } + final int translatedUserId = + translateUserId(userId, UserHandle.USER_NULL, "runSetUserRestriction"); + final IUserManager um = IUserManager.Stub.asInterface( + ServiceManager.getService(Context.USER_SERVICE)); + um.setUserRestriction(restriction, value, translatedUserId); + return 0; + } + + private int runGetUserRestriction() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + int userId = UserHandle.USER_SYSTEM; + boolean getAllRestrictions = false; + + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--user": + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + case "--all": + getAllRestrictions = true; + if (getNextArg() != null) { + throw new IllegalArgumentException("Argument unexpected after \"--all\""); + } + break; + default: + throw new IllegalArgumentException("Unknown option " + opt); + } + } + + final int translatedUserId = + translateUserId(userId, UserHandle.USER_NULL, "runGetUserRestriction"); + final IUserManager um = IUserManager.Stub.asInterface( + ServiceManager.getService(Context.USER_SERVICE)); + + if (getAllRestrictions) { + final Bundle restrictions = um.getUserRestrictions(translatedUserId); + pw.println("All restrictions:"); + pw.println(restrictions.toString()); + } else { + String restriction = getNextArg(); + if (restriction == null) { + throw new IllegalArgumentException("No restriction key specified"); + } + String unexpectedArgument = getNextArg(); + if (unexpectedArgument != null) { + throw new IllegalArgumentException("Argument unexpected after restriction key"); + } + pw.println(um.hasUserRestriction(restriction, translatedUserId)); + } + return 0; + } + + public int runSupportsMultipleUsers() { + getOutPrintWriter().println("Is multiuser supported: " + + UserManager.supportsMultipleUsers()); + return 0; + } + + public int runGetMaxUsers() { + getOutPrintWriter().println("Maximum supported users: " + + UserManager.getMaxSupportedUsers()); + return 0; + } + + public int runGetMaxRunningUsers() { + ActivityManagerInternal activityManagerInternal = + LocalServices.getService(ActivityManagerInternal.class); + getOutPrintWriter().println("Maximum supported running users: " + + activityManagerInternal.getMaxRunningUsers()); + return 0; + } + + private static class InstallParams { + SessionParams sessionParams; + String installerPackageName; + int userId = UserHandle.USER_ALL; + long stagedReadyTimeoutMs = DEFAULT_STAGED_READY_TIMEOUT_MS; + } + + private InstallParams makeInstallParams(Set unsupportedOptions) { + final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL); + final InstallParams params = new InstallParams(); + + params.sessionParams = sessionParams; + // Allowlist all permissions by default + sessionParams.installFlags |= PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS; + // Set package source to other by default + sessionParams.setPackageSource(PackageInstaller.PACKAGE_SOURCE_OTHER); + + // Encodes one of the states: + // 1. Install request explicitly specified --staged, then value will be true. + // 2. Install request explicitly specified --non-staged, then value will be false. + // 3. Install request did not specify either --staged or --non-staged, then for APEX + // installs the value will be true, and for apk installs it will be false. + Boolean staged = null; + + String opt; + boolean replaceExisting = true; + boolean forceNonStaged = false; + while ((opt = getNextOption()) != null) { + if (unsupportedOptions.contains(opt)) { + throw new IllegalArgumentException("Unsupported option " + opt); + } + switch (opt) { + case "-r": // ignore + break; + case "-R": + replaceExisting = false; + break; + case "-i": + params.installerPackageName = getNextArg(); + if (params.installerPackageName == null) { + throw new IllegalArgumentException("Missing installer package"); + } + break; + case "-t": + sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST; + break; + case "-f": + sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL; + break; + case "-d": + sessionParams.installFlags |= PackageManager.INSTALL_REQUEST_DOWNGRADE; + break; + case "-g": + sessionParams.installFlags |= + PackageManager.INSTALL_GRANT_ALL_REQUESTED_PERMISSIONS; + break; + case "--restrict-permissions": + sessionParams.installFlags &= + ~PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS; + break; + case "--dont-kill": + sessionParams.installFlags |= PackageManager.INSTALL_DONT_KILL_APP; + break; + case "--originating-uri": + sessionParams.originatingUri = Uri.parse(getNextArg()); + break; + case "--referrer": + sessionParams.referrerUri = Uri.parse(getNextArg()); + break; + case "-p": + sessionParams.mode = SessionParams.MODE_INHERIT_EXISTING; + sessionParams.appPackageName = getNextArg(); + if (sessionParams.appPackageName == null) { + throw new IllegalArgumentException("Missing inherit package name"); + } + break; + case "--pkg": + sessionParams.appPackageName = getNextArg(); + if (sessionParams.appPackageName == null) { + throw new IllegalArgumentException("Missing package name"); + } + break; + case "-S": + final long sizeBytes = Long.parseLong(getNextArg()); + if (sizeBytes <= 0) { + throw new IllegalArgumentException("Size must be positive"); + } + sessionParams.setSize(sizeBytes); + break; + case "--abi": + sessionParams.abiOverride = checkAbiArgument(getNextArg()); + break; + case "--ephemeral": + case "--instant": + case "--instantapp": + sessionParams.setInstallAsInstantApp(true /*isInstantApp*/); + break; + case "--full": + sessionParams.setInstallAsInstantApp(false /*isInstantApp*/); + break; + case "--preload": + sessionParams.setInstallAsVirtualPreload(); + break; + case "--user": + params.userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + case "--install-location": + sessionParams.installLocation = Integer.parseInt(getNextArg()); + break; + case "--install-reason": + sessionParams.installReason = Integer.parseInt(getNextArg()); + break; + case "--update-ownership": + if (params.installerPackageName == null) { + // Enabling update ownership enforcement needs an installer. Since the + // default installer is null when using adb install, that effectively + // disable this enforcement. + params.installerPackageName = "com.android.shell"; + } + sessionParams.installFlags |= PackageManager.INSTALL_REQUEST_UPDATE_OWNERSHIP; + break; + case "--force-uuid": + sessionParams.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID; + sessionParams.volumeUuid = getNextArg(); + if ("internal".equals(sessionParams.volumeUuid)) { + sessionParams.volumeUuid = null; + } + break; + case "--force-sdk": // ignore + break; + case "--apex": + sessionParams.setInstallAsApex(); + break; + case "--force-non-staged": + forceNonStaged = true; + break; + case "--multi-package": + sessionParams.setMultiPackage(); + break; + case "--staged": + staged = true; + break; + case "--non-staged": + staged = false; + break; + case "--force-queryable": + sessionParams.setForceQueryable(); + break; + case "--enable-rollback": + if (params.installerPackageName == null) { + // com.android.shell has the TEST_MANAGE_ROLLBACKS + // permission needed to enable rollback for non-module + // packages, which is likely what the user wants when + // enabling rollback through the shell command. Set + // the installer to com.android.shell if no installer + // has been provided so that the user doesn't have to + // remember to set it themselves. + params.installerPackageName = "com.android.shell"; + } + int rollbackStrategy = PackageManager.ROLLBACK_DATA_POLICY_RESTORE; + try { + rollbackStrategy = Integer.parseInt(peekNextArg()); + if (rollbackStrategy < PackageManager.ROLLBACK_DATA_POLICY_RESTORE + || rollbackStrategy > PackageManager.ROLLBACK_DATA_POLICY_RETAIN) { + throw new IllegalArgumentException( + rollbackStrategy + " is not a valid rollback data policy."); + } + getNextArg(); // pop the argument + } catch (NumberFormatException e) { + // not followed by a number assume ROLLBACK_DATA_POLICY_RESTORE. + } + sessionParams.setEnableRollback(true, rollbackStrategy); + break; + case "--staged-ready-timeout": + params.stagedReadyTimeoutMs = Long.parseLong(getNextArgRequired()); + break; + case "--skip-verification": + sessionParams.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION; + break; + case "--skip-enable": + sessionParams.setApplicationEnabledSettingPersistent(); + break; + case "--bypass-low-target-sdk-block": + sessionParams.installFlags |= + PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK; + break; + case "--ignore-dexopt-profile": + sessionParams.installFlags |= PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE; + break; + default: + throw new IllegalArgumentException("Unknown option " + opt); + } + } + if (staged == null) { + staged = (sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0; + } + if (replaceExisting) { + sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; + } + if (forceNonStaged) { + sessionParams.isStaged = false; + sessionParams.developmentInstallFlags |= + PackageManager.INSTALL_DEVELOPMENT_FORCE_NON_STAGED_APEX_UPDATE; + } else if (staged) { + sessionParams.setStaged(); + } + if ((sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0 + && (sessionParams.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0 + && sessionParams.rollbackDataPolicy == PackageManager.ROLLBACK_DATA_POLICY_WIPE) { + throw new IllegalArgumentException("Data policy 'wipe' is not supported for apex."); + } + return params; + } + + private int runSetHomeActivity() { + final PrintWriter pw = getOutPrintWriter(); + int userId = UserHandle.USER_SYSTEM; + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--user": + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + default: + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + + String pkgName; + String component = getNextArg(); + if (component.indexOf('/') < 0) { + // No component specified, so assume it's just a package name. + pkgName = component; + } else { + ComponentName componentName = + component != null ? ComponentName.unflattenFromString(component) : null; + if (componentName == null) { + pw.println("Error: invalid component name"); + return 1; + } + pkgName = componentName.getPackageName(); + } + final int translatedUserId = + translateUserId(userId, UserHandle.USER_NULL, "runSetHomeActivity"); + final CompletableFuture future = new CompletableFuture<>(); + try { + RoleManager roleManager = mContext.getSystemService(RoleManager.class); + roleManager.addRoleHolderAsUser(RoleManager.ROLE_HOME, pkgName, 0, + UserHandle.of(translatedUserId), FgThread.getExecutor(), future::complete); + boolean success = future.get(); + if (success) { + pw.println("Success"); + return 0; + } else { + pw.println("Error: Failed to set default home."); + return 1; + } + } catch (Exception e) { + pw.println(e.toString()); + return 1; + } + } + + private int runSetInstaller() throws RemoteException { + final String targetPackage = getNextArg(); + final String installerPackageName = getNextArg(); + + if (targetPackage == null || installerPackageName == null) { + getErrPrintWriter().println("Must provide both target and installer package names"); + return 1; + } + + mInterface.setInstallerPackageName(targetPackage, installerPackageName); + getOutPrintWriter().println("Success"); + return 0; + } + + private int runGetInstantAppResolver() { + final PrintWriter pw = getOutPrintWriter(); + try { + final ComponentName instantAppsResolver = mInterface.getInstantAppResolverComponent(); + if (instantAppsResolver == null) { + return 1; + } + pw.println(instantAppsResolver.flattenToString()); + return 0; + } catch (Exception e) { + pw.println(e.toString()); + return 1; + } + } + + private int runHasFeature() { + final PrintWriter err = getErrPrintWriter(); + final String featureName = getNextArg(); + if (featureName == null) { + err.println("Error: expected FEATURE name"); + return 1; + } + final String versionString = getNextArg(); + try { + final int version = (versionString == null) ? 0 : Integer.parseInt(versionString); + final boolean hasFeature = mInterface.hasSystemFeature(featureName, version); + getOutPrintWriter().println(hasFeature); + return hasFeature ? 0 : 1; + } catch (NumberFormatException e) { + err.println("Error: illegal version number " + versionString); + return 1; + } catch (RemoteException e) { + err.println(e.toString()); + return 1; + } + } + + private int runDump() { + String pkg = getNextArg(); + if (pkg == null) { + getErrPrintWriter().println("Error: no package specified"); + return 1; + } + ActivityManager.dumpPackageStateStatic(getOutFileDescriptor(), pkg); + return 0; + } + + private int runDumpPackage() { + String pkg = getNextArg(); + if (pkg == null) { + getErrPrintWriter().println("Error: no package specified"); + return 1; + } + try { + ((IBinder) mInterface).dump(getOutFileDescriptor(), new String[]{pkg}); + } catch (Throwable e) { + PrintWriter pw = getErrPrintWriter(); + pw.println("Failure dumping service:"); + e.printStackTrace(pw); + pw.flush(); + } + return 0; + } + + private int runSetHarmfulAppWarning() throws RemoteException { + int userId = UserHandle.USER_CURRENT; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } else { + getErrPrintWriter().println("Error: Unknown option: " + opt); + return -1; + } + } + + final int translatedUserId = + translateUserId(userId, UserHandle.USER_NULL, "runSetHarmfulAppWarning"); + final String packageName = getNextArgRequired(); + final String warning = getNextArg(); + + mInterface.setHarmfulAppWarning(packageName, warning, translatedUserId); + + return 0; + } + + private int runGetHarmfulAppWarning() throws RemoteException { + int userId = UserHandle.USER_CURRENT; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } else { + getErrPrintWriter().println("Error: Unknown option: " + opt); + return -1; + } + } + + final int translatedUserId = + translateUserId(userId, UserHandle.USER_NULL, "runGetHarmfulAppWarning"); + final String packageName = getNextArgRequired(); + final CharSequence warning = mInterface.getHarmfulAppWarning(packageName, translatedUserId); + if (!TextUtils.isEmpty(warning)) { + getOutPrintWriter().println(warning); + return 0; + } else { + return 1; + } + } + + private int runSetSilentUpdatesPolicy() { + final PrintWriter pw = getOutPrintWriter(); + String opt; + String installerPackageName = null; + Long throttleTimeInSeconds = null; + boolean reset = false; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--allow-unlimited-silent-updates": + installerPackageName = getNextArgRequired(); + break; + case "--throttle-time": + throttleTimeInSeconds = Long.parseLong(getNextArgRequired()); + break; + case "--reset": + reset = true; + break; + default: + pw.println("Error: Unknown option: " + opt); + return -1; + } + } + if (throttleTimeInSeconds != null && throttleTimeInSeconds < 0) { + pw.println("Error: Invalid value for \"--throttle-time\":" + throttleTimeInSeconds); + return -1; + } + + try { + final IPackageInstaller installer = mInterface.getPackageInstaller(); + if (reset) { + installer.setAllowUnlimitedSilentUpdates(null /* installerPackageName */); + installer.setSilentUpdatesThrottleTime(-1 /* restore to the default */); + } else { + if (installerPackageName != null) { + installer.setAllowUnlimitedSilentUpdates(installerPackageName); + } + if (throttleTimeInSeconds != null) { + installer.setSilentUpdatesThrottleTime(throttleTimeInSeconds); + } + } + } catch (RemoteException e) { + pw.println("Failure [" + + e.getClass().getName() + " - " + + e.getMessage() + "]"); + return -1; + } + return 1; + } + + private int runGetAppMetadata() { + mContext.enforceCallingOrSelfPermission(GET_APP_METADATA, "getAppMetadataFd"); + final PrintWriter pw = getOutPrintWriter(); + String pkgName = getNextArgRequired(); + ParcelFileDescriptor pfd = null; + try { + pfd = mInterface.getAppMetadataFd(pkgName, mContext.getUserId()); + } catch (RemoteException e) { + pw.println("Failure [" + e.getClass().getName() + " - " + e.getMessage() + "]"); + return -1; + } + if (pfd != null) { + try (BufferedReader br = new BufferedReader( + new InputStreamReader(new ParcelFileDescriptor.AutoCloseInputStream(pfd)))) { + while (br.ready()) { + pw.println(br.readLine()); + } + } catch (IOException e) { + pw.println("Failure [" + e.getClass().getName() + " - " + e.getMessage() + "]"); + return -1; + } + } + return 1; + } + + private int runWaitForHandler(boolean forBackgroundHandler) { + final PrintWriter pw = getOutPrintWriter(); + long timeoutMillis = 60000; // default timeout is 60 seconds + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--timeout": + timeoutMillis = Long.parseLong(getNextArgRequired()); + break; + default: + pw.println("Error: Unknown option: " + opt); + return -1; + } + } + if (timeoutMillis <= 0) { + pw.println("Error: --timeout value must be positive: " + timeoutMillis); + return -1; + } + final boolean success; + try { + success = mInterface.waitForHandler(timeoutMillis, forBackgroundHandler); + } catch (RemoteException e) { + pw.println("Failure [" + e.getClass().getName() + " - " + e.getMessage() + "]"); + return -1; + } + if (success) { + pw.println("Success"); + return 0; + } else { + pw.println("Timeout. PackageManager handlers are still busy."); + return -1; + } + } + + private int runArtServiceCommand() { + try (var in = ParcelFileDescriptor.dup(getInFileDescriptor()); + var out = ParcelFileDescriptor.dup(getOutFileDescriptor()); + var err = ParcelFileDescriptor.dup(getErrFileDescriptor())) { + return LocalManagerRegistry.getManagerOrThrow(ArtManagerLocal.class) + .handleShellCommand(getTarget(), in, out, err, getAllArgs()); + } catch (IOException e) { + throw new IllegalStateException(e); + } catch (ManagerNotFoundException e) { + PrintWriter epw = getErrPrintWriter(); + epw.println("ART Service is not ready. Please try again later"); + return -1; + } + } + + private static String checkAbiArgument(String abi) { + if (TextUtils.isEmpty(abi)) { + throw new IllegalArgumentException("Missing ABI argument"); + } + + if ("-".equals(abi)) { + return abi; + } + + final String[] supportedAbis = Build.SUPPORTED_ABIS; + for (String supportedAbi : supportedAbis) { + if (supportedAbi.equals(abi)) { + return abi; + } + } + + throw new IllegalArgumentException("ABI " + abi + " not supported on this device"); + } + + private int translateUserId(int userId, int allUserId, String logContext) { + final boolean allowAll = (allUserId != UserHandle.USER_NULL); + final int translatedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, allowAll, true, logContext, "pm command"); + return translatedUserId == UserHandle.USER_ALL ? allUserId : translatedUserId; + } + + private int doCreateSession(SessionParams params, String installerPackageName, int userId) + throws RemoteException { + if (userId == UserHandle.USER_ALL) { + params.installFlags |= PackageManager.INSTALL_ALL_USERS; + } + final int translatedUserId = + translateUserId(userId, UserHandle.USER_SYSTEM, "doCreateSession"); + final int sessionId = mInterface.getPackageInstaller() + .createSession(params, installerPackageName, null /*installerAttributionTag*/, + translatedUserId); + return sessionId; + } + + private int doAddFiles(int sessionId, ArrayList args, long sessionSizeBytes, + boolean isApex, boolean installArchived) throws RemoteException { + PackageInstaller.Session session = null; + try { + session = new PackageInstaller.Session( + mInterface.getPackageInstaller().openSession(sessionId)); + + // 1. Single file from stdin. + if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) { + final String name = "base" + RANDOM.nextInt() + "." + (isApex ? "apex" : "apk"); + final long size; + final Metadata metadata; + if (!installArchived) { + metadata = Metadata.forStdIn(name); + size = sessionSizeBytes; + } else { + metadata = Metadata.forArchived( + getArchivedPackage(STDIN_PATH, sessionSizeBytes)); + size = -1; + } + session.addFile(LOCATION_DATA_APP, name, size, metadata.toByteArray(), null); + return 0; + } + + for (String arg : args) { + final int delimLocation = arg.indexOf(':'); + + if (delimLocation != -1) { + // 2. File with specified size read from stdin. + if (installArchived) { + getOutPrintWriter().println( + "Error: can't install with size from STDIN for Archival install"); + return 1; + } + if (processArgForStdin(arg, session) != 0) { + return 1; + } + } else { + // 3. Local file. + processArgForLocalFile(arg, session, installArchived); + } + } + return 0; + } catch (IOException | IllegalArgumentException e) { + getErrPrintWriter().println("Failed to add file(s), reason: " + e); + getOutPrintWriter().println("Failure [failed to add file(s)]"); + return 1; + } finally { + IoUtils.closeQuietly(session); + } + } + + private int processArgForStdin(String arg, PackageInstaller.Session session) { + final String[] fileDesc = arg.split(":"); + String name, fileId; + long sizeBytes; + byte[] signature = null; + int streamingVersion = 0; + + try { + if (fileDesc.length < 2) { + getErrPrintWriter().println("Must specify file name and size"); + return 1; + } + name = fileDesc[0]; + sizeBytes = Long.parseUnsignedLong(fileDesc[1]); + fileId = name; + + if (fileDesc.length > 2 && !TextUtils.isEmpty(fileDesc[2])) { + fileId = fileDesc[2]; + } + if (fileDesc.length > 3) { + signature = Base64.getDecoder().decode(fileDesc[3]); + } + if (fileDesc.length > 4) { + streamingVersion = Integer.parseUnsignedInt(fileDesc[4]); + if (streamingVersion < 0 || streamingVersion > 1) { + getErrPrintWriter().println( + "Unsupported streaming version: " + streamingVersion); + return 1; + } + } + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Unable to parse file parameters: " + arg + ", reason: " + e); + return 1; + } + + if (TextUtils.isEmpty(name)) { + getErrPrintWriter().println("Empty file name in: " + arg); + return 1; + } + + final Metadata metadata; + + if (signature != null) { + // Streaming/adb mode. Versions: + // 0: data only streaming, tree has to be fully available, + // 1: tree and data streaming. + metadata = (streamingVersion == 0) ? Metadata.forDataOnlyStreaming(fileId) + : Metadata.forStreaming(fileId); + try { + if ((signature.length > 0) && (V4Signature.readFrom(signature) == null)) { + getErrPrintWriter().println("V4 signature is invalid in: " + arg); + return 1; + } + } catch (Exception e) { + getErrPrintWriter().println( + "V4 signature is invalid: " + e + " in " + arg); + return 1; + } + } else { + // Single-shot read from stdin. + metadata = Metadata.forStdIn(fileId); + } + + session.addFile(LOCATION_DATA_APP, name, sizeBytes, metadata.toByteArray(), signature); + return 0; + } + + private long getFileStatSize(File file) { + final ParcelFileDescriptor pfd = openFileForSystem(file.getPath(), "r"); + if (pfd == null) { + throw new IllegalArgumentException("Error: Can't open file: " + file.getPath()); + } + try { + return pfd.getStatSize(); + } finally { + IoUtils.closeQuietly(pfd); + } + } + + private ArchivedPackageParcel getArchivedPackage(String inPath, long sizeBytes) + throws RemoteException, IOException { + final var fdWithSize = openInFile(inPath, sizeBytes); + if (fdWithSize.first == null) { + throw new IllegalArgumentException("Error: Can't open file: " + inPath); + } + + final String encoded; + final ParcelFileDescriptor fd = fdWithSize.first; + final int size = (int) (long) fdWithSize.second; + try (InputStream inStream = new AutoCloseInputStream(fd)) { + byte[] bytes = new byte[size]; + Streams.readFully(inStream, bytes); + encoded = new String(bytes); + } catch (IOException e) { + throw new IllegalArgumentException("Error: Can't load archived package from: " + inPath, + e); + } + + var result = Metadata.readArchivedPackageParcel(HexEncoding.decode(encoded)); + if (result == null) { + throw new IllegalArgumentException( + "Error: Can't parse archived package from: " + inPath); + } + return result; + } + + private void processArgForLocalFile(String arg, PackageInstaller.Session session, + boolean installArchived) throws IOException, RemoteException { + final String inPath = arg; + + final File file = new File(inPath); + final String name = file.getName(); + final long size; + final Metadata metadata; + if (installArchived) { + metadata = Metadata.forArchived(getArchivedPackage(inPath, -1)); + size = 0; + } else { + metadata = Metadata.forLocalFile(inPath); + size = getFileStatSize(file); + } + + byte[] v4signatureBytes = null; + if (!installArchived) { + // Try to load the v4 signature file for the APK; it might not exist. + final String v4SignaturePath = inPath + V4Signature.EXT; + final ParcelFileDescriptor pfd = openFileForSystem(v4SignaturePath, "r"); + if (pfd != null) { + try { + final V4Signature v4signature = V4Signature.readFrom(pfd); + v4signatureBytes = v4signature.toByteArray(); + } catch (IOException ex) { + Slog.e(TAG, "V4 signature file exists but failed to be parsed.", ex); + } finally { + IoUtils.closeQuietly(pfd); + } + } + } + + session.addFile(LOCATION_DATA_APP, name, size, metadata.toByteArray(), v4signatureBytes); + } + + private int doWriteSplits(int sessionId, ArrayList splitPaths, long sessionSizeBytes, + boolean isApex) throws RemoteException { + final boolean multipleSplits = splitPaths.size() > 1; + for (String splitPath : splitPaths) { + String splitName = multipleSplits ? new File(splitPath).getName() + : "base." + (isApex ? "apex" : "apk"); + + if (doWriteSplit(sessionId, splitPath, sessionSizeBytes, splitName, + false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { + return 1; + } + } + return 0; + } + + private Pair openInFile(String inPath, long sizeBytes) + throws IOException { + final ParcelFileDescriptor fd; + if (STDIN_PATH.equals(inPath)) { + fd = ParcelFileDescriptor.dup(getInFileDescriptor()); + } else if (inPath != null) { + fd = openFileForSystem(inPath, "r"); + if (fd == null) { + return Pair.create(null, -1L); + } + sizeBytes = fd.getStatSize(); + if (sizeBytes < 0) { + fd.close(); + getErrPrintWriter().println("Unable to get size of: " + inPath); + return Pair.create(null, -1L); + } + } else { + fd = ParcelFileDescriptor.dup(getInFileDescriptor()); + } + if (sizeBytes <= 0) { + getErrPrintWriter().println("Error: must specify an APK size"); + return Pair.create(null, 1L); + } + return Pair.create(fd, sizeBytes); + } + + private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName, + boolean logSuccess) throws RemoteException { + PackageInstaller.Session session = null; + try { + session = new PackageInstaller.Session( + mInterface.getPackageInstaller().openSession(sessionId)); + + final PrintWriter pw = getOutPrintWriter(); + + final var fdWithSize = openInFile(inPath, sizeBytes); + if (fdWithSize.first == null) { + long resultCode = fdWithSize.second; + return (int) resultCode; + } + final ParcelFileDescriptor fd = fdWithSize.first; + sizeBytes = fdWithSize.second; + + session.write(splitName, 0, sizeBytes, fd); + + if (logSuccess) { + pw.println("Success: streamed " + sizeBytes + " bytes"); + } + return 0; + } catch (IOException e) { + getErrPrintWriter().println("Error: failed to write; " + e.getMessage()); + return 1; + } finally { + IoUtils.closeQuietly(session); + } + } + + private int doInstallAddSession(int parentId, int[] sessionIds, boolean logSuccess) + throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + PackageInstaller.Session session = null; + try { + session = new PackageInstaller.Session( + mInterface.getPackageInstaller().openSession(parentId)); + if (!session.isMultiPackage()) { + getErrPrintWriter().println( + "Error: parent session ID is not a multi-package session"); + return 1; + } + for (int i = 0; i < sessionIds.length; i++) { + session.addChildSessionId(sessionIds[i]); + } + if (logSuccess) { + pw.println("Success"); + } + return 0; + } finally { + IoUtils.closeQuietly(session); + } + } + + private int doRemoveSplits(int sessionId, Collection splitNames, boolean logSuccess) + throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + PackageInstaller.Session session = null; + try { + session = new PackageInstaller.Session( + mInterface.getPackageInstaller().openSession(sessionId)); + for (String splitName : splitNames) { + session.removeSplit(splitName); + } + + if (logSuccess) { + pw.println("Success"); + } + return 0; + } catch (IOException e) { + pw.println("Error: failed to remove split; " + e.getMessage()); + return 1; + } finally { + IoUtils.closeQuietly(session); + } + } + + private int doCommitSession(int sessionId, boolean logSuccess) + throws RemoteException { + + final PrintWriter pw = getOutPrintWriter(); + PackageInstaller.Session session = null; + try { + session = new PackageInstaller.Session( + mInterface.getPackageInstaller().openSession(sessionId)); + if (!session.isMultiPackage() && !session.isStaged()) { + // Validity check that all .dm files match an apk. + // (The installer does not support standalone .dm files and will not process them.) + try { + DexMetadataHelper.validateDexPaths(session.getNames()); + } catch (IllegalStateException | IOException e) { + pw.println( + "Warning [Could not validate the dex paths: " + e.getMessage() + "]"); + } + } + final LocalIntentReceiver receiver = new LocalIntentReceiver(); + session.commit(receiver.getIntentSender()); + if (!session.isStaged()) { + final Intent result = receiver.getResult(); + int status = result.getIntExtra( + PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); + List warnings = + result.getStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS); + if (status == PackageInstaller.STATUS_SUCCESS) { + if (!ArrayUtils.isEmpty(warnings)) { + // Don't start the output string with "Success" because that will make adb + // treat this as a success. + for (String warning : warnings) { + pw.println("Warning: " + warning); + } + // Treat warnings as failure to draw app developers' attention. + status = PackageInstaller.STATUS_FAILURE; + pw.println("Completed with warning(s)"); + } else if (logSuccess) { + pw.println("Success"); + } + } else { + pw.println("Failure [" + + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]"); + } + return status; + } else { + // Return immediately without retrieving the result. The caller will decide + // whether to wait for the session to become ready. + if (logSuccess) { + pw.println("Success"); + } + return PackageInstaller.STATUS_SUCCESS; + } + } finally { + IoUtils.closeQuietly(session); + } + } + + private int doAbandonSession(int sessionId, boolean logSuccess) throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + PackageInstaller.Session session = null; + try { + session = new PackageInstaller.Session( + mInterface.getPackageInstaller().openSession(sessionId)); + session.abandon(); + if (logSuccess) { + pw.println("Success"); + } + return 0; + } finally { + IoUtils.closeQuietly(session); + } + } + + private void doListPermissions(ArrayList groupList, boolean groups, boolean labels, + boolean summary, int startProtectionLevel, int endProtectionLevel) + throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final int groupCount = groupList.size(); + for (int i = 0; i < groupCount; i++) { + String groupName = groupList.get(i); + String prefix = ""; + if (groups) { + if (i > 0) { + pw.println(""); + } + if (groupName != null) { + PermissionGroupInfo pgi = + mInterface.getPermissionGroupInfo(groupName, 0 /*flags*/); + if (summary) { + Resources res = getResources(pgi); + if (res != null) { + pw.print(loadText(pgi, pgi.labelRes, pgi.nonLocalizedLabel) + ": "); + } else { + pw.print(pgi.name + ": "); + + } + } else { + pw.println((labels ? "+ " : "") + "group:" + pgi.name); + if (labels) { + pw.println(" package:" + pgi.packageName); + Resources res = getResources(pgi); + if (res != null) { + pw.println(" label:" + + loadText(pgi, pgi.labelRes, pgi.nonLocalizedLabel)); + pw.println(" description:" + + loadText(pgi, pgi.descriptionRes, + pgi.nonLocalizedDescription)); + } + } + } + } else { + pw.println(((labels && !summary) ? "+ " : "") + "ungrouped:"); + } + prefix = " "; + } + List ps = mPermissionManager + .queryPermissionsByGroup(groupList.get(i), 0 /*flags*/); + final int count = (ps == null ? 0 : ps.size()); + boolean first = true; + for (int p = 0 ; p < count ; p++) { + PermissionInfo pi = ps.get(p); + if (groups && groupName == null && pi.group != null) { + continue; + } + final int base = pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; + if (base < startProtectionLevel + || base > endProtectionLevel) { + continue; + } + if (summary) { + if (first) { + first = false; + } else { + pw.print(", "); + } + Resources res = getResources(pi); + if (res != null) { + pw.print(loadText(pi, pi.labelRes, + pi.nonLocalizedLabel)); + } else { + pw.print(pi.name); + } + } else { + pw.println(prefix + (labels ? "+ " : "") + + "permission:" + pi.name); + if (labels) { + pw.println(prefix + " package:" + pi.packageName); + Resources res = getResources(pi); + if (res != null) { + pw.println(prefix + " label:" + + loadText(pi, pi.labelRes, + pi.nonLocalizedLabel)); + pw.println(prefix + " description:" + + loadText(pi, pi.descriptionRes, + pi.nonLocalizedDescription)); + } + pw.println(prefix + " protectionLevel:" + + PermissionInfo.protectionToString(pi.protectionLevel)); + } + } + } + + if (summary) { + pw.println(""); + } + } + } + + private String loadText(PackageItemInfo pii, int res, CharSequence nonLocalized) + throws RemoteException { + if (nonLocalized != null) { + return nonLocalized.toString(); + } + if (res != 0) { + Resources r = getResources(pii); + if (r != null) { + try { + return r.getString(res); + } catch (Resources.NotFoundException e) { + } + } + } + return null; + } + + private Resources getResources(PackageItemInfo pii) throws RemoteException { + Resources res = mResourceCache.get(pii.packageName); + if (res != null) return res; + + ApplicationInfo ai = mInterface.getApplicationInfo(pii.packageName, + PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS + | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, 0); + if (ai == null) { + Slog.e(TAG, "Failed to get ApplicationInfo for package name(" + pii.packageName + ")."); + return null; + } + AssetManager am = new AssetManager(); + am.addAssetPath(ai.publicSourceDir); + res = new Resources(am, null, null); + mResourceCache.put(pii.packageName, res); + return res; + } + + // Resolves the userId; supports UserHandle.USER_CURRENT, but not other special values + private @UserIdInt int resolveUserId(@UserIdInt int userId) { + return userId == UserHandle.USER_CURRENT ? ActivityManager.getCurrentUser() : userId; + } + + private int runClearPackagePreferredActivities() { + final PrintWriter pw = getErrPrintWriter(); + final String packageName = getNextArg(); + if (packageName == null) { + pw.println("Error: package name not specified"); + return 1; + } + try { + mContext.getPackageManager().clearPackagePreferredActivities(packageName); + return 0; + } catch (Exception e) { + pw.println(e.toString()); + return 1; + } + } + + private int runArchive() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + int flags = 0; + int userId = UserHandle.USER_ALL; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT) { + UserManagerInternal umi = + LocalServices.getService(UserManagerInternal.class); + UserInfo userInfo = umi.getUserInfo(userId); + if (userInfo == null) { + pw.println("Failure [user " + userId + " doesn't exist]"); + return 1; + } + } + } else { + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + + final String packageName = getNextArg(); + if (packageName == null) { + pw.println("Error: package name not specified"); + return 1; + } + + if (userId == UserHandle.USER_ALL) { + flags |= PackageManager.DELETE_ALL_USERS; + } + final int translatedUserId = + translateUserId(userId, UserHandle.USER_SYSTEM, "runArchive"); + final LocalIntentReceiver receiver = new LocalIntentReceiver(); + + try { + mInterface.getPackageInstaller().requestArchive(packageName, + /* callerPackageName= */ "", flags, receiver.getIntentSender(), + new UserHandle(translatedUserId)); + } catch (Exception e) { + pw.println("Failure [" + e.getMessage() + "]"); + return 1; + } + + final Intent result = receiver.getResult(); + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + if (status == PackageInstaller.STATUS_SUCCESS) { + pw.println("Success"); + return 0; + } else { + pw.println("Failure [" + + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]"); + return 1; + } + } + + private int runUnarchive() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + int userId = UserHandle.USER_ALL; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT) { + UserManagerInternal umi = + LocalServices.getService(UserManagerInternal.class); + UserInfo userInfo = umi.getUserInfo(userId); + if (userInfo == null) { + pw.println("Failure [user " + userId + " doesn't exist]"); + return 1; + } + } + } else { + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + + final String packageName = getNextArg(); + if (packageName == null) { + pw.println("Error: package name not specified"); + return 1; + } + + final int translatedUserId = + translateUserId(userId, UserHandle.USER_SYSTEM, "runArchive"); + final LocalIntentReceiver receiver = new LocalIntentReceiver(); + + try { + mInterface.getPackageInstaller().requestUnarchive(packageName, + mContext.getPackageName(), receiver.getIntentSender(), + new UserHandle(translatedUserId)); + } catch (Exception e) { + pw.println("Failure [" + e.getMessage() + "]"); + return 1; + } + + pw.println("Success"); + return 0; + } + + private int runGetDomainVerificationAgent() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + try { + final ComponentName domainVerificationAgent = mInterface.getDomainVerificationAgent(); + pw.println(domainVerificationAgent == null + ? "No Domain Verifier available!" : domainVerificationAgent.flattenToString()); + } catch (Exception e) { + pw.println("Failure [" + e.getMessage() + "]"); + return 1; + } + return 0; + } + + @Override + public void onHelp() { + final PrintWriter pw = getOutPrintWriter(); + pw.println("Package manager (package) commands:"); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(""); + pw.println(" path [--user USER_ID] PACKAGE"); + pw.println(" Print the path to the .apk of the given PACKAGE."); + pw.println(""); + pw.println(" dump PACKAGE"); + pw.println(" Print various system state associated with the given PACKAGE."); + pw.println(""); + pw.println(" dump-package PACKAGE"); + pw.println(" Print package manager state associated with the given PACKAGE."); + pw.println(""); + pw.println(" has-feature FEATURE_NAME [version]"); + pw.println(" Prints true and returns exit status 0 when system has a FEATURE_NAME,"); + pw.println(" otherwise prints false and returns exit status 1"); + pw.println(""); + pw.println(" list features"); + pw.println(" Prints all features of the system."); + pw.println(""); + pw.println(" list instrumentation [-f] [TARGET-PACKAGE]"); + pw.println(" Prints all test packages; optionally only those targeting TARGET-PACKAGE"); + pw.println(" Options:"); + pw.println(" -f: dump the name of the .apk file containing the test package"); + pw.println(""); + pw.println(" list libraries [-v]"); + pw.println(" Prints all system libraries."); + pw.println(" Options:"); + pw.println(" -v: shows the location of the library in the device's filesystem"); + pw.println(""); + pw.println(" list packages [-f] [-d] [-e] [-s] [-q] [-3] [-i] [-l] [-u] [-U] "); + pw.println(" [--show-versioncode] [--apex-only] [--factory-only]"); + pw.println(" [--uid UID] [--user USER_ID] [FILTER]"); + pw.println(" Prints all packages; optionally only those whose name contains"); + pw.println(" the text in FILTER. Options are:"); + pw.println(" -f: see their associated file"); + pw.println(" -a: all known packages (but excluding APEXes)"); + pw.println(" -d: filter to only show disabled packages"); + pw.println(" -e: filter to only show enabled packages"); + pw.println(" -s: filter to only show system packages"); + pw.println(" -q: filter to only show quarantined packages"); + pw.println(" -3: filter to only show third party packages"); + pw.println(" -i: see the installer for the packages"); + pw.println(" -l: ignored (used for compatibility with older releases)"); + pw.println(" -U: also show the package UID"); + pw.println(" -u: also include uninstalled packages"); + pw.println(" --show-versioncode: also show the version code"); + pw.println(" --apex-only: only show APEX packages"); + pw.println(" --factory-only: only show system packages excluding updates"); + pw.println(" --uid UID: filter to only show packages with the given UID"); + pw.println(" --user USER_ID: only list packages belonging to the given user"); + pw.println(" --match-libraries: include packages that declare static shared and SDK libraries"); + pw.println(""); + pw.println(" list permission-groups"); + pw.println(" Prints all known permission groups."); + pw.println(""); + pw.println(" list permissions [-g] [-f] [-d] [-u] [GROUP]"); + pw.println(" Prints all known permissions; optionally only those in GROUP. Options are:"); + pw.println(" -g: organize by group"); + pw.println(" -f: print all information"); + pw.println(" -s: short summary"); + pw.println(" -d: only list dangerous permissions"); + pw.println(" -u: list only the permissions users will see"); + pw.println(""); + pw.println(" list staged-sessions [--only-ready] [--only-sessionid] [--only-parent]"); + pw.println(" Prints all staged sessions."); + pw.println(" --only-ready: show only staged sessions that are ready"); + pw.println(" --only-sessionid: show only sessionId of each session"); + pw.println(" --only-parent: hide all children sessions"); + pw.println(""); + pw.println(" list users"); + pw.println(" Prints all users."); + pw.println(""); + pw.println(" resolve-activity [--brief] [--components] [--query-flags FLAGS]"); + pw.println(" [--user USER_ID] INTENT"); + pw.println(" Prints the activity that resolves to the given INTENT."); + pw.println(""); + pw.println(" query-activities [--brief] [--components] [--query-flags FLAGS]"); + pw.println(" [--user USER_ID] INTENT"); + pw.println(" Prints all activities that can handle the given INTENT."); + pw.println(""); + pw.println(" query-services [--brief] [--components] [--query-flags FLAGS]"); + pw.println(" [--user USER_ID] INTENT"); + pw.println(" Prints all services that can handle the given INTENT."); + pw.println(""); + pw.println(" query-receivers [--brief] [--components] [--query-flags FLAGS]"); + pw.println(" [--user USER_ID] INTENT"); + pw.println(" Prints all broadcast receivers that can handle the given INTENT."); + pw.println(""); + pw.println(" install [-rtfdg] [-i PACKAGE] [--user USER_ID|all|current]"); + pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]"); + pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]"); + pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]"); + pw.println(" [--preload] [--instant] [--full] [--dont-kill]"); + pw.println(" [--enable-rollback [0/1/2]]"); + pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]"); + pw.println(" [--apex] [--non-staged] [--force-non-staged]"); + pw.println(" [--staged-ready-timeout TIMEOUT] [--ignore-dexopt-profile]"); + pw.println(" [PATH [SPLIT...]|-]"); + pw.println(" Install an application. Must provide the apk data to install, either as"); + pw.println(" file path(s) or '-' to read from stdin. Options are:"); + pw.println(" -R: disallow replacement of existing application"); + pw.println(" -t: allow test packages"); + pw.println(" -i: specify package name of installer owning the app"); + pw.println(" -f: install application on internal flash"); + pw.println(" -d: allow version code downgrade (debuggable packages only)"); + pw.println(" -p: partial application install (new split on top of existing pkg)"); + pw.println(" -g: grant all runtime permissions"); + pw.println(" -S: size in bytes of package, required for stdin"); + pw.println(" --user: install under the given user."); + pw.println(" --dont-kill: installing a new feature split, don't kill running app"); + pw.println(" --restrict-permissions: don't whitelist restricted permissions at install"); + pw.println(" --originating-uri: set URI where app was downloaded from"); + pw.println(" --referrer: set URI that instigated the install of the app"); + pw.println(" --pkg: specify expected package name of app being installed"); + pw.println(" --abi: override the default ABI of the platform"); + pw.println(" --instant: cause the app to be installed as an ephemeral install app"); + pw.println(" --full: cause the app to be installed as a non-ephemeral full app"); + pw.println(" --enable-rollback: enable rollbacks for the upgrade."); + pw.println(" 0=restore (default), 1=wipe, 2=retain"); + pw.println(" --install-location: force the install location:"); + pw.println(" 0=auto, 1=internal only, 2=prefer external"); + pw.println(" --install-reason: indicates why the app is being installed:"); + pw.println(" 0=unknown, 1=admin policy, 2=device restore,"); + pw.println(" 3=device setup, 4=user request"); + pw.println(" --update-ownership: request the update ownership enforcement"); + pw.println(" --force-uuid: force install on to disk volume with given UUID"); + pw.println(" --apex: install an .apex file, not an .apk"); + pw.println(" --non-staged: explicitly set this installation to be non-staged."); + pw.println(" This flag is only useful for APEX installs that are implicitly"); + pw.println(" assumed to be staged."); + pw.println(" --force-non-staged: force the installation to run under a non-staged"); + pw.println(" session, which may complete without requiring a reboot. This will"); + pw.println(" force a rebootless update even for APEXes that don't support it"); + pw.println(" --staged-ready-timeout: By default, staged sessions wait " + + DEFAULT_STAGED_READY_TIMEOUT_MS); + pw.println(" milliseconds for pre-reboot verification to complete when"); + pw.println(" performing staged install. This flag is used to alter the waiting"); + pw.println(" time. You can skip the waiting time by specifying a TIMEOUT of '0'"); + pw.println(" --ignore-dexopt-profile: If set, all profiles are ignored by dexopt"); + pw.println(" during the installation, including the profile in the DM file and"); + pw.println(" the profile embedded in the APK file. If an invalid profile is"); + pw.println(" provided during installation, no warning will be reported by `adb"); + pw.println(" install`."); + pw.println(" This option does not affect later dexopt operations (e.g.,"); + pw.println(" background dexopt and manual `pm compile` invocations)."); + pw.println(""); + pw.println(" scan-fast [PATH]"); + pw.println(" Skip more steps and faster than fast-install."); + pw.println(""); + pw.println(" install-existing [--user USER_ID|all|current]"); + pw.println(" [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE"); + pw.println(" Installs an existing application for a new user. Options are:"); + pw.println(" --user: install for the given user."); + pw.println(" --instant: install as an instant app"); + pw.println(" --full: install as a full app"); + pw.println(" --wait: wait until the package is installed"); + pw.println(" --restrict-permissions: don't whitelist restricted permissions"); + pw.println(""); + pw.println(" install-create [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]"); + pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]"); + pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]"); + pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]"); + pw.println(" [--preload] [--instant] [--full] [--dont-kill]"); + pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [--apex] [-S BYTES]"); + pw.println(" [--multi-package] [--staged] [--update-ownership]"); + pw.println(" Like \"install\", but starts an install session. Use \"install-write\""); + pw.println(" to push data into the session, and \"install-commit\" to finish."); + pw.println(""); + pw.println(" install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH|-]"); + pw.println(" Write an apk into the given install session. If the path is '-', data"); + pw.println(" will be read from stdin. Options are:"); + pw.println(" -S: size in bytes of package, required for stdin"); + pw.println(""); + pw.println(" install-remove SESSION_ID SPLIT..."); + pw.println(" Mark SPLIT(s) as removed in the given install session."); + pw.println(""); + pw.println(" install-add-session MULTI_PACKAGE_SESSION_ID CHILD_SESSION_IDs"); + pw.println(" Add one or more session IDs to a multi-package session."); + pw.println(""); + pw.println(" install-set-pre-verified-domains SESSION_ID PRE_VERIFIED_DOMAIN... "); + pw.println(" Specify a comma separated list of pre-verified domains for a session."); + pw.println(""); + pw.println(" install-get-pre-verified-domains SESSION_ID"); + pw.println(" List all the pre-verified domains that are specified in a session."); + pw.println(" The result list is comma separated."); + pw.println(""); + pw.println(" install-commit SESSION_ID"); + pw.println(" Commit the given active install session, installing the app."); + pw.println(""); + pw.println(" install-abandon SESSION_ID"); + pw.println(" Delete the given active install session."); + pw.println(""); + pw.println(" set-install-location LOCATION"); + pw.println(" Changes the default install location. NOTE this is only intended for debugging;"); + pw.println(" using this can cause applications to break and other undersireable behavior."); + pw.println(" LOCATION is one of:"); + pw.println(" 0 [auto]: Let system decide the best location"); + pw.println(" 1 [internal]: Install on internal device storage"); + pw.println(" 2 [external]: Install on external media"); + pw.println(""); + pw.println(" get-install-location"); + pw.println(" Returns the current install location: 0, 1 or 2 as per set-install-location."); + pw.println(""); + pw.println(" move-package PACKAGE [internal|UUID]"); + pw.println(""); + pw.println(" move-primary-storage [internal|UUID]"); + pw.println(""); + pw.println(" uninstall [-k] [--user USER_ID] [--versionCode VERSION_CODE]"); + pw.println(" PACKAGE [SPLIT...]"); + pw.println(" Remove the given package name from the system. May remove an entire app"); + pw.println(" if no SPLIT names specified, otherwise will remove only the splits of the"); + pw.println(" given app. Options are:"); + pw.println(" -k: keep the data and cache directories around after package removal."); + pw.println(" --user: remove the app from the given user."); + pw.println(" --versionCode: only uninstall if the app has the given version code."); + pw.println(""); + pw.println(" clear [--user USER_ID] [--cache-only] PACKAGE"); + pw.println(" Deletes data associated with a package. Options are:"); + pw.println(" --user: specifies the user for which we need to clear data"); + pw.println(" --cache-only: a flag which tells if we only need to clear cache data"); + pw.println(""); + pw.println(" enable [--user USER_ID] PACKAGE_OR_COMPONENT"); + pw.println(" disable [--user USER_ID] PACKAGE_OR_COMPONENT"); + pw.println(" disable-user [--user USER_ID] PACKAGE_OR_COMPONENT"); + pw.println(" disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT"); + pw.println(" default-state [--user USER_ID] PACKAGE_OR_COMPONENT"); + pw.println(" These commands change the enabled state of a given package or"); + pw.println(" component (written as \"package/class\")."); + pw.println(""); + pw.println(" hide [--user USER_ID] PACKAGE_OR_COMPONENT"); + pw.println(" unhide [--user USER_ID] PACKAGE_OR_COMPONENT"); + pw.println(""); + pw.println(" unstop [--user USER_ID] PACKAGE"); + pw.println(""); + pw.println(" suspend [--user USER_ID] PACKAGE [PACKAGE...]"); + pw.println(" Suspends the specified package(s) (as user)."); + pw.println(""); + pw.println(" unsuspend [--user USER_ID] PACKAGE [PACKAGE...]"); + pw.println(" Unsuspends the specified package(s) (as user)."); + pw.println(""); + pw.println(" set-distracting-restriction [--user USER_ID] [--flag FLAG ...]"); + pw.println(" PACKAGE [PACKAGE...]"); + pw.println(" Sets the specified restriction flags to given package(s) (for user)."); + pw.println(" Flags are:"); + pw.println(" hide-notifications: Hides notifications from this package"); + pw.println(" hide-from-suggestions: Hides this package from suggestions"); + pw.println(" (by the launcher, etc.)"); + pw.println(" Any existing flags are overwritten, which also means that if no flags are"); + pw.println(" specified then all existing flags will be cleared."); + pw.println(""); + pw.println(" get-distracting-restriction [--user USER_ID] PACKAGE [PACKAGE...]"); + pw.println(" Gets the specified restriction flags of given package(s) (of the user)."); + pw.println(""); + pw.println(" grant [--user USER_ID] [--all-permissions] PACKAGE PERMISSION"); + pw.println(" revoke [--user USER_ID] [--all-permissions] PACKAGE PERMISSION"); + pw.println(" These commands either grant or revoke permissions to apps. The permissions"); + pw.println(" must be declared as used in the app's manifest, be runtime permissions"); + pw.println(" (protection level dangerous), and the app targeting SDK greater than Lollipop MR1."); + pw.println(" Flags are:"); + pw.println(" --user: Specifies the user for which the operation needs to be performed"); + pw.println(" --all-permissions: If specified all the missing runtime permissions will"); + pw.println(" be granted to the PACKAGE or to all the packages if none is specified."); + pw.println(""); + pw.println(" set-permission-flags [--user USER_ID] PACKAGE PERMISSION [FLAGS..]"); + pw.println(" clear-permission-flags [--user USER_ID] PACKAGE PERMISSION [FLAGS..]"); + pw.println(" These commands either set or clear permission flags on apps. The permissions"); + pw.println(" must be declared as used in the app's manifest, be runtime permissions"); + pw.println(" (protection level dangerous), and the app targeting SDK greater than Lollipop MR1."); + pw.println(" The flags must be one or more of " + SUPPORTED_PERMISSION_FLAGS_LIST); + pw.println(""); + pw.println(" reset-permissions"); + pw.println(" Revert all runtime permissions to their default state."); + pw.println(""); + pw.println(" set-permission-enforced PERMISSION [true|false]"); + pw.println(""); + pw.println(" get-privapp-permissions TARGET-PACKAGE"); + pw.println(" Prints all privileged permissions for a package."); + pw.println(""); + pw.println(" get-privapp-deny-permissions TARGET-PACKAGE"); + pw.println(" Prints all privileged permissions that are denied for a package."); + pw.println(""); + pw.println(" get-oem-permissions TARGET-PACKAGE"); + pw.println(" Prints all OEM permissions for a package."); + pw.println(""); + pw.println(" trim-caches DESIRED_FREE_SPACE [internal|UUID]"); + pw.println(" Trim cache files to reach the given free space."); + pw.println(""); + pw.println(" list users"); + pw.println(" Lists the current users."); + pw.println(""); + pw.println(" create-user [--profileOf USER_ID] [--managed] [--restricted] [--guest]"); + pw.println(" [--user-type USER_TYPE] [--ephemeral] [--for-testing] [--pre-create-only] USER_NAME"); + pw.println(" Create a new user with the given USER_NAME, printing the new user identifier"); + pw.println(" of the user."); + // TODO(b/142482943): Consider fetching the list of user types from UMS. + pw.println(" USER_TYPE is the name of a user type, e.g. android.os.usertype.profile.MANAGED."); + pw.println(" If not specified, the default user type is android.os.usertype.full.SECONDARY."); + pw.println(" --managed is shorthand for '--user-type android.os.usertype.profile.MANAGED'."); + pw.println(" --restricted is shorthand for '--user-type android.os.usertype.full.RESTRICTED'."); + pw.println(" --guest is shorthand for '--user-type android.os.usertype.full.GUEST'."); + pw.println(""); + pw.println(" remove-user [--set-ephemeral-if-in-use | --wait] USER_ID"); + pw.println(" Remove the user with the given USER_IDENTIFIER, deleting all data"); + pw.println(" associated with that user."); + pw.println(" --set-ephemeral-if-in-use: If the user is currently running and"); + pw.println(" therefore cannot be removed immediately, mark the user as ephemeral"); + pw.println(" so that it will be automatically removed when possible (after user"); + pw.println(" switch or reboot)"); + pw.println(" --wait: Wait until user is removed. Ignored if set-ephemeral-if-in-use"); + pw.println(""); + pw.println(" mark-guest-for-deletion USER_ID"); + pw.println(" Mark the guest user for deletion. After this, it is possible to create a"); + pw.println(" new guest user and switch to it. This allows resetting the guest user"); + pw.println(" without switching to another user."); + pw.println(""); + pw.println(" rename-user USER_ID [USER_NAME]"); + pw.println(" Rename USER_ID with USER_NAME (or null when [USER_NAME] is not set)"); + pw.println(""); + pw.println(" set-user-restriction [--user USER_ID] RESTRICTION VALUE"); + pw.println(""); + pw.println(" get-user-restriction [--user USER_ID] [--all] RESTRICTION_KEY"); + pw.println(" Display the value of restriction for the given restriction key if the"); + pw.println(" given user is valid."); + pw.println(" --all: display all restrictions for the given user"); + pw.println(" This option is used without restriction key"); + pw.println(""); + pw.println(" get-max-users"); + pw.println(""); + pw.println(" get-max-running-users"); + pw.println(""); + pw.println(" set-home-activity [--user USER_ID] TARGET-COMPONENT"); + pw.println(" Set the default home activity (aka launcher)."); + pw.println(" TARGET-COMPONENT can be a package name (com.package.my) or a full"); + pw.println(" component (com.package.my/component.name). However, only the package name"); + pw.println(" matters: the actual component used will be determined automatically from"); + pw.println(" the package."); + pw.println(""); + pw.println(" set-installer PACKAGE INSTALLER"); + pw.println(" Set installer package name"); + pw.println(""); + pw.println(" get-instantapp-resolver"); + pw.println( + " Return the name of the component that is the current instant app installer."); + pw.println(""); + pw.println(" set-harmful-app-warning [--user ] []"); + pw.println(" Mark the app as harmful with the given warning message."); + pw.println(""); + pw.println(" get-harmful-app-warning [--user ] "); + pw.println(" Return the harmful app warning message for the given app, if present"); + pw.println(); + pw.println(" uninstall-system-updates []"); + pw.println(" Removes updates to the given system application and falls back to its"); + pw.println(" /system version. Does nothing if the given package is not a system app."); + pw.println(" If no package is specified, removes updates to all system applications."); + pw.println(""); + pw.println(" get-moduleinfo [--all | --installed] [module-name]"); + pw.println(" Displays module info. If module-name is specified only that info is shown"); + pw.println(" By default, without any argument only installed modules are shown."); + pw.println(" --all: show all module info"); + pw.println(" --installed: show only installed modules"); + pw.println(""); + pw.println(" log-visibility [--enable|--disable] "); + pw.println(" Turns on debug logging when visibility is blocked for the given package."); + pw.println(" --enable: turn on debug logging (default)"); + pw.println(" --disable: turn off debug logging"); + pw.println(""); + pw.println(" set-silent-updates-policy [--allow-unlimited-silent-updates ]"); + pw.println(" [--throttle-time ] [--reset]"); + pw.println(" Sets the policies of the silent updates."); + pw.println(" --allow-unlimited-silent-updates: allows unlimited silent updated"); + pw.println(" installation requests from the installer without the throttle time."); + pw.println(" --throttle-time: update the silent updates throttle time in seconds."); + pw.println(" --reset: restore the installer and throttle time to the default, and"); + pw.println(" clear tracks of silent updates in the system."); + pw.println(""); + pw.println(" clear-package-preferred-activities "); + pw.println(" Remove the preferred activity mappings for the given package."); + pw.println(" wait-for-handler --timeout "); + pw.println(" Wait for a given amount of time till the package manager handler finishes"); + pw.println(" handling all pending messages."); + pw.println(" --timeout: wait for a given number of milliseconds. If the handler(s)"); + pw.println(" fail to finish before the timeout, the command returns error."); + pw.println(""); + pw.println(" wait-for-background-handler --timeout "); + pw.println(" Wait for a given amount of time till the package manager's background"); + pw.println(" handler finishes handling all pending messages."); + pw.println(" --timeout: wait for a given number of milliseconds. If the handler(s)"); + pw.println(" fail to finish before the timeout, the command returns error."); + pw.println(""); + pw.println(" archive [--user USER_ID] PACKAGE "); + pw.println(" During the archival process, the apps APKs and cache are removed from the"); + pw.println(" device while the user data is kept. Options are:"); + pw.println(" --user: archive the app from the given user."); + pw.println(""); + pw.println(" request-unarchive [--user USER_ID] PACKAGE "); + pw.println(" Requests to unarchive a currently archived package by sending a request"); + pw.println(" to unarchive an app to the responsible installer. Options are:"); + pw.println(" --user: request unarchival of the app from the given user."); + pw.println(""); + pw.println(" get-domain-verification-agent"); + pw.println(" Displays the component name of the domain verification agent on device."); + pw.println(""); + if (DexOptHelper.useArtService()) { + printArtServiceHelp(); + } else { + printLegacyDexoptHelp(); + } + pw.println(""); + mDomainVerificationShell.printHelp(pw); + pw.println(""); + Intent.printIntentArgsHelp(pw, ""); + } + + private void printArtServiceHelp() { + final var ipw = new IndentingPrintWriter(getOutPrintWriter(), " " /* singleIndent */); + ipw.increaseIndent(); + try { + LocalManagerRegistry.getManagerOrThrow(ArtManagerLocal.class) + .printShellCommandHelp(ipw); + } catch (ManagerNotFoundException e) { + ipw.println("ART Service is not ready. Please try again later"); + } + ipw.decreaseIndent(); + } + + private void printLegacyDexoptHelp() { + final PrintWriter pw = getOutPrintWriter(); + pw.println(" compile [-m MODE | -r REASON] [-f] [-c] [--split SPLIT_NAME]"); + pw.println(" [--reset] [--check-prof (true | false)] (-a | TARGET-PACKAGE)"); + pw.println(" Trigger compilation of TARGET-PACKAGE or all packages if \"-a\". Options are:"); + pw.println(" -a: compile all packages"); + pw.println(" -c: clear profile data before compiling"); + pw.println(" -f: force compilation even if not needed"); + pw.println(" -m: select compilation mode"); + pw.println(" MODE is one of the dex2oat compiler filters:"); + pw.println(" verify"); + pw.println(" speed-profile"); + pw.println(" speed"); + pw.println(" -r: select compilation reason"); + pw.println(" REASON is one of:"); + for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) { + pw.println(" " + PackageManagerServiceCompilerMapping.REASON_STRINGS[i]); + } + pw.println(" --reset: restore package to its post-install state"); + pw.println(" --check-prof (true | false): ignored - this is always true"); + pw.println(" --secondary-dex: compile app secondary dex files"); + pw.println(" --split SPLIT: compile only the given split name"); + pw.println(""); + pw.println(" force-dex-opt PACKAGE"); + pw.println(" Force immediate execution of dex opt for the given PACKAGE."); + pw.println(""); + pw.println(" delete-dexopt PACKAGE"); + pw.println(" Delete dex optimization results for the given PACKAGE."); + pw.println(""); + pw.println(" bg-dexopt-job [PACKAGE... | --cancel | --disable | --enable]"); + pw.println(" Controls the background job that optimizes dex files:"); + pw.println(" Without flags, run background optimization immediately on the given"); + pw.println(" PACKAGEs, or all packages if none is specified, and wait until the job"); + pw.println(" finishes. Note that the command only runs the background optimizer logic."); + pw.println(" It will run even if the device is not in the idle maintenance mode. If a"); + pw.println(" job is already running (including one started automatically by the"); + pw.println(" system) it will wait for it to finish before starting. A background job"); + pw.println(" will not be started automatically while one started this way is running."); + pw.println(" --cancel: Cancels any currently running background optimization job"); + pw.println(" immediately. This cancels jobs started either automatically by the"); + pw.println(" system or through this command. Note that cancelling a currently"); + pw.println(" running bg-dexopt-job command requires running this command from a"); + pw.println(" separate adb shell."); + pw.println(" --disable: Disables background jobs from being started by the job"); + pw.println(" scheduler. Does not affect bg-dexopt-job invocations from the shell."); + pw.println(" Does not imply --cancel. This state will be lost when the"); + pw.println(" system_server process exits."); + pw.println(" --enable: Enables background jobs to be started by the job scheduler"); + pw.println(" again, if previously disabled by --disable."); + pw.println(" cancel-bg-dexopt-job"); + pw.println(" Same as bg-dexopt-job --cancel."); + pw.println(""); + pw.println(" reconcile-secondary-dex-files TARGET-PACKAGE"); + pw.println(" Reconciles the package secondary dex files with the generated oat files."); + pw.println(""); + pw.println(" dump-profiles [--dump-classes-and-methods] TARGET-PACKAGE"); + pw.println(" Dumps method/class profile files to"); + pw.println(" " + ART_PROFILE_SNAPSHOT_DEBUG_LOCATION + + "TARGET-PACKAGE-primary.prof.txt."); + pw.println(" --dump-classes-and-methods: passed along to the profman binary to"); + pw.println(" switch to the format used by 'profman --create-profile-from'."); + pw.println(""); + pw.println(" snapshot-profile TARGET-PACKAGE [--code-path path]"); + pw.println(" Take a snapshot of the package profiles to"); + pw.println(" " + ART_PROFILE_SNAPSHOT_DEBUG_LOCATION + + "TARGET-PACKAGE[-code-path].prof"); + pw.println(" If TARGET-PACKAGE=android it will take a snapshot of the boot image"); + } + + private static class LocalIntentReceiver { + private final LinkedBlockingQueue mResult = new LinkedBlockingQueue<>(); + + private final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { + @Override + public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, + IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { + try { + mResult.offer(intent, 5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + }; + + public IntentSender getIntentSender() { + return new IntentSender((IIntentSender) mLocalSender); + } + + public Intent getResult() { + try { + return mResult.take(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/aosp/frameworks/native/cmds/installd/InstalldNativeService.cpp b/aosp/frameworks/native/cmds/installd/InstalldNativeService.cpp index 7cbd9af0545670fc60934ed594745ccd296d77b2..ea9aaab3e0f7effa8e4f76aaf5d00d544063e142 100644 --- a/aosp/frameworks/native/cmds/installd/InstalldNativeService.cpp +++ b/aosp/frameworks/native/cmds/installd/InstalldNativeService.cpp @@ -123,6 +123,8 @@ static constexpr const char* kMntFuse = "/mnt/pass_through/0/"; static std::atomic sAppDataIsolationEnabled(false); +const int DELETE_UPDATE_SHARE_APP = 0x00000010; + /** * Flag to control if project ids are supported for internal storage */ @@ -761,6 +763,53 @@ static void chown_app_profile_dir(const std::string &packageName, int32_t appId, } } +void fix_app_path(const char *name, uid_t uid, gid_t gid) { + struct stat buf; + char path[PATH_MAX]; + memset(&buf, 0, sizeof(buf)); + + // Obtain file information by file name and save it in the structure stat pointed to by buf. + int ret = lstat(name, &buf); + if (ret < 0) { + LOG(WARNING) << "Obtain file:" << name << " information failed " << std::strerror(errno); + return; + } + + if ((buf.st_mode & S_IFMT) == S_IFLNK) { + // 原数据中软链接属主不是系统角色时修复新数据的属主 + if (buf.st_uid >= 10000) { + lchown(name, uid, gid); + } + + // 如果软链接的目标目录不存在则删除 + ret = stat(name, &buf); + if (ret < 0 && errno == ENOENT && unlink(name) < 0) { + LOG(WARNING) << "Unlink file:" << name << " failed. " << std::strerror(errno); + } + } else if ((buf.st_mode & S_IFMT) == S_IFREG) { + chown(name, uid, gid); + } else if ((buf.st_mode & S_IFMT) == S_IFDIR) { + chown(name, uid, gid); + struct dirent *de; + DIR *dir = opendir(name); + if (dir == NULL) { + return; + } + while ((de = readdir(dir))) { + if (strcmp(".", de->d_name) == 0 || + strcmp("..", de->d_name) ==0) { + continue; + } + memset(path, 0, sizeof(path)); + strlcpy(path, name, sizeof(path)); + strlcat(path, "/", sizeof(path)); + strlcat(path, de->d_name, sizeof(path)); + fix_app_path(path, uid, gid); + } + closedir(dir); + } +} + static binder::Status createAppDataDirs(const std::string& path, int32_t uid, int32_t gid, int32_t previousUid, int32_t cacheGid, const std::string& seInfo, mode_t targetMode, @@ -786,7 +835,8 @@ static binder::Status createAppDataDirs(const std::string& path, int32_t uid, in if (prepare_app_dir(path, targetMode, uid, gid, projectIdApp) || prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid, projectIdCache) || prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid, projectIdCache)) { - return error("Failed to prepare " + path); + fix_app_path(path.c_str(), uid, uid); + LOG(WARNING) << "Attemp to fix app data " << path.c_str(); } // Consider restorecon over contents if label changed @@ -3934,6 +3984,41 @@ binder::Status InstalldNativeService::migrateLegacyObbData() { return ok(); } +int InstalldNativeService::exe_system_cmd(const char *cmd_path, std::vector execve_str) { + int status = 0, ret = 0; + pid_t pid = fork(); + if (pid == 0) { + char *env[] = {nullptr}; + if (execve(cmd_path, const_cast(execve_str.data()), env) < 0) { + exit(1); + } + } else { + wait(&status); + ret = WEXITSTATUS(status); + } + if (ret == 1) { + return -1; + } + return 0; +} + +binder::Status InstalldNativeService::cleanAppLink(const std::string& packageName, int32_t deleteFlags) { + ENFORCE_UID(AID_SYSTEM); + std::vector execve_str = {nullptr}; + if (deleteFlags & DELETE_UPDATE_SHARE_APP) { + execve_str = {"appctrl", "clean", "-update", packageName.c_str(), nullptr}; + } else { + execve_str = {"appctrl", "clean", packageName.c_str(), nullptr}; + } + LOG(INFO) << "clean app link deleteFlags " << deleteFlags; + + const char *appctrl_path = "/system/bin/appctrl"; + if (exe_system_cmd(appctrl_path, execve_str) == -1) { + LOG(WARNING) << "appctrl clean fail package:" << packageName.c_str(); + } + return ok(); +} + binder::Status InstalldNativeService::cleanupInvalidPackageDirs( const std::optional& uuid, int32_t userId, int32_t flags) { ENFORCE_VALID_USER(userId); diff --git a/aosp/frameworks/native/cmds/installd/InstalldNativeService.h b/aosp/frameworks/native/cmds/installd/InstalldNativeService.h new file mode 100644 index 0000000000000000000000000000000000000000..5f4c58ea828236a99e7f5db38c300bff7c96f454 --- /dev/null +++ b/aosp/frameworks/native/cmds/installd/InstalldNativeService.h @@ -0,0 +1,270 @@ +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef COMMANDS_H_ +#define COMMANDS_H_ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "android/os/BnInstalld.h" +#include "installd_constants.h" + +namespace android { +namespace installd { + +using IFsveritySetupAuthToken = android::os::IInstalld::IFsveritySetupAuthToken; + +class InstalldNativeService : public BinderService, public os::BnInstalld { +public: + class FsveritySetupAuthToken : public os::IInstalld::BnFsveritySetupAuthToken { + public: + FsveritySetupAuthToken() : mStatFromAuthFd() {} + + binder::Status authenticate(const android::os::ParcelFileDescriptor& authFd, int32_t uid); + bool isSameStat(const struct stat& st) const; + + private: + // Not copyable or movable + FsveritySetupAuthToken(const FsveritySetupAuthToken&) = delete; + FsveritySetupAuthToken& operator=(const FsveritySetupAuthToken&) = delete; + + struct stat mStatFromAuthFd; + }; + + static status_t start(); + static char const* getServiceName() { return "installd"; } + virtual status_t dump(int fd, const Vector &args) override; + + binder::Status createUserData(const std::optional& uuid, int32_t userId, + int32_t userSerial, int32_t flags); + binder::Status destroyUserData(const std::optional& uuid, int32_t userId, + int32_t flags); + + binder::Status createAppData(const std::optional& uuid, + const std::string& packageName, int32_t userId, int32_t flags, + int32_t appId, int32_t previousAppId, const std::string& seInfo, + int32_t targetSdkVersion, int64_t* ceDataInode, + int64_t* deDataInode); + + binder::Status createAppData( + const android::os::CreateAppDataArgs& args, + android::os::CreateAppDataResult* _aidl_return); + binder::Status createAppDataBatched( + const std::vector& args, + std::vector* _aidl_return); + + binder::Status reconcileSdkData(const android::os::ReconcileSdkDataArgs& args); + + binder::Status restoreconAppData(const std::optional& uuid, + const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, + const std::string& seInfo); + + binder::Status migrateAppData(const std::optional& uuid, + const std::string& packageName, int32_t userId, int32_t flags); + binder::Status clearAppData(const std::optional& uuid, + const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode); + binder::Status destroyAppData(const std::optional& uuid, + const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode); + + binder::Status fixupAppData(const std::optional& uuid, int32_t flags); + + binder::Status snapshotAppData(const std::optional& volumeUuid, + const std::string& packageName, const int32_t user, const int32_t snapshotId, + int32_t storageFlags, int64_t* _aidl_return); + binder::Status restoreAppDataSnapshot(const std::optional& volumeUuid, + const std::string& packageName, const int32_t appId, const std::string& seInfo, + const int32_t user, const int32_t snapshotId, int32_t storageFlags); + binder::Status destroyAppDataSnapshot(const std::optional &volumeUuid, + const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode, + const int32_t snapshotId, int32_t storageFlags); + binder::Status destroyCeSnapshotsNotSpecified(const std::optional &volumeUuid, + const int32_t user, const std::vector& retainSnapshotIds); + + binder::Status getAppSize(const std::optional& uuid, + const std::vector& packageNames, int32_t userId, int32_t flags, + int32_t appId, const std::vector& ceDataInodes, + const std::vector& codePaths, std::vector* _aidl_return); + binder::Status getUserSize(const std::optional& uuid, + int32_t userId, int32_t flags, const std::vector& appIds, + std::vector* _aidl_return); + binder::Status getExternalSize(const std::optional& uuid, + int32_t userId, int32_t flags, const std::vector& appIds, + std::vector* _aidl_return); + + binder::Status getAppCrates(const std::optional& uuid, + const std::vector& packageNames, + int32_t userId, + std::optional>>* + _aidl_return); + binder::Status getUserCrates( + const std::optional& uuid, int32_t userId, + std::optional>>* + _aidl_return); + + binder::Status setAppQuota(const std::optional& uuid, + int32_t userId, int32_t appId, int64_t cacheQuota); + + binder::Status moveCompleteApp(const std::optional& fromUuid, + const std::optional& toUuid, const std::string& packageName, + int32_t appId, const std::string& seInfo, + int32_t targetSdkVersion, const std::string& fromCodePath); + + binder::Status dexopt(const std::string& apkPath, int32_t uid, const std::string& packageName, + const std::string& instructionSet, int32_t dexoptNeeded, + const std::optional& outputPath, int32_t dexFlags, + const std::string& compilerFilter, const std::optional& uuid, + const std::optional& classLoaderContext, + const std::optional& seInfo, bool downgrade, + int32_t targetSdkVersion, const std::optional& profileName, + const std::optional& dexMetadataPath, + const std::optional& compilationReason, bool* aidl_return); + + binder::Status controlDexOptBlocking(bool block); + + binder::Status rmdex(const std::string& codePath, const std::string& instructionSet); + + binder::Status mergeProfiles(int32_t uid, const std::string& packageName, + const std::string& profileName, int* _aidl_return); + binder::Status dumpProfiles(int32_t uid, const std::string& packageName, + const std::string& profileName, const std::string& codePath, + bool dumpClassesAndMethods, bool* _aidl_return); + binder::Status copySystemProfile(const std::string& systemProfile, + int32_t uid, const std::string& packageName, const std::string& profileName, + bool* _aidl_return); + binder::Status clearAppProfiles(const std::string& packageName, const std::string& profileName); + binder::Status destroyAppProfiles(const std::string& packageName); + binder::Status deleteReferenceProfile(const std::string& packageName, + const std::string& profileName); + + binder::Status createProfileSnapshot(int32_t appId, const std::string& packageName, + const std::string& profileName, const std::string& classpath, bool* _aidl_return); + binder::Status destroyProfileSnapshot(const std::string& packageName, + const std::string& profileName); + + binder::Status rmPackageDir(const std::string& packageName, const std::string& packageDir); + binder::Status freeCache(const std::optional& uuid, int64_t targetFreeBytes, + int32_t flags); + binder::Status linkNativeLibraryDirectory(const std::optional& uuid, + const std::string& packageName, const std::string& nativeLibPath32, int32_t userId); + binder::Status createOatDir(const std::string& packageName, const std::string& oatDir, + const std::string& instructionSet); + binder::Status linkFile(const std::string& packageName, const std::string& relativePath, + const std::string& fromBase, const std::string& toBase); + binder::Status moveAb(const std::string& packageName, const std::string& apkPath, + const std::string& instructionSet, const std::string& outputPath); + binder::Status deleteOdex(const std::string& packageName, const std::string& apkPath, + const std::string& instructionSet, + const std::optional& outputPath, int64_t* _aidl_return); + binder::Status reconcileSecondaryDexFile(const std::string& dexPath, + const std::string& packageName, int32_t uid, const std::vector& isa, + const std::optional& volumeUuid, int32_t storage_flag, bool* _aidl_return); + binder::Status hashSecondaryDexFile(const std::string& dexPath, + const std::string& packageName, int32_t uid, const std::optional& volumeUuid, + int32_t storageFlag, std::vector* _aidl_return); + + binder::Status invalidateMounts(); + binder::Status setFirstBoot(); + binder::Status isQuotaSupported(const std::optional& volumeUuid, + bool* _aidl_return); + binder::Status tryMountDataMirror(const std::optional& volumeUuid); + binder::Status onPrivateVolumeRemoved(const std::optional& volumeUuid); + + binder::Status prepareAppProfile(const std::string& packageName, + int32_t userId, int32_t appId, const std::string& profileName, + const std::string& codePath, const std::optional& dexMetadata, + bool* _aidl_return); + + binder::Status migrateLegacyObbData(); + + binder::Status cleanAppLink(const std::string& packageName, int32_t deleteFlags); + + binder::Status cleanupInvalidPackageDirs(const std::optional& uuid, int32_t userId, + int32_t flags); + + binder::Status getOdexVisibility(const std::string& packageName, const std::string& apkPath, + const std::string& instructionSet, + const std::optional& outputPath, + int32_t* _aidl_return); + + binder::Status createFsveritySetupAuthToken(const android::os::ParcelFileDescriptor& authFd, + int32_t uid, + android::sp* _aidl_return); + binder::Status enableFsverity(const android::sp& authToken, + const std::string& filePath, const std::string& packageName, + int32_t* _aidl_return); + +private: + std::recursive_mutex mLock; + std::unordered_map> mUserIdLock; + std::unordered_map> mPackageNameLock; + + std::recursive_mutex mMountsLock; + std::recursive_mutex mQuotasLock; + + /* Map of all storage mounts from source to target */ + std::unordered_map mStorageMounts; + + /* Map from UID to cache quota size */ + std::unordered_map mCacheQuotas; + + std::string findDataMediaPath(const std::optional& uuid, userid_t userid); + + int exe_system_cmd(const char *cmd_path, std::vector execve_str); + + binder::Status createAppDataLocked(const std::optional& uuid, + const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, int32_t previousAppId, + const std::string& seInfo, int32_t targetSdkVersion, + int64_t* ceDataInode, int64_t* deDataInode); + binder::Status restoreconAppDataLocked(const std::optional& uuid, + const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, const std::string& seInfo); + + binder::Status createSdkSandboxDataPackageDirectory(const std::optional& uuid, + const std::string& packageName, + int32_t userId, int32_t appId, + int32_t flags); + binder::Status clearSdkSandboxDataPackageDirectory(const std::optional& uuid, + const std::string& packageName, + int32_t userId, int32_t flags); + binder::Status destroySdkSandboxDataPackageDirectory(const std::optional& uuid, + const std::string& packageName, + int32_t userId, int32_t flags); + binder::Status reconcileSdkData(const std::optional& uuid, + const std::string& packageName, + const std::vector& subDirNames, int32_t userId, + int32_t appId, int32_t previousAppId, const std::string& seInfo, + int flags); + binder::Status restoreconSdkDataLocked(const std::optional& uuid, + const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, const std::string& seInfo); +}; + +} // namespace installd +} // namespace android + +#endif // COMMANDS_H_ diff --git a/aosp/frameworks/native/cmds/installd/binder/android/os/IInstalld.aidl b/aosp/frameworks/native/cmds/installd/binder/android/os/IInstalld.aidl new file mode 100644 index 0000000000000000000000000000000000000000..67008dd6be2b81d0413428a16adcad41905eb599 --- /dev/null +++ b/aosp/frameworks/native/cmds/installd/binder/android/os/IInstalld.aidl @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** {@hide} */ +interface IInstalld { + void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags); + void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags); + void setFirstBoot(); + android.os.CreateAppDataResult createAppData(in android.os.CreateAppDataArgs args); + android.os.CreateAppDataResult[] createAppDataBatched(in android.os.CreateAppDataArgs[] args); + + void reconcileSdkData(in android.os.ReconcileSdkDataArgs args); + + void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, + int userId, int flags, int appId, @utf8InCpp String seInfo); + void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, + int userId, int flags); + void clearAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, + int userId, int flags, long ceDataInode); + void destroyAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, + int userId, int flags, long ceDataInode); + + void fixupAppData(@nullable @utf8InCpp String uuid, int flags); + + long[] getAppSize(@nullable @utf8InCpp String uuid, in @utf8InCpp String[] packageNames, + int userId, int flags, int appId, in long[] ceDataInodes, + in @utf8InCpp String[] codePaths); + long[] getUserSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds); + long[] getExternalSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds); + + @nullable + android.os.storage.CrateMetadata[] getAppCrates( + @nullable @utf8InCpp String uuid, in @utf8InCpp String[] packageNames, + int userId); + @nullable + android.os.storage.CrateMetadata[] getUserCrates( + @nullable @utf8InCpp String uuid, int userId); + + void setAppQuota(@nullable @utf8InCpp String uuid, int userId, int appId, long cacheQuota); + + void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid, + @utf8InCpp String packageName, int appId, + @utf8InCpp String seInfo, int targetSdkVersion, @utf8InCpp String fromCodePath); + + // Returns false if it is cancelled. Returns true if it is completed or have other errors. + boolean dexopt(@utf8InCpp String apkPath, int uid, @utf8InCpp String packageName, + @utf8InCpp String instructionSet, int dexoptNeeded, + @nullable @utf8InCpp String outputPath, int dexFlags, + @utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid, + @nullable @utf8InCpp String sharedLibraries, + @nullable @utf8InCpp String seInfo, boolean downgrade, int targetSdkVersion, + @nullable @utf8InCpp String profileName, + @nullable @utf8InCpp String dexMetadataPath, + @nullable @utf8InCpp String compilationReason); + // Blocks (when block is true) or unblock (when block is false) dexopt. + // Blocking also invloves cancelling the currently running dexopt. + void controlDexOptBlocking(boolean block); + + void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet); + + int mergeProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String profileName); + boolean dumpProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String profileName, + @utf8InCpp String codePath, boolean dumpClassesAndMethods); + boolean copySystemProfile(@utf8InCpp String systemProfile, int uid, + @utf8InCpp String packageName, @utf8InCpp String profileName); + void clearAppProfiles(@utf8InCpp String packageName, @utf8InCpp String profileName); + void destroyAppProfiles(@utf8InCpp String packageName); + void deleteReferenceProfile(@utf8InCpp String packageName, @utf8InCpp String profileName); + + boolean createProfileSnapshot(int appId, @utf8InCpp String packageName, + @utf8InCpp String profileName, @utf8InCpp String classpath); + void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName); + + void rmPackageDir(@utf8InCpp String packageName, @utf8InCpp String packageDir); + void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes, int flags); + void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid, + @utf8InCpp String packageName, @utf8InCpp String nativeLibPath32, int userId); + void createOatDir(@utf8InCpp String packageName, @utf8InCpp String oatDir, + @utf8InCpp String instructionSet); + void linkFile(@utf8InCpp String packageName, @utf8InCpp String relativePath, + @utf8InCpp String fromBase, @utf8InCpp String toBase); + void moveAb(@utf8InCpp String packageName, @utf8InCpp String apkPath, + @utf8InCpp String instructionSet, @utf8InCpp String outputPath); + long deleteOdex(@utf8InCpp String packageName, @utf8InCpp String apkPath, + @utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath); + + boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName, + int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid, + int storage_flag); + + byte[] hashSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName, + int uid, @nullable @utf8InCpp String volumeUuid, int storageFlag); + + void invalidateMounts(); + boolean isQuotaSupported(@nullable @utf8InCpp String uuid); + + boolean prepareAppProfile(@utf8InCpp String packageName, + int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath, + @nullable @utf8InCpp String dexMetadata); + + long snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, + int userId, int snapshotId, int storageFlags); + void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, + int appId, @utf8InCpp String seInfo, int user, int snapshotId, int storageflags); + void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, + int userId, long ceSnapshotInode, int snapshotId, int storageFlags); + void destroyCeSnapshotsNotSpecified(@nullable @utf8InCpp String uuid, int userId, + in int[] retainSnapshotIds); + + void tryMountDataMirror(@nullable @utf8InCpp String volumeUuid); + void onPrivateVolumeRemoved(@nullable @utf8InCpp String volumeUuid); + + void migrateLegacyObbData(); + + void cleanupInvalidPackageDirs(@nullable @utf8InCpp String uuid, int userId, int flags); + + int getOdexVisibility(@utf8InCpp String packageName, @utf8InCpp String apkPath, + @utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath); + + void cleanAppLink(@utf8InCpp String packageName, int deleteFlags); + + interface IFsveritySetupAuthToken { + // Using an interface here is an easy way to create and maintain an IBinder object across + // the processes. When installd creates this binder object, it stores the file stat + // privately for later authentication, and only returns the reference to the caller process. + // Once the binder object has no reference count, it gets destructed automatically + // (alternatively, installd can maintain an internal mapping, but it is more error prone + // because the app may crash and not finish the fs-verity setup, keeping the memory unused + // forever). + // + // We don't necessarily need a method here, so it's left blank intentionally. + } + IFsveritySetupAuthToken createFsveritySetupAuthToken(in ParcelFileDescriptor authFd, int uid); + int enableFsverity(in IFsveritySetupAuthToken authToken, @utf8InCpp String filePath, + @utf8InCpp String packageName); + + const int FLAG_STORAGE_DE = 0x1; + const int FLAG_STORAGE_CE = 0x2; + const int FLAG_STORAGE_EXTERNAL = 0x4; + const int FLAG_STORAGE_SDK = 0x8; + + const int FLAG_CLEAR_CACHE_ONLY = 0x10; + const int FLAG_CLEAR_CODE_CACHE_ONLY = 0x20; + + const int FLAG_FREE_CACHE_V2 = 0x100; + const int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 0x200; + const int FLAG_FREE_CACHE_NOOP = 0x400; + // Set below flag to clear cache irrespective of target free bytes required + const int FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES = 0x800; + + const int FLAG_USE_QUOTA = 0x1000; + const int FLAG_FORCE = 0x2000; + + const int FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES = 0x20000; +} diff --git a/aosp/frameworks/native/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/aosp/frameworks/native/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl new file mode 100644 index 0000000000000000000000000000000000000000..365374c68dee80cb909a5dd8b7734e3055f1160c --- /dev/null +++ b/aosp/frameworks/native/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl @@ -0,0 +1,140 @@ +/* +** +** Copyright 2017, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.content.pm; + +import android.content.pm.IStagedApexObserver; +import android.content.pm.StagedApexInfo; + +/** + * Parallel implementation of certain {@link PackageManager} APIs that need to + * be exposed to native code. + *

These APIs are a parallel definition to the APIs in PackageManager, so, + * they can technically diverge. However, it's good practice to keep these + * APIs in sync with each other. + *

Because these APIs are exposed to native code, it's possible they will + * be exposed to privileged components [such as UID 0]. Care should be taken + * to avoid exposing potential security holes for methods where permission + * checks are bypassed based upon UID alone. + * + * @hide + */ +interface IPackageManagerNative { + /** + * Returns a set of names for the given UIDs. + * IMPORTANT: Unlike the Java version of this API, unknown UIDs are + * not represented by 'null's. Instead, they are represented by empty + * strings. + */ + @utf8InCpp String[] getNamesForUids(in int[] uids); + + /** + * Returns the name of the installer (a package) which installed the named + * package. Preloaded packages return the string "preload". Sideloaded packages + * return an empty string. Unknown or unknowable are returned as empty strings. + */ + + @utf8InCpp String getInstallerForPackage(in String packageName); + + /** + * Returns the version code of the named package. + * Unknown or unknowable versions are returned as 0. + */ + + long getVersionCodeForPackage(in String packageName); + + /** + * Return if each app, identified by its package name allows its audio to be recorded. + * Unknown packages are mapped to false. + */ + boolean[] isAudioPlaybackCaptureAllowed(in @utf8InCpp String[] packageNames); + + /* ApplicationInfo.isSystemApp() == true */ + const int LOCATION_SYSTEM = 0x1; + /* ApplicationInfo.isVendor() == true */ + const int LOCATION_VENDOR = 0x2; + /* ApplicationInfo.isProduct() == true */ + const int LOCATION_PRODUCT = 0x4; + + /** + * Returns a set of bitflags about package location. + * LOCATION_SYSTEM: getApplicationInfo(packageName).isSystemApp() + * LOCATION_VENDOR: getApplicationInfo(packageName).isVendor() + * LOCATION_PRODUCT: getApplicationInfo(packageName).isProduct() + */ + int getLocationFlags(in @utf8InCpp String packageName); + + /** + * Returns the target SDK version for the given package. + * Unknown packages will cause the call to fail. The caller must check the + * returned Status before using the result of this function. + */ + int getTargetSdkVersionForPackage(in String packageName); + + /** + * Returns the name of module metadata package, or empty string if device doesn't have such + * package. + */ + @utf8InCpp String getModuleMetadataPackageName(); + + /** + * Returns true if the package has the SHA 256 version of the signing certificate. + * @see PackageManager#hasSigningCertificate(String, byte[], int), where type + * has been set to {@link PackageManager#CERT_INPUT_SHA256}. + */ + boolean hasSha256SigningCertificate(in @utf8InCpp String packageName, in byte[] certificate); + + /** + * Returns the debug flag for the given package. + * Unknown packages will cause the call to fail. + */ + boolean isPackageDebuggable(in String packageName); + + /** + * Check whether the given feature name and version is one of the available + * features as returned by {@link PackageManager#getSystemAvailableFeatures()}. Since + * features are defined to always be backwards compatible, this returns true + * if the available feature version is greater than or equal to the + * requested version. + */ + boolean hasSystemFeature(in String featureName, in int version); + + /** Register a observer for change in set of staged APEX ready for installation */ + void registerStagedApexObserver(in IStagedApexObserver observer); + + /** + * Unregister an existing staged apex observer. + * This does nothing if this observer was not already registered. + */ + void unregisterStagedApexObserver(in IStagedApexObserver observer); + + /** + * Get APEX module names of all APEX that are staged ready for installation + */ + @utf8InCpp String[] getStagedApexModuleNames(); + + /** + * Get information of APEX which is staged ready for installation. + * Returns null if no such APEX is found. + */ + @nullable StagedApexInfo getStagedApexInfo(in @utf8InCpp String moduleName); + + /* + * Returns the uid of given package and userId. + */ + int getPackageUid(in @utf8InCpp String packageName, in int userId); +} diff --git a/aosp/packages/modules/adb/daemon/transport_local.cpp b/aosp/packages/modules/adb/daemon/transport_local.cpp index a06be3381c709a811f1988332fe8a0d9cd41c4ce..09236df57348aef97330a9e203edbe98fe6a5fd1 100644 --- a/aosp/packages/modules/adb/daemon/transport_local.cpp +++ b/aosp/packages/modules/adb/daemon/transport_local.cpp @@ -104,4 +104,3 @@ int init_socket_transport(atransport* t, unique_fd fd, int adb_port, int local) t->SetConnection(std::make_unique(std::move(fd_connection))); return 0; } - diff --git a/aosp/packages/modules/adb/socket_spec.cpp b/aosp/packages/modules/adb/socket_spec.cpp index 743878eee31e40916740c8495e9b4f24cd5a60ec..ab0d3cde57166494024e22386fb2901cdd1955a6 100644 --- a/aosp/packages/modules/adb/socket_spec.cpp +++ b/aosp/packages/modules/adb/socket_spec.cpp @@ -531,4 +531,3 @@ int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_ *error += spec; return -1; } - diff --git a/aosp/packages/modules/adb/socket_spec.h b/aosp/packages/modules/adb/socket_spec.h index 2a3cf0241b0b9ab690cbccc8f59e27e9a668bccb..6af9e8d508e0e7767ab175cc2bf5bff803693d19 100644 --- a/aosp/packages/modules/adb/socket_spec.h +++ b/aosp/packages/modules/adb/socket_spec.h @@ -39,4 +39,3 @@ bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* po std::string* serial, std::string* error); int get_host_socket_spec_port(std::string_view spec, std::string* error); - diff --git a/aosp/packages/modules/adb/transport.cpp b/aosp/packages/modules/adb/transport.cpp index f3e51bf6ac803c448718c059ad4644d5d7ba5b0d..bb0ef94d7b4b1e01cdfa3f2cfe75705994893b63 100644 --- a/aosp/packages/modules/adb/transport.cpp +++ b/aosp/packages/modules/adb/transport.cpp @@ -1717,4 +1717,3 @@ void atransport::ResetKeys() { keys_.clear(); } #endif - diff --git a/aosp/packages/modules/adb/transport.h b/aosp/packages/modules/adb/transport.h index c56c6b1f75039acc5343209edcee680ed1d3a8ab..8ca3239eb0e3ea322e1012d2a9c90fe91ffd5856 100644 --- a/aosp/packages/modules/adb/transport.h +++ b/aosp/packages/modules/adb/transport.h @@ -545,4 +545,3 @@ void server_socket_thread(std::function -#include - -#include -#include -#include - -#include - -using android::base::GetProperty; -using android::base::SetProperty; -using namespace std::literals; - -static void ControlService(bool start, const std::string& service) { - if (!android::base::SetProperty(start ? "ctl.start" : "ctl.stop", service)) { - std::cerr << "Unable to " << (start ? "start" : "stop") << " service '" << service - << "'\nSee dmesg for error reason." << std::endl; - exit(EXIT_FAILURE); - } -} - -static void ControlDefaultServices(bool start) { - std::vector services = { - "netd", - "surfaceflinger", - "audioserver", - "zygote", - }; - - // Only start zygote_secondary if not single arch. - std::string zygote_configuration = GetProperty("ro.zygote", ""); - if (zygote_configuration != "zygote32" && zygote_configuration != "zygote64") { - #if 0// FIXME - services.emplace_back("zygote_secondary"); - #endif - } - - if (start) { - for (const auto& service : services) { - ControlService(true, service); - } - } else { - for (auto it = services.crbegin(); it != services.crend(); ++it) { - ControlService(false, *it); - } - } -} - -static int StartStop(int argc, char** argv, bool start) { - if (getuid()) { - std::cerr << "Must be root" << std::endl; - return EXIT_FAILURE; - } - - if (argc == 1) { - ControlDefaultServices(start); - } - - if (argc == 2 && argv[1] == "--help"s) { - std::cout << "usage: " << (start ? "start" : "stop") - << " [SERVICE...]\n" - "\n" - << (start ? "Starts" : "Stops") - << " the given system service, or netd/surfaceflinger/zygotes." << std::endl; - return EXIT_SUCCESS; - } - - for (int i = 1; i < argc; ++i) { - ControlService(start, argv[i]); - } - return EXIT_SUCCESS; -} - -extern "C" int start_main(int argc, char** argv) { - return StartStop(argc, argv, true); -} - -extern "C" int stop_main(int argc, char** argv) { - return StartStop(argc, argv, false); -} diff --git a/aosp/system/hardware/interfaces/suspend/1.0/default/android.system.suspend-service.rc b/aosp/system/hardware/interfaces/suspend/1.0/default/android.system.suspend-service.rc new file mode 100644 index 0000000000000000000000000000000000000000..f0065f0a6b9cda73b61e52c04b4c77374f4df2d9 --- /dev/null +++ b/aosp/system/hardware/interfaces/suspend/1.0/default/android.system.suspend-service.rc @@ -0,0 +1,4 @@ +service system_suspend /system/bin/hw/android.system.suspend-service + class early_hal + user system + group system wakelock diff --git a/aosp/system/vold/Android.bp b/aosp/system/vold/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..8d6efaa84a6cec68675f5f8161756e06973e1613 --- /dev/null +++ b/aosp/system/vold/Android.bp @@ -0,0 +1,273 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_defaults { + name: "vold_default_flags", + + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + "-Wno-unused-parameter", + ], + + tidy: true, + tidy_checks: [ + "-*", + "cert-*", + "clang-analyzer-security*", + "android-*", + ], + tidy_checks_as_errors: [ + "clang-analyzer-security*", + "cert-*", + ], +} + +cc_defaults { + name: "vold_default_libs", + + static_libs: [ + "android.hardware.health.storage@1.0", + "android.hardware.health.storage-V1-ndk", + "android.security.maintenance-ndk", + "libasync_safe", + "libavb", + "libbootloader_message", + "libdm", + "libext2_uuid", + "libfec", + "libfec_rs", + "libfs_avb", + "libfs_mgr", + "libsquashfs_utils", + "libvold_binder", + ], + shared_libs: [ + "android.hardware.boot@1.0", + "android.hardware.boot-V1-ndk", + "libboot_control_client", + "libbase", + "libbinder", + "libbinder_ndk", + "libcrypto", + "libcrypto_utils", + "libcutils", + "libext4_utils", + "libf2fs_sparseblock", + "libgsi", + "libhardware", + "libhardware_legacy", + "libincfs", + "libhidlbase", + "libkeymint_support", + "liblog", + "liblogwrap", + "libselinux", + "libsysutils", + "libutils", + ], +} + +cc_library_static { + name: "libvold_binder", + defaults: ["vold_default_flags"], + + srcs: [ + ":vold_aidl", + ], + shared_libs: [ + "libbinder", + "libutils", + ], + aidl: { + local_include_dirs: ["binder"], + include_dirs: [ + "frameworks/native/aidl/binder", + "frameworks/base/core/java", + ], + export_aidl_headers: true, + }, + whole_static_libs: [ + "libincremental_aidl-cpp", + ], + export_shared_lib_headers: [ + "libbinder", + ], +} + +cc_library_headers { + name: "libvold_headers", + recovery_available: true, + export_include_dirs: ["."], +} + +// Static library factored out to support testing +cc_library_static { + name: "libvold", + defaults: [ + "vold_default_flags", + "vold_default_libs", + "keystore2_use_latest_aidl_ndk_shared", + ], + + srcs: [ + "AppFuseUtil.cpp", + "Benchmark.cpp", + "Checkpoint.cpp", + "CryptoType.cpp", + "EncryptInplace.cpp", + "FileDeviceUtils.cpp", + "FsCrypt.cpp", + "IdleMaint.cpp", + "KeyBuffer.cpp", + "KeyStorage.cpp", + "KeyUtil.cpp", + "Keystore.cpp", + "Loop.cpp", + "MetadataCrypt.cpp", + "MoveStorage.cpp", + "NetlinkHandler.cpp", + "NetlinkManager.cpp", + "Process.cpp", + "Utils.cpp", + "VoldNativeService.cpp", + "VoldNativeServiceValidation.cpp", + "VoldUtil.cpp", + "VolumeManager.cpp", + "cryptfs.cpp", + "fs/Exfat.cpp", + "fs/Ext4.cpp", + "fs/F2fs.cpp", + "fs/Vfat.cpp", + "model/Disk.cpp", + "model/EmulatedVolume.cpp", + "model/ObbVolume.cpp", + "model/PrivateVolume.cpp", + "model/PublicVolume.cpp", + "model/StubVolume.cpp", + "model/VolumeBase.cpp", + "model/VolumeEncryption.cpp", + ], + product_variables: { + arc: { + exclude_srcs: [ + "model/StubVolume.cpp", + ], + static_libs: [ + "libarcvolume", + ], + }, + debuggable: { + cppflags: ["-D__ANDROID_DEBUGGABLE__"], + }, + }, + whole_static_libs: [ + "libc++fs", + ], + shared_libs: [ + "packagemanager_aidl-cpp", + ], +} + +cc_binary { + name: "vold", + defaults: [ + "vold_default_flags", + "vold_default_libs", + "keystore2_use_latest_aidl_ndk_shared", + ], + + srcs: ["main.cpp"], + static_libs: [ + "libvold", + ], + shared_libs: [ + "packagemanager_aidl-cpp", + ], + + init_rc: [ + "vold.rc", + ], + + required: [ + "mke2fs", + "vold_prepare_subdirs", + "fuseMedia.o", + ], + + product_variables: { + arc: { + exclude_srcs: [ + "model/StubVolume.cpp", + ], + static_libs: [ + "libarcvolume", + ], + }, + }, +} + +cc_binary { + name: "vdc", + defaults: ["vold_default_flags"], + + srcs: [ + "vdc.cpp", + "Utils.cpp", + ], + shared_libs: [ + "libbase", + "libbinder", + "libcutils", + "liblogwrap", + "libselinux", + "libutils", + ], + static_libs: [ + "libvold_binder", + ], +} + +cc_binary { + name: "secdiscard", + defaults: ["vold_default_flags"], + + srcs: [ + "FileDeviceUtils.cpp", + "secdiscard.cpp", + ], + shared_libs: ["libbase"], +} + +cc_binary { + name: "vold_prepare_subdirs", + defaults: ["vold_default_flags"], + + srcs: [ + "vold_prepare_subdirs.cpp", + "Utils.cpp", + ], + shared_libs: [ + "libbase", + "libcutils", + "liblogwrap", + "libselinux", + "libutils", + ], + static_libs: [ + "libvold_binder", + ], +} + +filegroup { + name: "vold_aidl", + srcs: [ + "binder/android/os/IVold.aidl", + "binder/android/os/IVoldListener.aidl", + "binder/android/os/IVoldMountCallback.aidl", + "binder/android/os/IVoldTaskListener.aidl", + ], + path: "binder", +} diff --git a/aosp/system/vold/Utils.cpp b/aosp/system/vold/Utils.cpp index 8b290ff349709022c0226d58f917b78882a20fc5..7add69fba406f7931ad93035cee42e6e26f5e3dc 100644 --- a/aosp/system/vold/Utils.cpp +++ b/aosp/system/vold/Utils.cpp @@ -266,7 +266,30 @@ int PrepareDirWithProjectId(const std::string& path, mode_t mode, uid_t uid, gid return ret; } -static int FixupAppDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid, long projectId) { +static int FixupEntry(std::filesystem::directory_entry itEntry, mode_t mode, uid_t uid, gid_t gid, long projectId) { + int ret = lchown(itEntry.path().c_str(), uid, gid); + if (ret != 0) { + LOG(ERROR) << "Failed to chown(" << itEntry.path() << ", 0" << std::oct << mode << ")"; + return ret; + } + + ret = chmod(itEntry.path().c_str(), mode); + if (ret != 0) { + LOG(ERROR) << "Failed to chmod(" << itEntry.path() << ", 0" << std::oct << mode << ")"; + return ret; + } + + + if (!IsSdcardfsUsed()) { + ret = SetQuotaProjectId(itEntry.path(), projectId); + if (ret != 0) { + return ret; + } + } + return OK; +} + +static int FixupAppDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid, long projectId, bool fixupRecursively) { namespace fs = std::filesystem; // Setup the directory itself correctly @@ -275,31 +298,32 @@ static int FixupAppDir(const std::string& path, mode_t mode, uid_t uid, gid_t gi return ret; } - // Fixup all of its file entries - for (const auto& itEntry : fs::directory_iterator(path)) { - ret = lchown(itEntry.path().c_str(), uid, gid); - if (ret != 0) { - return ret; - } - - ret = chmod(itEntry.path().c_str(), mode); - if (ret != 0) { - return ret; - } - - if (!IsSdcardfsUsed()) { - ret = SetQuotaProjectId(itEntry.path(), projectId); + if (fixupRecursively) { + for (const auto& itEntry : fs::recursive_directory_iterator(path)) { + if (itEntry.is_regular_file()) { + ret = FixupEntry(itEntry, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, uid, gid, projectId); + } else { + ret = FixupEntry(itEntry, mode, uid, gid, projectId); + } if (ret != 0) { return ret; } } + return OK; } + // Fixup all of its file entries + for (const auto& itEntry : fs::directory_iterator(path)) { + ret = FixupEntry(itEntry, mode, uid, gid, projectId); + if (ret != 0) { + return ret; + } + } return OK; } int PrepareAppDirFromRoot(const std::string& path, const std::string& root, int appUid, - bool fixupExisting) { + bool fixupExisting, bool fixupRecursively) { long projectId; size_t pos; int ret = 0; @@ -373,6 +397,7 @@ int PrepareAppDirFromRoot(const std::string& path, const std::string& root, int while ((pos = leftToCreate.find('/')) != std::string::npos) { std::string component = leftToCreate.substr(0, pos + 1); leftToCreate = leftToCreate.erase(0, pos + 1); + bool lastDir = leftToCreate.empty(); pathToCreate = pathToCreate + component; if (appDir == kAppDataDir && depth == 1 && component == "cache/") { @@ -386,7 +411,7 @@ int PrepareAppDirFromRoot(const std::string& path, const std::string& root, int if (fixupExisting && access(pathToCreate.c_str(), F_OK) == 0) { // Fixup all files in this existing directory with the correct UID/GID // and project ID. - ret = FixupAppDir(pathToCreate, mode, uid, gid, projectId); + ret = FixupAppDir(pathToCreate, mode, uid, gid, projectId, lastDir && fixupRecursively); } else { ret = PrepareDirWithProjectId(pathToCreate, mode, uid, gid, projectId); } diff --git a/aosp/system/vold/Utils.h b/aosp/system/vold/Utils.h new file mode 100644 index 0000000000000000000000000000000000000000..016405dd08fa782e23caad25caa2ab0207e67330 --- /dev/null +++ b/aosp/system/vold/Utils.h @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_VOLD_UTILS_H +#define ANDROID_VOLD_UTILS_H + +#include "KeyBuffer.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct DIR; + +namespace android { +namespace vold { + +static const char* kVoldAppDataIsolationEnabled = "persist.sys.vold_app_data_isolation_enabled"; +static const char* kExternalStorageSdcardfs = "external_storage.sdcardfs.enabled"; + +static constexpr std::chrono::seconds kUntrustedFsckSleepTime(45); + +/* SELinux contexts used depending on the block device type */ +extern char* sBlkidContext; +extern char* sBlkidUntrustedContext; +extern char* sFsckContext; +extern char* sFsckUntrustedContext; + +// TODO remove this with better solution, b/64143519 +extern bool sSleepOnUnmount; + +std::string GetFuseMountPathForUser(userid_t user_id, const std::string& relative_upper_path); + +status_t CreateDeviceNode(const std::string& path, dev_t dev); +status_t DestroyDeviceNode(const std::string& path); + +status_t SetDefaultAcl(const std::string& path, mode_t mode, uid_t uid, gid_t gid, + std::vector additionalGids); + +status_t AbortFuseConnections(); + +int SetQuotaInherit(const std::string& path); +int SetQuotaProjectId(const std::string& path, long projectId); +/* + * Creates and sets up an application-specific path on external + * storage with the correct ACL and project ID (if needed). + * + * ONLY for use with app-specific data directories on external storage! + * (eg, /Android/data/com.foo, /Android/obb/com.foo, etc.) + */ +int PrepareAppDirFromRoot(const std::string& path, const std::string& root, int appUid, + bool fixupExisting, bool fixupRecursively); + +/* fs_prepare_dir wrapper that creates with SELinux context */ +status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid, + unsigned int attrs = 0); + +/* Really unmounts the path, killing active processes along the way */ +status_t ForceUnmount(const std::string& path); + +/* Kills any processes using given path */ +status_t KillProcessesUsingPath(const std::string& path); + +/* Kills any processes using given tmpfs mount prifix */ +status_t KillProcessesWithTmpfsMountPrefix(const std::string& path); + +/* Creates bind mount from source to target */ +status_t BindMount(const std::string& source, const std::string& target); + +/** Creates a symbolic link to target */ +status_t Symlink(const std::string& target, const std::string& linkpath); + +/** Calls unlink(2) at linkpath */ +status_t Unlink(const std::string& linkpath); + +/** Creates the given directory if it is not already available */ +status_t CreateDir(const std::string& dir, mode_t mode); + +bool FindValue(const std::string& raw, const std::string& key, std::string* value); + +/* Reads filesystem metadata from device at path */ +status_t ReadMetadata(const std::string& path, std::string* fsType, std::string* fsUuid, + std::string* fsLabel); + +/* Reads filesystem metadata from untrusted device at path */ +status_t ReadMetadataUntrusted(const std::string& path, std::string* fsType, std::string* fsUuid, + std::string* fsLabel); + +/* Returns either WEXITSTATUS() status, or a negative errno */ +status_t ForkExecvp(const std::vector& args, + std::vector* output = nullptr, char* context = nullptr); +status_t ForkExecvpTimeout(const std::vector& args, std::chrono::seconds timeout, + char* context = nullptr); + +pid_t ForkExecvpAsync(const std::vector& args, char* context = nullptr); + +/* Gets block device size in bytes */ +status_t GetBlockDevSize(int fd, uint64_t* size); +status_t GetBlockDevSize(const std::string& path, uint64_t* size); +/* Gets block device size in 512 byte sectors */ +status_t GetBlockDev512Sectors(const std::string& path, uint64_t* nr_sec); + +status_t ReadRandomBytes(size_t bytes, std::string& out); +status_t ReadRandomBytes(size_t bytes, char* buffer); +status_t GenerateRandomUuid(std::string& out); + +/* Converts hex string to raw bytes, ignoring [ :-] */ +status_t HexToStr(const std::string& hex, std::string& str); +/* Converts raw bytes to hex string */ +status_t StrToHex(const std::string& str, std::string& hex); +/* Converts raw key bytes to hex string */ +status_t StrToHex(const KeyBuffer& str, KeyBuffer& hex); +/* Normalize given hex string into consistent format */ +status_t NormalizeHex(const std::string& in, std::string& out); + +uint64_t GetFreeBytes(const std::string& path); +uint64_t GetTreeBytes(const std::string& path); + +bool IsFilesystemSupported(const std::string& fsType); +bool IsSdcardfsUsed(); +bool IsFuseDaemon(const pid_t pid); + +/* Wipes contents of block device at given path */ +status_t WipeBlockDevice(const std::string& path); + +std::string BuildKeyPath(const std::string& partGuid); + +std::string BuildDataSystemLegacyPath(userid_t userid); +std::string BuildDataSystemCePath(userid_t userid); +std::string BuildDataSystemDePath(userid_t userid); +std::string BuildDataProfilesDePath(userid_t userid); +std::string BuildDataVendorCePath(userid_t userid); +std::string BuildDataVendorDePath(userid_t userid); + +std::string BuildDataPath(const std::string& volumeUuid); +std::string BuildDataMediaCePath(const std::string& volumeUuid, userid_t userid); +std::string BuildDataMiscCePath(const std::string& volumeUuid, userid_t userid); +std::string BuildDataMiscDePath(const std::string& volumeUuid, userid_t userid); +std::string BuildDataUserCePath(const std::string& volumeUuid, userid_t userid); +std::string BuildDataUserDePath(const std::string& volumeUuid, userid_t userid); + +dev_t GetDevice(const std::string& path); + +bool IsSymlink(const std::string& path); + +bool IsSameFile(const std::string& path1, const std::string& path2); + +status_t EnsureDirExists(const std::string& path, mode_t mode, uid_t uid, gid_t gid); + +status_t RestoreconRecursive(const std::string& path); + +// TODO: promote to android::base +bool Readlinkat(int dirfd, const std::string& path, std::string* result); + +// Handles dynamic major assignment for virtio-block +bool IsVirtioBlkDevice(unsigned int major); + +status_t UnmountTree(const std::string& mountPoint); + +bool IsDotOrDotDot(const struct dirent& ent); + +status_t DeleteDirContentsAndDir(const std::string& pathname); +status_t DeleteDirContents(const std::string& pathname); + +status_t WaitForFile(const char* filename, std::chrono::nanoseconds timeout); + +bool pathExists(const std::string& path); + +bool FsyncDirectory(const std::string& dirname); + +bool FsyncParentDirectory(const std::string& path); + +bool MkdirsSync(const std::string& path, mode_t mode); + +bool writeStringToFile(const std::string& payload, const std::string& filename); + +void ConfigureMaxDirtyRatioForFuse(const std::string& fuse_mount, unsigned int max_ratio); + +void ConfigureReadAheadForFuse(const std::string& fuse_mount, size_t read_ahead_kb); + +status_t MountUserFuse(userid_t user_id, const std::string& absolute_lower_path, + const std::string& relative_upper_path, android::base::unique_fd* fuse_fd); + +status_t UnmountUserFuse(userid_t userId, const std::string& absolute_lower_path, + const std::string& relative_upper_path); + +status_t PrepareAndroidDirs(const std::string& volumeRoot); + +bool IsFuseBpfEnabled(); + +// Open a given directory as an FD, and return that and the corresponding procfs virtual +// symlink path that can be used in any API that accepts a path string. Path stays valid until +// the directory FD is closed. +// +// This may be useful when an API wants to restrict a path passed from an untrusted process, +// and do it without any TOCTOU attacks possible (e.g. where an attacker replaces one of +// the components with a symlink after the check passed). In that case opening a path through +// this function guarantees that the target directory stays the same, and that it can be +// referenced inside the current process via the virtual procfs symlink returned here. +std::pair OpenDirInProcfs(std::string_view path); + +} // namespace vold +} // namespace android + +#endif diff --git a/aosp/system/vold/VoldNativeService.cpp b/aosp/system/vold/VoldNativeService.cpp index 2771860670206654ee132817f8616a4a41024c93..5693febc3300313a19bcdbf353698ac0613ceab3 100644 --- a/aosp/system/vold/VoldNativeService.cpp +++ b/aosp/system/vold/VoldNativeService.cpp @@ -19,6 +19,7 @@ #include "VoldNativeService.h" #include +#include #include #include #include @@ -43,6 +44,7 @@ #include "cryptfs.h" #include "incfs.h" +using android::base::SetProperty; using namespace std::literals; namespace android { @@ -287,6 +289,14 @@ binder::Status VoldNativeService::mount( vol->setMountCallback(callback); int res = vol->mount(); + if (vol->getType() == VolumeBase::Type::kEmulated) { + LOG(INFO) << "Start to fix " << volId; + res |= VolumeManager::Instance()->fixupAllDirOfUser(mountUserId); + LOG(INFO) << "Finished fixing " << volId; + if (mountUserId == 0) { + SetProperty("sys.storage_ready", "1"); + } + } vol->setMountCallback(nullptr); if (res != OK) { @@ -425,6 +435,21 @@ binder::Status VoldNativeService::fixupAppDir(const std::string& path, int32_t a return translate(VolumeManager::Instance()->fixupAppDir(path, appUid)); } +binder::Status VoldNativeService::fixupAppDirRecursively(const std::string& path, int32_t appUid) { + ENFORCE_SYSTEM_OR_ROOT; + CHECK_ARGUMENT_PATH(path); + ACQUIRE_LOCK; + + return translate(VolumeManager::Instance()->fixupAppDirRecursively(path, appUid)); +} + +binder::Status VoldNativeService::fixupAllDirOfUser(int32_t userId) { + ENFORCE_SYSTEM_OR_ROOT; + ACQUIRE_LOCK; + + return translate(VolumeManager::Instance()->fixupAllDirOfUser(userId)); +} + binder::Status VoldNativeService::createObb(const std::string& sourcePath, int32_t ownerGid, std::string* _aidl_return) { ENFORCE_SYSTEM_OR_ROOT; diff --git a/aosp/system/vold/VoldNativeService.h b/aosp/system/vold/VoldNativeService.h new file mode 100644 index 0000000000000000000000000000000000000000..7b8e31480f48af5c750a2bd4578d19c431b246ff --- /dev/null +++ b/aosp/system/vold/VoldNativeService.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _VOLD_NATIVE_SERVICE_H_ +#define _VOLD_NATIVE_SERVICE_H_ + +#include +#include + +#include "android/os/BnVold.h" + +namespace android { +namespace vold { + +class VoldNativeService : public BinderService, public os::BnVold { + public: + static status_t start(); + static char const* getServiceName() { return "vold"; } + virtual status_t dump(int fd, const Vector& args) override; + + binder::Status setListener(const android::sp& listener); + + binder::Status monitor(); + binder::Status reset(); + binder::Status shutdown(); + binder::Status abortFuse(); + + binder::Status onUserAdded(int32_t userId, int32_t userSerial, int32_t sharesStorageWithUserId); + binder::Status onUserRemoved(int32_t userId); + binder::Status onUserStarted(int32_t userId); + binder::Status onUserStopped(int32_t userId); + + binder::Status addAppIds(const std::vector& packageNames, + const std::vector& appIds); + binder::Status addSandboxIds(const std::vector& appIds, + const std::vector& sandboxIds); + + binder::Status onSecureKeyguardStateChanged(bool isShowing); + + binder::Status partition(const std::string& diskId, int32_t partitionType, int32_t ratio); + binder::Status forgetPartition(const std::string& partGuid, const std::string& fsUuid); + + binder::Status mount(const std::string& volId, int32_t mountFlags, int32_t mountUserId, + const android::sp& callback); + binder::Status unmount(const std::string& volId); + binder::Status format(const std::string& volId, const std::string& fsType); + binder::Status benchmark(const std::string& volId, + const android::sp& listener); + + binder::Status moveStorage(const std::string& fromVolId, const std::string& toVolId, + const android::sp& listener); + + binder::Status remountUid(int32_t uid, int32_t remountMode); + binder::Status remountAppStorageDirs(int uid, int pid, + const std::vector& packageNames); + binder::Status unmountAppStorageDirs(int uid, int pid, + const std::vector& packageNames); + + binder::Status ensureAppDirsCreated(const std::vector& paths, int32_t appUid); + binder::Status setupAppDir(const std::string& path, int32_t appUid); + binder::Status fixupAppDir(const std::string& path, int32_t appUid); + binder::Status fixupAppDirRecursively(const std::string& path, int32_t appUid); + binder::Status fixupAllDirOfUser(int32_t userid_t); + + binder::Status createObb(const std::string& sourcePath, int32_t ownerGid, + std::string* _aidl_return); + binder::Status destroyObb(const std::string& volId); + + binder::Status createStubVolume(const std::string& sourcePath, const std::string& mountPath, + const std::string& fsType, const std::string& fsUuid, + const std::string& fsLabel, int32_t flags, + std::string* _aidl_return); + binder::Status destroyStubVolume(const std::string& volId); + + binder::Status fstrim(int32_t fstrimFlags, + const android::sp& listener); + binder::Status runIdleMaint(bool needGC, + const android::sp& listener); + binder::Status abortIdleMaint(const android::sp& listener); + binder::Status getStorageLifeTime(int32_t* _aidl_return); + binder::Status getStorageRemainingLifetime(int32_t* _aidl_return); + binder::Status setGCUrgentPace(int32_t neededSegments, int32_t minSegmentThreshold, + float dirtyReclaimRate, float reclaimWeight, int32_t gcPeriod, + int32_t minGCSleepTime, int32_t targetDirtyRatio); + binder::Status refreshLatestWrite(); + binder::Status getWriteAmount(int32_t* _aidl_return); + + binder::Status mountAppFuse(int32_t uid, int32_t mountId, + android::base::unique_fd* _aidl_return); + binder::Status unmountAppFuse(int32_t uid, int32_t mountId); + binder::Status openAppFuseFile(int32_t uid, int32_t mountId, int32_t fileId, int32_t flags, + android::base::unique_fd* _aidl_return); + + binder::Status fbeEnable(); + + binder::Status initUser0(); + binder::Status mountFstab(const std::string& blkDevice, const std::string& mountPoint, + const std::string& zonedDevice); + binder::Status encryptFstab(const std::string& blkDevice, const std::string& mountPoint, + bool shouldFormat, const std::string& fsType, + const std::string& zonedDevice); + + binder::Status setStorageBindingSeed(const std::vector& seed); + + binder::Status createUserStorageKeys(int32_t userId, bool ephemeral); + binder::Status destroyUserStorageKeys(int32_t userId); + + binder::Status setCeStorageProtection(int32_t userId, const std::string& secret); + + binder::Status getUnlockedUsers(std::vector* _aidl_return); + binder::Status unlockCeStorage(int32_t userId, const std::string& secret); + binder::Status lockCeStorage(int32_t userId); + + binder::Status prepareUserStorage(const std::optional& uuid, int32_t userId, + int32_t flags); + binder::Status destroyUserStorage(const std::optional& uuid, int32_t userId, + int32_t flags); + + binder::Status prepareSandboxForApp(const std::string& packageName, int32_t appId, + const std::string& sandboxId, int32_t userId); + binder::Status destroySandboxForApp(const std::string& packageName, + const std::string& sandboxId, int32_t userId); + + binder::Status startCheckpoint(int32_t retry); + binder::Status needsCheckpoint(bool* _aidl_return); + binder::Status needsRollback(bool* _aidl_return); + binder::Status isCheckpointing(bool* _aidl_return); + binder::Status commitChanges(); + binder::Status prepareCheckpoint(); + binder::Status restoreCheckpoint(const std::string& mountPoint); + binder::Status restoreCheckpointPart(const std::string& mountPoint, int count); + binder::Status markBootAttempt(); + binder::Status abortChanges(const std::string& message, bool retry); + binder::Status supportsCheckpoint(bool* _aidl_return); + binder::Status supportsBlockCheckpoint(bool* _aidl_return); + binder::Status supportsFileCheckpoint(bool* _aidl_return); + binder::Status resetCheckpoint(); + + binder::Status earlyBootEnded(); + + binder::Status incFsEnabled(bool* _aidl_return) override; + binder::Status mountIncFs( + const std::string& backingPath, const std::string& targetDir, int32_t flags, + const std::string& sysfsName, + ::android::os::incremental::IncrementalFileSystemControlParcel* _aidl_return) override; + binder::Status unmountIncFs(const std::string& dir) override; + binder::Status setIncFsMountOptions( + const ::android::os::incremental::IncrementalFileSystemControlParcel& control, + bool enableReadLogs, bool enableReadTimeouts, const std::string& sysfsName) override; + binder::Status bindMount(const std::string& sourceDir, const std::string& targetDir) override; + + binder::Status destroyDsuMetadataKey(const std::string& dsuSlot) override; + + binder::Status getStorageSize(int64_t* storageSize) override; +}; + +} // namespace vold +} // namespace android + +#endif // _VOLD_NATIVE_SERVICE_H_ diff --git a/aosp/system/vold/VolumeManager.cpp b/aosp/system/vold/VolumeManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7f49eec524552633a70f622fe8aba85a50efbb8c --- /dev/null +++ b/aosp/system/vold/VolumeManager.cpp @@ -0,0 +1,1512 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include + +#include +#include + +#include "AppFuseUtil.h" +#include "FsCrypt.h" +#include "Loop.h" +#include "NetlinkManager.h" +#include "Process.h" +#include "Utils.h" +#include "VoldNativeService.h" +#include "VoldUtil.h" +#include "VolumeManager.h" +#include "fs/Ext4.h" +#include "fs/Vfat.h" +#include "model/EmulatedVolume.h" +#include "model/ObbVolume.h" +#include "model/PrivateVolume.h" +#include "model/PublicVolume.h" +#include "model/StubVolume.h" + +using android::OK; +using android::base::GetBoolProperty; +using android::base::StartsWith; +using android::base::StringAppendF; +using android::base::StringPrintf; +using android::base::unique_fd; +using android::vold::BindMount; +using android::vold::CreateDir; +using android::vold::DeleteDirContents; +using android::vold::DeleteDirContentsAndDir; +using android::vold::EnsureDirExists; +using android::vold::GetFuseMountPathForUser; +using android::vold::IsFilesystemSupported; +using android::vold::IsSdcardfsUsed; +using android::vold::IsVirtioBlkDevice; +using android::vold::PrepareAndroidDirs; +using android::vold::PrepareAppDirFromRoot; +using android::vold::PrivateVolume; +using android::vold::PublicVolume; +using android::vold::Symlink; +using android::vold::Unlink; +using android::vold::UnmountTree; +using android::vold::VoldNativeService; +using android::vold::VolumeBase; + +static const char* kPathVirtualDisk = "/data/misc/vold/virtual_disk"; + +static const char* kPropVirtualDisk = "persist.sys.virtual_disk"; + +static const std::string kEmptyString(""); + +/* 512MiB is large enough for testing purposes */ +static const unsigned int kSizeVirtualDisk = 536870912; + +static const unsigned int kMajorBlockMmc = 179; + +using ScanProcCallback = bool(*)(uid_t uid, pid_t pid, int nsFd, const char* name, void* params); + +VolumeManager* VolumeManager::sInstance = NULL; + +VolumeManager* VolumeManager::Instance() { + if (!sInstance) sInstance = new VolumeManager(); + return sInstance; +} + +VolumeManager::VolumeManager() { + mDebug = false; + mNextObbId = 0; + mNextStubId = 0; + // For security reasons, assume that a secure keyguard is + // showing until we hear otherwise + mSecureKeyguardShowing = true; +} + +VolumeManager::~VolumeManager() {} + +int VolumeManager::updateVirtualDisk() { + ATRACE_NAME("VolumeManager::updateVirtualDisk"); + if (GetBoolProperty(kPropVirtualDisk, false)) { + if (access(kPathVirtualDisk, F_OK) != 0) { + Loop::createImageFile(kPathVirtualDisk, kSizeVirtualDisk / 512); + } + + if (mVirtualDisk == nullptr) { + if (Loop::create(kPathVirtualDisk, mVirtualDiskPath) != 0) { + LOG(ERROR) << "Failed to create virtual disk"; + return -1; + } + + struct stat buf; + if (stat(mVirtualDiskPath.c_str(), &buf) < 0) { + PLOG(ERROR) << "Failed to stat " << mVirtualDiskPath; + return -1; + } + + auto disk = new android::vold::Disk( + "virtual", buf.st_rdev, "virtual", + android::vold::Disk::Flags::kAdoptable | android::vold::Disk::Flags::kSd); + mVirtualDisk = std::shared_ptr(disk); + handleDiskAdded(mVirtualDisk); + } + } else { + if (mVirtualDisk != nullptr) { + dev_t device = mVirtualDisk->getDevice(); + handleDiskRemoved(device); + + Loop::destroyByDevice(mVirtualDiskPath.c_str()); + mVirtualDisk = nullptr; + } + + if (access(kPathVirtualDisk, F_OK) == 0) { + unlink(kPathVirtualDisk); + } + } + return 0; +} + +int VolumeManager::setDebug(bool enable) { + mDebug = enable; + return 0; +} + +int VolumeManager::start() { + ATRACE_NAME("VolumeManager::start"); + + // Always start from a clean slate by unmounting everything in + // directories that we own, in case we crashed. + unmountAll(); + + Loop::destroyAll(); + + // Assume that we always have an emulated volume on internal + // storage; the framework will decide if it should be mounted. + CHECK(mInternalEmulatedVolumes.empty()); + + auto vol = std::shared_ptr( + new android::vold::EmulatedVolume("/data/media", 0)); + vol->setMountUserId(0); + vol->create(); + mInternalEmulatedVolumes.push_back(vol); + + // Consider creating a virtual disk + updateVirtualDisk(); + + return 0; +} + +void VolumeManager::handleBlockEvent(NetlinkEvent* evt) { + std::lock_guard lock(mLock); + + if (mDebug) { + LOG(DEBUG) << "----------------"; + LOG(DEBUG) << "handleBlockEvent with action " << (int)evt->getAction(); + evt->dump(); + } + + std::string eventPath(evt->findParam("DEVPATH") ? evt->findParam("DEVPATH") : ""); + std::string devType(evt->findParam("DEVTYPE") ? evt->findParam("DEVTYPE") : ""); + + if (devType != "disk") return; + + int major = std::stoi(evt->findParam("MAJOR")); + int minor = std::stoi(evt->findParam("MINOR")); + dev_t device = makedev(major, minor); + + switch (evt->getAction()) { + case NetlinkEvent::Action::kAdd: { + for (const auto& source : mDiskSources) { + if (source->matches(eventPath)) { + // For now, assume that MMC and virtio-blk (the latter is + // specific to virtual platforms; see Utils.cpp for details) + // devices are SD, and that everything else is USB + int flags = source->getFlags(); + if (major == kMajorBlockMmc || IsVirtioBlkDevice(major)) { + flags |= android::vold::Disk::Flags::kSd; + } else { + flags |= android::vold::Disk::Flags::kUsb; + } + + auto disk = + new android::vold::Disk(eventPath, device, source->getNickname(), flags); + handleDiskAdded(std::shared_ptr(disk)); + break; + } + } + break; + } + case NetlinkEvent::Action::kChange: { + LOG(VERBOSE) << "Disk at " << major << ":" << minor << " changed"; + handleDiskChanged(device); + break; + } + case NetlinkEvent::Action::kRemove: { + handleDiskRemoved(device); + break; + } + default: { + LOG(WARNING) << "Unexpected block event action " << (int)evt->getAction(); + break; + } + } +} + +void VolumeManager::handleDiskAdded(const std::shared_ptr& disk) { + // For security reasons, if secure keyguard is showing, wait + // until the user unlocks the device to actually touch it + // Additionally, wait until user 0 is actually started, since we need + // the user to be up before we can mount a FUSE daemon to handle the disk. + bool userZeroStarted = mStartedUsers.find(0) != mStartedUsers.end(); + if (mSecureKeyguardShowing) { + LOG(INFO) << "Found disk at " << disk->getEventPath() + << " but delaying scan due to secure keyguard"; + mPendingDisks.push_back(disk); + } else if (!userZeroStarted) { + LOG(INFO) << "Found disk at " << disk->getEventPath() + << " but delaying scan due to user zero not having started"; + mPendingDisks.push_back(disk); + } else { + disk->create(); + mDisks.push_back(disk); + } +} + +void VolumeManager::handleDiskChanged(dev_t device) { + for (const auto& disk : mDisks) { + if (disk->getDevice() == device) { + disk->readMetadata(); + disk->readPartitions(); + } + } + + // For security reasons, we ignore all pending disks, since + // we'll scan them once the device is unlocked +} + +void VolumeManager::handleDiskRemoved(dev_t device) { + auto i = mDisks.begin(); + while (i != mDisks.end()) { + if ((*i)->getDevice() == device) { + (*i)->destroy(); + i = mDisks.erase(i); + } else { + ++i; + } + } + auto j = mPendingDisks.begin(); + while (j != mPendingDisks.end()) { + if ((*j)->getDevice() == device) { + j = mPendingDisks.erase(j); + } else { + ++j; + } + } +} + +void VolumeManager::addDiskSource(const std::shared_ptr& diskSource) { + std::lock_guard lock(mLock); + mDiskSources.push_back(diskSource); +} + +std::shared_ptr VolumeManager::findDisk(const std::string& id) { + for (auto disk : mDisks) { + if (disk->getId() == id) { + return disk; + } + } + return nullptr; +} + +std::shared_ptr VolumeManager::findVolume(const std::string& id) { + for (const auto& vol : mInternalEmulatedVolumes) { + if (vol->getId() == id) { + return vol; + } + } + for (const auto& disk : mDisks) { + auto vol = disk->findVolume(id); + if (vol != nullptr) { + return vol; + } + } + for (const auto& vol : mObbVolumes) { + if (vol->getId() == id) { + return vol; + } + } + return nullptr; +} + +void VolumeManager::listVolumes(android::vold::VolumeBase::Type type, + std::list& list) const { + list.clear(); + for (const auto& disk : mDisks) { + disk->listVolumes(type, list); + } +} + +bool VolumeManager::forgetPartition(const std::string& partGuid, const std::string& fsUuid) { + std::string normalizedGuid; + if (android::vold::NormalizeHex(partGuid, normalizedGuid)) { + LOG(WARNING) << "Invalid GUID " << partGuid; + return false; + } + + std::string keyPath = android::vold::BuildKeyPath(normalizedGuid); + if (unlink(keyPath.c_str()) != 0) { + LOG(ERROR) << "Failed to unlink " << keyPath; + return false; + } + return true; +} + +void VolumeManager::destroyEmulatedVolumesForUser(userid_t userId) { + // Destroy and remove all unstacked EmulatedVolumes for the user + auto i = mInternalEmulatedVolumes.begin(); + while (i != mInternalEmulatedVolumes.end()) { + auto vol = *i; + if (vol->getMountUserId() == userId) { + vol->destroy(); + i = mInternalEmulatedVolumes.erase(i); + } else { + i++; + } + } + + // Destroy and remove all stacked EmulatedVolumes for the user on each mounted private volume + std::list private_vols; + listVolumes(VolumeBase::Type::kPrivate, private_vols); + for (const std::string& id : private_vols) { + PrivateVolume* pvol = static_cast(findVolume(id).get()); + std::list> vols_to_remove; + if (pvol->getState() == VolumeBase::State::kMounted) { + for (const auto& vol : pvol->getVolumes()) { + if (vol->getMountUserId() == userId) { + vols_to_remove.push_back(vol); + } + } + for (const auto& vol : vols_to_remove) { + vol->destroy(); + pvol->removeVolume(vol); + } + } // else EmulatedVolumes will be destroyed on VolumeBase#unmount + } +} + +void VolumeManager::createEmulatedVolumesForUser(userid_t userId) { + // Create unstacked EmulatedVolumes for the user + auto vol = std::shared_ptr( + new android::vold::EmulatedVolume("/data/media", userId)); + vol->setMountUserId(userId); + mInternalEmulatedVolumes.push_back(vol); + vol->create(); + + // Create stacked EmulatedVolumes for the user on each PrivateVolume + std::list private_vols; + listVolumes(VolumeBase::Type::kPrivate, private_vols); + for (const std::string& id : private_vols) { + PrivateVolume* pvol = static_cast(findVolume(id).get()); + if (pvol->getState() == VolumeBase::State::kMounted) { + auto evol = + std::shared_ptr(new android::vold::EmulatedVolume( + pvol->getPath() + "/media", pvol->getRawDevice(), pvol->getFsUuid(), + userId)); + evol->setMountUserId(userId); + pvol->addVolume(evol); + evol->create(); + } // else EmulatedVolumes will be created per user when on PrivateVolume#doMount + } +} + +userid_t VolumeManager::getSharedStorageUser(userid_t userId) { + if (mSharedStorageUser.find(userId) == mSharedStorageUser.end()) { + return USER_UNKNOWN; + } + return mSharedStorageUser.at(userId); +} + +int VolumeManager::onUserAdded(userid_t userId, int userSerialNumber, + userid_t sharesStorageWithUserId) { + LOG(INFO) << "onUserAdded: " << userId; + + mAddedUsers[userId] = userSerialNumber; + if (sharesStorageWithUserId != USER_UNKNOWN) { + mSharedStorageUser[userId] = sharesStorageWithUserId; + } + return 0; +} + +int VolumeManager::onUserRemoved(userid_t userId) { + LOG(INFO) << "onUserRemoved: " << userId; + + onUserStopped(userId); + mAddedUsers.erase(userId); + mSharedStorageUser.erase(userId); + return 0; +} + +int VolumeManager::onUserStarted(userid_t userId) { + LOG(INFO) << "onUserStarted: " << userId; + + if (mStartedUsers.find(userId) == mStartedUsers.end()) { + createEmulatedVolumesForUser(userId); + std::list public_vols; + listVolumes(VolumeBase::Type::kPublic, public_vols); + for (const std::string& id : public_vols) { + PublicVolume* pvol = static_cast(findVolume(id).get()); + if (pvol->getState() != VolumeBase::State::kMounted) { + continue; + } + if (pvol->isVisible() == 0) { + continue; + } + userid_t mountUserId = pvol->getMountUserId(); + if (userId == mountUserId) { + // No need to bind mount for the user that owns the mount + continue; + } + if (mountUserId != VolumeManager::Instance()->getSharedStorageUser(userId)) { + // No need to bind if the user does not share storage with the mount owner + continue; + } + auto bindMountStatus = pvol->bindMountForUser(userId); + if (bindMountStatus != OK) { + LOG(ERROR) << "Bind Mounting Public Volume: " << pvol << " for user: " << userId + << "Failed. Error: " << bindMountStatus; + } + } + } + + mStartedUsers.insert(userId); + + createPendingDisksIfNeeded(); + return 0; +} + +int VolumeManager::onUserStopped(userid_t userId) { + LOG(VERBOSE) << "onUserStopped: " << userId; + + if (mStartedUsers.find(userId) != mStartedUsers.end()) { + destroyEmulatedVolumesForUser(userId); + } + + mStartedUsers.erase(userId); + return 0; +} + +void VolumeManager::createPendingDisksIfNeeded() { + bool userZeroStarted = mStartedUsers.find(0) != mStartedUsers.end(); + if (!mSecureKeyguardShowing && userZeroStarted) { + // Now that secure keyguard has been dismissed and user 0 has + // started, process any pending disks + for (const auto& disk : mPendingDisks) { + disk->create(); + mDisks.push_back(disk); + } + mPendingDisks.clear(); + } +} + +int VolumeManager::onSecureKeyguardStateChanged(bool isShowing) { + mSecureKeyguardShowing = isShowing; + createPendingDisksIfNeeded(); + return 0; +} + +// This code is executed after a fork so it's very important that the set of +// methods we call here is strictly limited. +// +// TODO: Get rid of this guesswork altogether and instead exec a process +// immediately after fork to do our bindding for us. +static bool childProcess(const char* storageSource, const char* userSource, int nsFd, + const char* name) { + if (setns(nsFd, CLONE_NEWNS) != 0) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to setns for %s :%s", name, + strerror(errno)); + return false; + } + + // NOTE: Inlined from vold::UnmountTree here to avoid using PLOG methods and + // to also protect against future changes that may cause issues across a + // fork. + if (TEMP_FAILURE_RETRY(umount2("/storage/", MNT_DETACH)) < 0 && errno != EINVAL && + errno != ENOENT) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to unmount /storage/ :%s", + strerror(errno)); + return false; + } + + if (TEMP_FAILURE_RETRY(mount(storageSource, "/storage", NULL, MS_BIND | MS_REC, NULL)) == -1) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount %s for %s :%s", + storageSource, name, strerror(errno)); + return false; + } + + if (TEMP_FAILURE_RETRY(mount(NULL, "/storage", NULL, MS_REC | MS_SLAVE, NULL)) == -1) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", + "Failed to set MS_SLAVE to /storage for %s :%s", name, + strerror(errno)); + return false; + } + + if (TEMP_FAILURE_RETRY(mount(userSource, "/storage/self", NULL, MS_BIND, NULL)) == -1) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount %s for %s :%s", + userSource, name, strerror(errno)); + return false; + } + + return true; +} + +// Fork the process and remount storage +bool forkAndRemountChild(uid_t uid, pid_t pid, int nsFd, const char* name, void* params) { + int32_t mountMode = *static_cast(params); + std::string userSource; + std::string storageSource; + pid_t child; + // Need to fix these paths to account for when sdcardfs is gone + switch (mountMode) { + case VoldNativeService::REMOUNT_MODE_NONE: + return true; + case VoldNativeService::REMOUNT_MODE_DEFAULT: + storageSource = "/mnt/runtime/default"; + break; + case VoldNativeService::REMOUNT_MODE_ANDROID_WRITABLE: + case VoldNativeService::REMOUNT_MODE_INSTALLER: + storageSource = "/mnt/runtime/write"; + break; + case VoldNativeService::REMOUNT_MODE_PASS_THROUGH: + return true; + default: + PLOG(ERROR) << "Unknown mode " << std::to_string(mountMode); + return false; + } + LOG(DEBUG) << "Remounting " << uid << " as " << storageSource; + + // Fork a child to mount user-specific symlink helper into place + userSource = StringPrintf("/mnt/user/%d", multiuser_get_user_id(uid)); + if (!(child = fork())) { + if (childProcess(storageSource.c_str(), userSource.c_str(), nsFd, name)) { + _exit(0); + } else { + _exit(1); + } + } + + if (child == -1) { + PLOG(ERROR) << "Failed to fork"; + return false; + } else { + TEMP_FAILURE_RETRY(waitpid(child, nullptr, 0)); + } + return true; +} + +// Helper function to scan all processes in /proc and call the callback if: +// 1). pid belongs to an app process +// 2). If input uid is 0 or it matches the process uid +// 3). If userId is not -1 or userId matches the process userId +bool scanProcProcesses(uid_t uid, userid_t userId, ScanProcCallback callback, void* params) { + DIR* dir; + struct dirent* de; + std::string rootName; + std::string pidName; + std::string exeName; + int pidFd; + int nsFd; + struct stat sb; + + if (!(dir = opendir("/proc"))) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to opendir"); + return false; + } + + // Figure out root namespace to compare against below + if (!android::vold::Readlinkat(dirfd(dir), "1/ns/mnt", &rootName)) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to read root namespace"); + closedir(dir); + return false; + } + + async_safe_format_log(ANDROID_LOG_INFO, "vold", "Start scanning all processes"); + // Poke through all running PIDs look for apps running as UID + while ((de = readdir(dir))) { + pid_t pid; + if (de->d_type != DT_DIR) continue; + if (!android::base::ParseInt(de->d_name, &pid)) continue; + + pidFd = -1; + nsFd = -1; + + pidFd = openat(dirfd(dir), de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (pidFd < 0) { + goto next; + } + if (fstat(pidFd, &sb) != 0) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to stat %s", de->d_name); + goto next; + } + if (uid != 0 && sb.st_uid != uid) { + goto next; + } + if (userId != static_cast(-1) && multiuser_get_user_id(sb.st_uid) != userId) { + goto next; + } + + // Matches so far, but refuse to touch if in root namespace + if (!android::vold::Readlinkat(pidFd, "ns/mnt", &pidName)) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", + "Failed to read namespacefor %s", de->d_name); + goto next; + } + if (rootName == pidName) { + goto next; + } + + // Some early native processes have mount namespaces that are different + // from that of the init. Therefore, above check can't filter them out. + // Since the propagation type of / is 'shared', unmounting /storage + // for the early native processes affects other processes including + // init. Filter out such processes by skipping if a process is a + // non-Java process whose UID is < AID_APP_START. (The UID condition + // is required to not filter out child processes spawned by apps.) + if (!android::vold::Readlinkat(pidFd, "exe", &exeName)) { + goto next; + } + if (!StartsWith(exeName, "/system/bin/app_process") && sb.st_uid < AID_APP_START) { + goto next; + } + + // We purposefully leave the namespace open across the fork + // NOLINTNEXTLINE(android-cloexec-open): Deliberately not O_CLOEXEC + nsFd = openat(pidFd, "ns/mnt", O_RDONLY); + if (nsFd < 0) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", + "Failed to open namespace for %s", de->d_name); + goto next; + } + + if (!callback(sb.st_uid, pid, nsFd, de->d_name, params)) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed in callback"); + } + + next: + close(nsFd); + close(pidFd); + } + closedir(dir); + async_safe_format_log(ANDROID_LOG_INFO, "vold", "Finished scanning all processes"); + return true; +} + +// In each app's namespace, unmount obb and data dirs +static bool umountStorageDirs(int nsFd, const char* android_data_dir, const char* android_obb_dir, + int uid, const char* targets[], int size) { + // This code is executed after a fork so it's very important that the set of + // methods we call here is strictly limited. + if (setns(nsFd, CLONE_NEWNS) != 0) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to setns %s", strerror(errno)); + return false; + } + + // Unmount of Android/data/foo needs to be done before Android/data below. + bool result = true; + for (int i = 0; i < size; i++) { + if (TEMP_FAILURE_RETRY(umount2(targets[i], MNT_DETACH)) < 0 && errno != EINVAL && + errno != ENOENT) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to umount %s: %s", + targets[i], strerror(errno)); + result = false; + } + } + + // Mount tmpfs on Android/data and Android/obb + if (TEMP_FAILURE_RETRY(umount2(android_data_dir, MNT_DETACH)) < 0 && errno != EINVAL && + errno != ENOENT) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to umount %s :%s", + android_data_dir, strerror(errno)); + result = false; + } + if (TEMP_FAILURE_RETRY(umount2(android_obb_dir, MNT_DETACH)) < 0 && errno != EINVAL && + errno != ENOENT) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to umount %s :%s", + android_obb_dir, strerror(errno)); + result = false; + } + return result; +} + +// In each app's namespace, mount tmpfs on obb and data dir, and bind mount obb and data +// package dirs. +static bool remountStorageDirs(int nsFd, const char* android_data_dir, const char* android_obb_dir, + int uid, const char* sources[], const char* targets[], int size) { + // This code is executed after a fork so it's very important that the set of + // methods we call here is strictly limited. + if (setns(nsFd, CLONE_NEWNS) != 0) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to setns %s", strerror(errno)); + return false; + } + + // Mount tmpfs on Android/data and Android/obb + if (TEMP_FAILURE_RETRY(mount("tmpfs", android_data_dir, "tmpfs", + MS_NOSUID | MS_NODEV | MS_NOEXEC, "uid=0,gid=0,mode=0751")) == -1) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount tmpfs to %s :%s", + android_data_dir, strerror(errno)); + return false; + } + if (TEMP_FAILURE_RETRY(mount("tmpfs", android_obb_dir, "tmpfs", + MS_NOSUID | MS_NODEV | MS_NOEXEC, "uid=0,gid=0,mode=0751")) == -1) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount tmpfs to %s :%s", + android_obb_dir, strerror(errno)); + return false; + } + + for (int i = 0; i < size; i++) { + // Create package dir and bind mount it to the actual one. + if (TEMP_FAILURE_RETRY(mkdir(targets[i], 0700)) == -1) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mkdir %s %s", + targets[i], strerror(errno)); + return false; + } + if (TEMP_FAILURE_RETRY(mount(sources[i], targets[i], NULL, MS_BIND | MS_REC, NULL)) == -1) { + async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount %s to %s :%s", + sources[i], targets[i], strerror(errno)); + return false; + } + } + return true; +} + +static std::string getStorageDirSrc(userid_t userId, const std::string& dirName, + const std::string& packageName) { + if (IsSdcardfsUsed()) { + return StringPrintf("/mnt/runtime/default/emulated/%d/%s/%s", + userId, dirName.c_str(), packageName.c_str()); + } else { + return StringPrintf("/mnt/pass_through/%d/emulated/%d/%s/%s", + userId, userId, dirName.c_str(), packageName.c_str()); + } +} + +static std::string getStorageDirTarget(userid_t userId, std::string dirName, + std::string packageName) { + return StringPrintf("/storage/emulated/%d/%s/%s", + userId, dirName.c_str(), packageName.c_str()); +} + +// Fork the process and remount / unmount app data and obb dirs +bool VolumeManager::forkAndRemountStorage(int uid, int pid, bool doUnmount, + const std::vector& packageNames) { + userid_t userId = multiuser_get_user_id(uid); + std::string mnt_path = StringPrintf("/proc/%d/ns/mnt", pid); + android::base::unique_fd nsFd( + TEMP_FAILURE_RETRY(open(mnt_path.c_str(), O_RDONLY | O_CLOEXEC))); + if (nsFd == -1) { + PLOG(ERROR) << "Unable to open " << mnt_path.c_str(); + return false; + } + // Storing both Android/obb and Android/data paths. + int size = packageNames.size() * 2; + + std::unique_ptr sources(new std::string[size]); + std::unique_ptr targets(new std::string[size]); + std::unique_ptr sources_uptr(new const char*[size]); + std::unique_ptr targets_uptr(new const char*[size]); + const char** sources_cstr = sources_uptr.get(); + const char** targets_cstr = targets_uptr.get(); + + for (int i = 0; i < size; i += 2) { + std::string const& packageName = packageNames[i/2]; + sources[i] = getStorageDirSrc(userId, "Android/data", packageName); + targets[i] = getStorageDirTarget(userId, "Android/data", packageName); + sources[i+1] = getStorageDirSrc(userId, "Android/obb", packageName); + targets[i+1] = getStorageDirTarget(userId, "Android/obb", packageName); + + sources_cstr[i] = sources[i].c_str(); + targets_cstr[i] = targets[i].c_str(); + sources_cstr[i+1] = sources[i+1].c_str(); + targets_cstr[i+1] = targets[i+1].c_str(); + } + + for (int i = 0; i < size; i++) { + // Make sure /storage/emulated/... paths are setup correctly + // This needs to be done before EnsureDirExists to ensure Android/ is created. + auto status = setupAppDir(targets_cstr[i], uid, false /* fixupExistingOnly */); + if (status != OK) { + PLOG(ERROR) << "Failed to create dir: " << targets_cstr[i]; + return false; + } + status = EnsureDirExists(sources_cstr[i], 0771, AID_MEDIA_RW, AID_MEDIA_RW); + if (status != OK) { + PLOG(ERROR) << "Failed to create dir: " << sources_cstr[i]; + return false; + } + } + + char android_data_dir[PATH_MAX]; + char android_obb_dir[PATH_MAX]; + snprintf(android_data_dir, PATH_MAX, "/storage/emulated/%d/Android/data", userId); + snprintf(android_obb_dir, PATH_MAX, "/storage/emulated/%d/Android/obb", userId); + + pid_t child; + // Fork a child to mount Android/obb android Android/data dirs, as we don't want it to affect + // original vold process mount namespace. + if (!(child = fork())) { + if (doUnmount) { + if (umountStorageDirs(nsFd, android_data_dir, android_obb_dir, uid, + targets_cstr, size)) { + _exit(0); + } else { + _exit(1); + } + } else { + if (remountStorageDirs(nsFd, android_data_dir, android_obb_dir, uid, + sources_cstr, targets_cstr, size)) { + _exit(0); + } else { + _exit(1); + } + } + } + + if (child == -1) { + PLOG(ERROR) << "Failed to fork"; + return false; + } else { + int status; + if (TEMP_FAILURE_RETRY(waitpid(child, &status, 0)) == -1) { + PLOG(ERROR) << "Failed to waitpid: " << child; + return false; + } + if (!WIFEXITED(status)) { + PLOG(ERROR) << "Process did not exit normally, status: " << status; + return false; + } + if (WEXITSTATUS(status)) { + PLOG(ERROR) << "Process exited with code: " << WEXITSTATUS(status); + return false; + } + } + return true; +} + +int VolumeManager::handleAppStorageDirs(int uid, int pid, + bool doUnmount, const std::vector& packageNames) { + // Only run the remount if fuse is mounted for that user. + userid_t userId = multiuser_get_user_id(uid); + bool fuseMounted = false; + for (auto& vol : mInternalEmulatedVolumes) { + if (vol->getMountUserId() == userId && vol->getState() == VolumeBase::State::kMounted) { + auto* emulatedVol = static_cast(vol.get()); + if (emulatedVol) { + fuseMounted = emulatedVol->isFuseMounted(); + } + break; + } + } + if (fuseMounted) { + forkAndRemountStorage(uid, pid, doUnmount, packageNames); + } + return 0; +} + +int VolumeManager::abortFuse() { + return android::vold::AbortFuseConnections(); +} + +int VolumeManager::reset() { + // Tear down all existing disks/volumes and start from a blank slate so + // newly connected framework hears all events. + for (const auto& vol : mInternalEmulatedVolumes) { + vol->destroy(); + } + mInternalEmulatedVolumes.clear(); + + // Destroy and recreate all disks except that StubVolume disks are just + // destroyed and removed from both mDisks and mPendingDisks. + // StubVolumes are managed from outside Android (e.g. from Chrome OS) and + // their disk recreation on reset events should be handled from outside by + // calling createStubVolume() again. + for (const auto& disk : mDisks) { + disk->destroy(); + if (!disk->isStub()) { + disk->create(); + } + } + const auto isStub = [](const auto& disk) { return disk->isStub(); }; + mDisks.remove_if(isStub); + mPendingDisks.remove_if(isStub); + + updateVirtualDisk(); + mAddedUsers.clear(); + mStartedUsers.clear(); + mSharedStorageUser.clear(); + + // Abort all FUSE connections to avoid deadlocks if the FUSE daemon was killed + // with FUSE fds open. + abortFuse(); + return 0; +} + +// Can be called twice (sequentially) during shutdown. should be safe for that. +int VolumeManager::shutdown() { + if (mInternalEmulatedVolumes.empty()) { + return 0; // already shutdown + } + android::vold::sSleepOnUnmount = false; + for (const auto& vol : mInternalEmulatedVolumes) { + vol->destroy(); + } + for (const auto& disk : mDisks) { + disk->destroy(); + } + + mInternalEmulatedVolumes.clear(); + mDisks.clear(); + mPendingDisks.clear(); + android::vold::sSleepOnUnmount = true; + + return 0; +} + +int VolumeManager::unmountAll() { + std::lock_guard lock(mLock); + ATRACE_NAME("VolumeManager::unmountAll()"); + + // First, try gracefully unmounting all known devices + for (const auto& vol : mInternalEmulatedVolumes) { + vol->unmount(); + } + for (const auto& disk : mDisks) { + disk->unmountAll(); + } + + // Worst case we might have some stale mounts lurking around, so + // force unmount those just to be safe. + FILE* fp = setmntent("/proc/mounts", "re"); + if (fp == NULL) { + PLOG(ERROR) << "Failed to open /proc/mounts"; + return -errno; + } + + // Some volumes can be stacked on each other, so force unmount in + // reverse order to give us the best chance of success. + std::list toUnmount; + mntent* mentry; + while ((mentry = getmntent(fp)) != NULL) { + auto test = std::string(mentry->mnt_dir); + if ((StartsWith(test, "/mnt/") && +#ifdef __ANDROID_DEBUGGABLE__ + !StartsWith(test, "/mnt/scratch") && +#endif + !StartsWith(test, "/mnt/vendor") && !StartsWith(test, "/mnt/product") && + !StartsWith(test, "/mnt/installer") && !StartsWith(test, "/mnt/androidwritable")) || + StartsWith(test, "/storage/")) { + toUnmount.push_front(test); + } + } + endmntent(fp); + + for (const auto& path : toUnmount) { + LOG(DEBUG) << "Tearing down stale mount " << path; + android::vold::ForceUnmount(path); + } + + return 0; +} + +int VolumeManager::ensureAppDirsCreated(const std::vector& paths, int32_t appUid) { + int size = paths.size(); + for (int i = 0; i < size; i++) { + int result = setupAppDir(paths[i], appUid, false /* fixupExistingOnly */, + true /* skipIfDirExists */); + if (result != OK) { + return result; + } + } + return OK; +} + +int VolumeManager::setupAppDir(const std::string& path, int32_t appUid, bool fixupExistingOnly, + bool skipIfDirExists, bool fixupRecursively) { + // Only offer to create directories for paths managed by vold + if (!StartsWith(path, "/storage/")) { + LOG(ERROR) << "Failed to find mounted volume for " << path; + return -EINVAL; + } + + // Find the volume it belongs to + auto filter_fn = [&](const VolumeBase& vol) { + if (vol.getState() != VolumeBase::State::kMounted) { + // The volume must be mounted + return false; + } + if (!vol.isVisibleForWrite()) { + // App dirs should only be created for writable volumes. + return false; + } + if (vol.getInternalPath().empty()) { + return false; + } + if (vol.getMountUserId() != USER_UNKNOWN && + vol.getMountUserId() != multiuser_get_user_id(appUid)) { + // The app dir must be created on a volume with the same user-id + return false; + } + if (!path.empty() && StartsWith(path, vol.getPath())) { + return true; + } + + return false; + }; + auto volume = findVolumeWithFilter(filter_fn); + if (volume == nullptr) { + LOG(ERROR) << "Failed to find mounted volume for " << path; + return -EINVAL; + } + // Convert paths to lower filesystem paths to avoid making FUSE requests for these reasons: + // 1. A FUSE request from vold puts vold at risk of hanging if the FUSE daemon is down + // 2. The FUSE daemon prevents requests on /mnt/user/0/emulated/ and a request + // on /storage/emulated/10 means /mnt/user/0/emulated/10 + const std::string lowerPath = + volume->getInternalPath() + path.substr(volume->getPath().length()); + + const std::string volumeRoot = volume->getRootPath(); // eg /data/media/0 + + const int access_result = access(lowerPath.c_str(), F_OK); + if (fixupExistingOnly && access_result != 0) { + // Nothing to fixup + return OK; + } + + if (skipIfDirExists && access_result == 0) { + // It's safe to assume it's ok as it will be used for zygote to bind mount dir only, + // which the dir doesn't need to have correct permission for now yet. + return OK; + } + + if (volume->getType() == VolumeBase::Type::kPublic) { + // On public volumes, we don't need to setup permissions, as everything goes through + // FUSE; just create the dirs and be done with it. + return fs_mkdirs(lowerPath.c_str(), 0700); + } + + // Create the app paths we need from the root + return PrepareAppDirFromRoot(lowerPath, volumeRoot, appUid, fixupExistingOnly, fixupRecursively); +} + +int VolumeManager::fixupAppDir(const std::string& path, int32_t appUid) { + if (IsSdcardfsUsed()) { + //sdcardfs magically does this for us + return OK; + } + return setupAppDir(path, appUid, true /* fixupExistingOnly */); +} + +int VolumeManager::fixupAppDirRecursively(const std::string& path, int32_t appUid) { + return setupAppDir(path, appUid, true /* fixupExistingOnly */, true /* fixupRecursively */); +} + +static std::string getPackageName(const std::string& path, const std::regex& pattern, int group) { + std::smatch match; + if (!std::regex_search(path, match, pattern)) { + return ""; + } + return match[group]; +} + +static std::string getUpperPath(const std::string& lowerPath) { + if (!StartsWith(lowerPath, "/data/media/")) { + return ""; + } + std::string res; + res = "/storage/emulated/" + lowerPath.substr(std::strlen("/data/media/")); + return res; +} + +static void logUnknownFileType(const std::filesystem::directory_entry& entry) { + const std::string header("Entry neither regular file nor dir. "); + if (entry.is_symlink()) { + LOG(INFO) << header << entry.path() << " is a symbolic link" << std::endl; + } else if (entry.is_block_file()) { + LOG(INFO) << header << entry.path() << " is a block device" << std::endl; + } else if (entry.is_character_file()) { + LOG(INFO) << header << entry.path() << " is a character device" << std::endl; + } else if (entry.is_fifo()) { + LOG(INFO) << header << entry.path() << " is a named IPC pipe" << std::endl; + } else if (entry.is_socket()) { + LOG(INFO) << header << entry.path() << " is a named IPC socket" << std::endl; + } else { + LOG(INFO) << header << entry.path() << " has unknown type" << std::endl; + } +} + +static int fixEntry(const std::string& path, uid_t uid, gid_t gid, mode_t mode) { + int ret = lchown(path.c_str(), uid, gid); + if (ret != 0) { + LOG(ERROR) << "Failed to chown(" << path << ", 0" << std::oct << mode << ")"; + return ret; + } + + ret = chmod(path.c_str(), mode); + if (ret != 0) { + LOG(ERROR) << "Failed to chmod(" << path << ", 0" << std::oct << mode << ")"; + return ret; + } + + return OK; +} + +static int fixDir(const std::string& path, uid_t uid, gid_t gid) { + // 700 + mode_t mode = S_IRWXU; + return fixEntry(path, uid, gid, mode); +} + +static int fixRegularFile(const std::string& path, uid_t uid, gid_t gid) { + // 600 + mode_t mode = S_IRUSR | S_IWUSR; + return fixEntry(path, uid, gid, mode); +} + +static int fixFuseDir(userid_t userId) { + namespace fs = std::filesystem; + using namespace android; + + sp sm = defaultServiceManager(); + if (sm == NULL) { + LOG(ERROR) << "Failed to get service manager"; + return DEAD_OBJECT; + } + + sp binder = sm->getService(String16("package_native")); + sp pm = interface_cast(binder); + int32_t mediaProviderUid; + static const std::string mediaProviderPackageName("com.android.providers.media.module"); + pm->getPackageUid(mediaProviderPackageName, userId, &mediaProviderUid); + + bool debugForceFixup = GetBoolProperty("persist.vold.debug.force_fixup", false); + + std::string lowerPathPrefix("/data/media/"); + std::string parentPath = lowerPathPrefix + std::to_string(userId); + std::vector paths; + for (const auto& itEntry : fs::directory_iterator(parentPath)) { + const std::regex patternLowerPath( + "^/data/media/(?:[0-9]+/)?Android/?" + , std::regex_constants::icase); + if (std::regex_match(itEntry.path().string(), patternLowerPath)) { + continue; + } + struct stat info; + stat(itEntry.path().c_str(), &info); + if (!debugForceFixup && info.st_uid == (uid_t)mediaProviderUid) { + continue; + } + paths.push_back(itEntry.path()); + } + + int ret = 0; + uid_t uid = mediaProviderUid; + gid_t gid = mediaProviderUid; + for (std::string path : paths) { + if (!fs::is_directory(path)) { + ret |= fixRegularFile(path, uid, gid); + continue; + } + ret |= fixDir(path, uid, gid); + for (const auto& itEntry : fs::recursive_directory_iterator(path)) { + if (itEntry.is_directory()) { + ret |= fixDir(itEntry.path(), uid, gid); + } else if (itEntry.is_regular_file()) { + ret |= fixRegularFile(itEntry.path(), uid, gid); + } else { + logUnknownFileType(itEntry); + } + } + } + return ret; +} + +static bool isDataNoMedia(const std::string& path) { + const std::regex patternLowerPath( + "^/data/media/(?:[0-9]+/)?Android/data/.nomedia" + , std::regex_constants::icase); + return std::regex_match(path, patternLowerPath); +} + +static bool isObbNoMedia(const std::string& path) { + const std::regex patternLowerPath( + "^/data/media/(?:[0-9]+/)?Android/obb/.nomedia" + , std::regex_constants::icase); + return std::regex_match(path, patternLowerPath); +} + +static bool isNoMedia(const std::string& path) { + return isDataNoMedia(path) || isObbNoMedia(path); +} + +int VolumeManager::fixDataObbMediaDir(const std::string& parentPath, bool mediaDir, userid_t userId) { + namespace fs = std::filesystem; + using namespace android; + + sp sm = defaultServiceManager(); + if (sm == NULL) { + LOG(ERROR) << "Failed to get service manager"; + return DEAD_OBJECT; + } + + sp binder = sm->getService(String16("package_native")); + sp pm = interface_cast(binder); + + const std::regex patternLowerPath( + "^/data/media/(?:[0-9]+/)?Android/(?:data|obb|media)/([^/]+)/?" + , std::regex_constants::icase); + + bool debugForceFixup = GetBoolProperty("persist.vold.debug.force_fixup", false); + + static const std::string mediaProviderPackageName("com.android.providers.media.module"); + int ret = 0; + for (const auto& itEntry : fs::directory_iterator(parentPath)) { + if (!itEntry.is_directory() && !isNoMedia(itEntry.path().string())) { + LOG(WARNING) << "Encountered a non-dir file, we don't fix: " << itEntry.path(); + continue; + } + struct stat info; + stat(itEntry.path().c_str(), &info); + + std::string packageName = getPackageName(itEntry.path(), patternLowerPath, 1); + int32_t appUid; + if (mediaDir || isNoMedia(itEntry.path())) { + pm->getPackageUid(mediaProviderPackageName, userId, &appUid); + } else { + pm->getPackageUid(packageName, userId, &appUid); + } + if (!debugForceFixup && info.st_uid == (uid_t)appUid) { + continue; + } + if (isDataNoMedia(itEntry.path())) { + // 600 + ret |= fixRegularFile(itEntry.path(), appUid, AID_EXT_DATA_RW); + continue; + } + if (isObbNoMedia(itEntry.path())) { + // 660 + ret |= fixEntry(itEntry.path(), appUid, AID_EXT_OBB_RW, S_IREAD | S_IWRITE | S_IRGRP | S_IWGRP); + continue; + } + + int result = setupAppDir(getUpperPath(itEntry.path()), appUid, true /* fixupExistingOnly */, true /* fixupRecursively */); + ret |= result; + if (result) { + LOG(ERROR) << "Failed to fix " << getUpperPath(itEntry.path()); + } + } + return ret; +} + +int VolumeManager::fixAndroidDir(userid_t userId) { + std::string lowerPathPrefix("/data/media/"); + std::string androidPath("/Android/"); + std::string data("data/"); + std::string obb("obb/"); + std::string media("media/"); + + std::string lowerUserDataPath = lowerPathPrefix + std::to_string(userId) + androidPath + data; + std::string lowerUserObbPath = lowerPathPrefix + std::to_string(userId) + androidPath + obb; + std::string lowerUserMediaPath = lowerPathPrefix + std::to_string(userId) + androidPath + media; + + // /data/media/0/Android/data/ + int ret = fixDataObbMediaDir(lowerUserDataPath, false, userId); + // /data/media/0/Android/obb/ + ret |= fixDataObbMediaDir(lowerUserObbPath, false, userId); + // /data/media/0/Android/media/ + ret |= fixDataObbMediaDir(lowerUserMediaPath, true, userId); + + return ret; +} + +int VolumeManager::fixupAllDirOfUser(int32_t userId) { + int ret = fixFuseDir(userId); + ret |= fixAndroidDir(userId); + + return ret; +} + +int VolumeManager::createObb(const std::string& sourcePath, int32_t ownerGid, + std::string* outVolId) { + int id = mNextObbId++; + + std::string lowerSourcePath; + + // Convert to lower filesystem path + if (StartsWith(sourcePath, "/storage/")) { + auto filter_fn = [&](const VolumeBase& vol) { + if (vol.getState() != VolumeBase::State::kMounted) { + // The volume must be mounted + return false; + } + if (!vol.isVisibleForWrite()) { + // Obb volume should only be created for writable volumes. + return false; + } + if (vol.getInternalPath().empty()) { + return false; + } + if (!sourcePath.empty() && StartsWith(sourcePath, vol.getPath())) { + return true; + } + + return false; + }; + auto volume = findVolumeWithFilter(filter_fn); + if (volume == nullptr) { + LOG(ERROR) << "Failed to find mounted volume for " << sourcePath; + return -EINVAL; + } else { + lowerSourcePath = + volume->getInternalPath() + sourcePath.substr(volume->getPath().length()); + } + } else { + lowerSourcePath = sourcePath; + } + + auto vol = std::shared_ptr( + new android::vold::ObbVolume(id, lowerSourcePath, ownerGid)); + vol->create(); + + mObbVolumes.push_back(vol); + *outVolId = vol->getId(); + return android::OK; +} + +int VolumeManager::destroyObb(const std::string& volId) { + auto i = mObbVolumes.begin(); + while (i != mObbVolumes.end()) { + if ((*i)->getId() == volId) { + (*i)->destroy(); + i = mObbVolumes.erase(i); + } else { + ++i; + } + } + return android::OK; +} + +int VolumeManager::createStubVolume(const std::string& sourcePath, const std::string& mountPath, + const std::string& fsType, const std::string& fsUuid, + const std::string& fsLabel, int32_t flags, + std::string* outVolId) { + dev_t stubId = --mNextStubId; + auto vol = std::shared_ptr( + new android::vold::StubVolume(stubId, sourcePath, mountPath, fsType, fsUuid, fsLabel)); + + int32_t passedFlags = 0; + passedFlags |= (flags & android::vold::Disk::Flags::kUsb); + passedFlags |= (flags & android::vold::Disk::Flags::kSd); + if (flags & android::vold::Disk::Flags::kStubVisible) { + passedFlags |= (flags & android::vold::Disk::Flags::kStubVisible); + } else { + passedFlags |= (flags & android::vold::Disk::Flags::kStubInvisible); + } + // StubDisk doesn't have device node corresponds to it. So, a fake device + // number is used. + auto disk = std::shared_ptr( + new android::vold::Disk("stub", stubId, "stub", passedFlags)); + disk->initializePartition(vol); + handleDiskAdded(disk); + *outVolId = vol->getId(); + return android::OK; +} + +int VolumeManager::destroyStubVolume(const std::string& volId) { + auto tokens = android::base::Split(volId, ":"); + CHECK(tokens.size() == 2); + dev_t stubId; + CHECK(android::base::ParseUint(tokens[1], &stubId)); + handleDiskRemoved(stubId); + return android::OK; +} + +int VolumeManager::mountAppFuse(uid_t uid, int mountId, unique_fd* device_fd) { + return android::vold::MountAppFuse(uid, mountId, device_fd); +} + +int VolumeManager::unmountAppFuse(uid_t uid, int mountId) { + return android::vold::UnmountAppFuse(uid, mountId); +} + +int VolumeManager::openAppFuseFile(uid_t uid, int mountId, int fileId, int flags) { + return android::vold::OpenAppFuseFile(uid, mountId, fileId, flags); +} + +android::status_t android::vold::GetStorageSize(int64_t* storageSize) { + // Start with the /data mount point from fs_mgr + auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT); + if (entry == nullptr) { + LOG(ERROR) << "No mount point entry for " << DATA_MNT_POINT; + return EINVAL; + } + + // Follow any symbolic links + std::string blkDevice = entry->blk_device; + std::string dataDevice; + if (!android::base::Realpath(blkDevice, &dataDevice)) { + dataDevice = blkDevice; + } + + // Handle mapped volumes. + auto& dm = android::dm::DeviceMapper::Instance(); + for (;;) { + auto parent = dm.GetParentBlockDeviceByPath(dataDevice); + if (!parent.has_value()) break; + dataDevice = *parent; + } + + // Get the potential /sys/block entry + std::size_t leaf = dataDevice.rfind('/'); + if (leaf == std::string::npos) { + LOG(ERROR) << "data device " << dataDevice << " is not a path"; + return EINVAL; + } + if (dataDevice.substr(0, leaf) != "/dev/block") { + LOG(ERROR) << "data device " << dataDevice << " is not a block device"; + return EINVAL; + } + std::string sysfs = std::string() + "/sys/block/" + dataDevice.substr(leaf + 1); + + // Look for a directory in /sys/block containing size where the name is a shortened + // version of the name we now have + // Typically we start with something like /sys/block/sda2, and we want /sys/block/sda + // Note that this directory only contains actual disks, not partitions, so this is + // not going to find anything other than the disks + std::string size; + std::string sizeFile; + for (std::string sysfsDir = sysfs;; sysfsDir = sysfsDir.substr(0, sysfsDir.size() - 1)) { + if (sysfsDir.back() == '/') { + LOG(ERROR) << "Could not find valid block device from " << sysfs; + return EINVAL; + } + sizeFile = sysfsDir + "/size"; + if (android::base::ReadFileToString(sizeFile, &size, true)) { + break; + } + } + + // Read the size file and be done + std::stringstream ssSize(size); + ssSize >> *storageSize; + if (ssSize.fail()) { + LOG(ERROR) << sizeFile << " cannot be read as an integer"; + return EINVAL; + } + + *storageSize *= 512; + return OK; +} \ No newline at end of file diff --git a/aosp/system/vold/VolumeManager.h b/aosp/system/vold/VolumeManager.h new file mode 100644 index 0000000000000000000000000000000000000000..4ac4fe80d377a1f831660c4bb9a43c6e4e597472 --- /dev/null +++ b/aosp/system/vold/VolumeManager.h @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_VOLD_VOLUME_MANAGER_H +#define ANDROID_VOLD_VOLUME_MANAGER_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "android/os/IVoldListener.h" + +#include "model/Disk.h" +#include "model/VolumeBase.h" + +class VolumeManager { + private: + static VolumeManager* sInstance; + + bool mDebug; + + public: + virtual ~VolumeManager(); + + // TODO: pipe all requests through VM to avoid exposing this lock + std::mutex& getLock() { return mLock; } + std::mutex& getCryptLock() { return mCryptLock; } + + void setListener(android::sp listener) { mListener = listener; } + android::sp getListener() const { return mListener; } + + int start(); + + void handleBlockEvent(NetlinkEvent* evt); + + class DiskSource { + public: + DiskSource(const std::string& sysPattern, const std::string& nickname, int flags) + : mSysPattern(sysPattern), mNickname(nickname), mFlags(flags) {} + + bool matches(const std::string& sysPath) { + return !fnmatch(mSysPattern.c_str(), sysPath.c_str(), 0); + } + + const std::string& getNickname() const { return mNickname; } + int getFlags() const { return mFlags; } + + private: + std::string mSysPattern; + std::string mNickname; + int mFlags; + }; + + void addDiskSource(const std::shared_ptr& diskSource); + + std::shared_ptr findDisk(const std::string& id); + std::shared_ptr findVolume(const std::string& id); + + template + std::shared_ptr findVolumeWithFilter(Fn fn) { + for (const auto& vol : mInternalEmulatedVolumes) { + if (fn(*vol)) { + return vol; + } + } + for (const auto& disk : mDisks) { + for (const auto& vol : disk->getVolumes()) { + if (fn(*vol)) { + return vol; + } + } + } + + return nullptr; + } + + void listVolumes(android::vold::VolumeBase::Type type, std::list& list) const; + + const std::set& getStartedUsers() const { return mStartedUsers; } + + userid_t getSharedStorageUser(userid_t userId); + + bool forgetPartition(const std::string& partGuid, const std::string& fsUuid); + + int onUserAdded(userid_t userId, int userSerialNumber, userid_t cloneParentUserId); + int onUserRemoved(userid_t userId); + int onUserStarted(userid_t userId); + int onUserStopped(userid_t userId); + + void createPendingDisksIfNeeded(); + int onSecureKeyguardStateChanged(bool isShowing); + + int remountUid(uid_t uid, int32_t remountMode) { return 0; } + int handleAppStorageDirs(int uid, int pid, + bool doUnmount, const std::vector& packageNames); + + /* Aborts all FUSE filesystems, in case the FUSE daemon is no longer up. */ + int abortFuse(); + /* Reset all internal state, typically during framework boot */ + int reset(); + /* Prepare for device shutdown, safely unmounting all devices */ + int shutdown(); + /* Unmount all volumes, usually for encryption */ + int unmountAll(); + + int updateVirtualDisk(); + int setDebug(bool enable); + + bool forkAndRemountStorage(int uid, int pid, bool doUnmount, + const std::vector& packageNames); + + static VolumeManager* Instance(); + + /* + * Creates a directory 'path' for an application, automatically creating + * directories along the given path if they don't exist yet. + * + * Example: + * path = /storage/emulated/0/Android/data/com.foo/files/ + * + * This function will first match the first part of the path with the volume + * root of any known volumes; in this case, "/storage/emulated/0" matches + * with the volume root of the emulated volume for user 0. + * + * The subseqent part of the path must start with one of the well-known + * Android/ data directories, /Android/data, /Android/obb or + * /Android/media. + * + * The final part of the path is application specific. This function will + * create all directories, including the application-specific ones, and + * set the UID of all app-specific directories below the well-known data + * directories to the 'appUid' argument. In the given example, the UID + * of /storage/emulated/0/Android/data/com.foo and + * /storage/emulated/0/Android/data/com.foo/files would be set to 'appUid'. + * + * The UID/GID of the parent directories will be set according to the + * requirements of the underlying filesystem and are of no concern to the + * caller. + * + * If fixupExistingOnly is set, we make sure to fixup any existing dirs and + * files in the passed in path, but only if that path exists; if it doesn't + * exist, this function doesn't create them. + * + * If skipIfDirExists is set, we will not fix any existing dirs, we will + * only create app dirs if it doesn't exist. + * + * Validates that given paths are absolute and that they contain no relative + * "." or ".." paths or symlinks. Last path segment is treated as filename + * and ignored, unless the path ends with "/". Also ensures that path + * belongs to a volume managed by vold. + */ + int setupAppDir(const std::string& path, int32_t appUid, bool fixupExistingOnly = false, + bool skipIfDirExists = false, bool fixupRecursively = false); + + /** + * Fixes up an existing application directory, as if it was created with + * setupAppDir() above. This includes fixing up the UID/GID, permissions and + * project IDs of the contained files and directories. + */ + int fixupAppDir(const std::string& path, int32_t appUid); + + int fixupAppDirRecursively(const std::string& path, int32_t appUid); + + int fixupAllDirOfUser(int32_t userId); + + // Called before zygote starts to ensure dir exists so zygote can bind mount them. + int ensureAppDirsCreated(const std::vector& paths, int32_t appUid); + + int createObb(const std::string& path, int32_t ownerGid, std::string* outVolId); + int destroyObb(const std::string& volId); + + int createStubVolume(const std::string& sourcePath, const std::string& mountPath, + const std::string& fsType, const std::string& fsUuid, + const std::string& fsLabel, int32_t flags, std::string* outVolId); + int destroyStubVolume(const std::string& volId); + + int mountAppFuse(uid_t uid, int mountId, android::base::unique_fd* device_fd); + int unmountAppFuse(uid_t uid, int mountId); + int openAppFuseFile(uid_t uid, int mountId, int fileId, int flags); + + private: + VolumeManager(); + void readInitialState(); + + int linkPrimary(userid_t userId); + + void createEmulatedVolumesForUser(userid_t userId); + void destroyEmulatedVolumesForUser(userid_t userId); + + void handleDiskAdded(const std::shared_ptr& disk); + void handleDiskChanged(dev_t device); + void handleDiskRemoved(dev_t device); + + bool updateFuseMountedProperty(); + + int fixDataObbMediaDir(const std::string& parentPath, bool mediaDir, userid_t userId); + int fixAndroidDir(userid_t userId); + + std::mutex mLock; + std::mutex mCryptLock; + + android::sp mListener; + + std::list> mDiskSources; + std::list> mDisks; + std::list> mPendingDisks; + std::list> mObbVolumes; + std::list> mInternalEmulatedVolumes; + + std::unordered_map mAddedUsers; + // Map of users to a user with which they can share storage (eg clone profiles) + std::unordered_map mSharedStorageUser; + // This needs to be a regular set because we care about the ordering here; + // user 0 should always go first, because it is responsible for sdcardfs. + std::set mStartedUsers; + + std::string mVirtualDiskPath; + std::shared_ptr mVirtualDisk; + std::shared_ptr mPrimary; + + int mNextObbId; + int mNextStubId; + bool mSecureKeyguardShowing; +}; + +namespace android { +namespace vold { +android::status_t GetStorageSize(int64_t* storageSize); +} +} // namespace android + +#endif diff --git a/aosp/system/vold/binder/android/os/IVold.aidl b/aosp/system/vold/binder/android/os/IVold.aidl new file mode 100644 index 0000000000000000000000000000000000000000..4677b3d564a781f162b07585b112e5893e76d37f --- /dev/null +++ b/aosp/system/vold/binder/android/os/IVold.aidl @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.os.incremental.IncrementalFileSystemControlParcel; +import android.os.IVoldListener; +import android.os.IVoldMountCallback; +import android.os.IVoldTaskListener; + +/** {@hide} */ +interface IVold { + void setListener(IVoldListener listener); + + void abortFuse(); + void monitor(); + void reset(); + void shutdown(); + + void onUserAdded(int userId, int userSerial, int sharesStorageWithUserId); + void onUserRemoved(int userId); + void onUserStarted(int userId); + void onUserStopped(int userId); + + void addAppIds(in @utf8InCpp String[] packageNames, in int[] appIds); + void addSandboxIds(in int[] appIds, in @utf8InCpp String[] sandboxIds); + + void onSecureKeyguardStateChanged(boolean isShowing); + + void partition(@utf8InCpp String diskId, int partitionType, int ratio); + void forgetPartition(@utf8InCpp String partGuid, @utf8InCpp String fsUuid); + + void mount(@utf8InCpp String volId, int mountFlags, int mountUserId, + @nullable IVoldMountCallback callback); + void unmount(@utf8InCpp String volId); + void format(@utf8InCpp String volId, @utf8InCpp String fsType); + void benchmark(@utf8InCpp String volId, IVoldTaskListener listener); + + void moveStorage(@utf8InCpp String fromVolId, @utf8InCpp String toVolId, + IVoldTaskListener listener); + + void remountUid(int uid, int remountMode); + void remountAppStorageDirs(int uid, int pid, in @utf8InCpp String[] packageNames); + void unmountAppStorageDirs(int uid, int pid, in @utf8InCpp String[] packageNames); + + void setupAppDir(@utf8InCpp String path, int appUid); + void fixupAppDir(@utf8InCpp String path, int appUid); + void ensureAppDirsCreated(in @utf8InCpp String[] paths, int appUid); + + @utf8InCpp String createObb(@utf8InCpp String sourcePath, int ownerGid); + void destroyObb(@utf8InCpp String volId); + + void fstrim(int fstrimFlags, IVoldTaskListener listener); + void runIdleMaint(boolean needGC, IVoldTaskListener listener); + void abortIdleMaint(IVoldTaskListener listener); + // Returns the amount of storage lifetime used, as a percentage. + // (eg, 10 indicates 10% of lifetime used), or -1 on failure. + int getStorageLifeTime(); + void setGCUrgentPace(int neededSegments, int minSegmentThreshold, + float dirtyReclaimRate, float reclaimWeight, + int gcPeriod, int minGCSleepTime, + int targetDirtyRatio); + void refreshLatestWrite(); + int getWriteAmount(); + + FileDescriptor mountAppFuse(int uid, int mountId); + void unmountAppFuse(int uid, int mountId); + + void fbeEnable(); + + void initUser0(); + void mountFstab(@utf8InCpp String blkDevice, @utf8InCpp String mountPoint, @utf8InCpp String zonedDevice); + void encryptFstab(@utf8InCpp String blkDevice, @utf8InCpp String mountPoint, boolean shouldFormat, @utf8InCpp String fsType, @utf8InCpp String zonedDevice); + + void setStorageBindingSeed(in byte[] seed); + + void createUserStorageKeys(int userId, boolean ephemeral); + void destroyUserStorageKeys(int userId); + + void setCeStorageProtection(int userId, @utf8InCpp String secret); + + int[] getUnlockedUsers(); + void unlockCeStorage(int userId, @utf8InCpp String secret); + void lockCeStorage(int userId); + + void prepareUserStorage(@nullable @utf8InCpp String uuid, int userId, int storageFlags); + void destroyUserStorage(@nullable @utf8InCpp String uuid, int userId, int storageFlags); + + void prepareSandboxForApp(in @utf8InCpp String packageName, int appId, + in @utf8InCpp String sandboxId, int userId); + void destroySandboxForApp(in @utf8InCpp String packageName, + in @utf8InCpp String sandboxId, int userId); + + void startCheckpoint(int retry); + boolean needsCheckpoint(); + boolean needsRollback(); + boolean isCheckpointing(); + void abortChanges(in @utf8InCpp String device, boolean retry); + void commitChanges(); + void prepareCheckpoint(); + void restoreCheckpoint(@utf8InCpp String device); + void restoreCheckpointPart(@utf8InCpp String device, int count); + void markBootAttempt(); + boolean supportsCheckpoint(); + boolean supportsBlockCheckpoint(); + boolean supportsFileCheckpoint(); + void resetCheckpoint(); + + void earlyBootEnded(); + @utf8InCpp String createStubVolume(@utf8InCpp String sourcePath, + @utf8InCpp String mountPath, @utf8InCpp String fsType, + @utf8InCpp String fsUuid, @utf8InCpp String fsLabel, int flags); + void destroyStubVolume(@utf8InCpp String volId); + + FileDescriptor openAppFuseFile(int uid, int mountId, int fileId, int flags); + + boolean incFsEnabled(); + IncrementalFileSystemControlParcel mountIncFs(@utf8InCpp String backingPath, @utf8InCpp String targetDir, int flags, @utf8InCpp String sysfsName); + void unmountIncFs(@utf8InCpp String dir); + void setIncFsMountOptions(in IncrementalFileSystemControlParcel control, boolean enableReadLogs, boolean enableReadTimeouts, @utf8InCpp String sysfsName); + void bindMount(@utf8InCpp String sourceDir, @utf8InCpp String targetDir); + void fixupAppDirRecursively(@utf8InCpp String path, int appUid); + void fixupAllDirOfUser(int userId); + + void destroyDsuMetadataKey(@utf8InCpp String dsuSlot); + + long getStorageSize(); + + // Returns the remaining storage lifetime as a percentage, rounded up as + // needed when the underlying hardware reports low precision. Returns -1 + // on failure. + int getStorageRemainingLifetime(); + + const int FSTRIM_FLAG_DEEP_TRIM = 1; + + const int MOUNT_FLAG_PRIMARY = 1; + const int MOUNT_FLAG_VISIBLE_FOR_READ = 2; + const int MOUNT_FLAG_VISIBLE_FOR_WRITE = 4; + + const int PARTITION_TYPE_PUBLIC = 0; + const int PARTITION_TYPE_PRIVATE = 1; + const int PARTITION_TYPE_MIXED = 2; + + const int STORAGE_FLAG_DE = 1; + const int STORAGE_FLAG_CE = 2; + + const int REMOUNT_MODE_NONE = 0; + const int REMOUNT_MODE_DEFAULT = 1; + const int REMOUNT_MODE_INSTALLER = 2; + const int REMOUNT_MODE_PASS_THROUGH = 3; + const int REMOUNT_MODE_ANDROID_WRITABLE = 4; + + const int VOLUME_STATE_UNMOUNTED = 0; + const int VOLUME_STATE_CHECKING = 1; + const int VOLUME_STATE_MOUNTED = 2; + const int VOLUME_STATE_MOUNTED_READ_ONLY = 3; + const int VOLUME_STATE_FORMATTING = 4; + const int VOLUME_STATE_EJECTING = 5; + const int VOLUME_STATE_UNMOUNTABLE = 6; + const int VOLUME_STATE_REMOVED = 7; + const int VOLUME_STATE_BAD_REMOVAL = 8; + + const int VOLUME_TYPE_PUBLIC = 0; + const int VOLUME_TYPE_PRIVATE = 1; + const int VOLUME_TYPE_EMULATED = 2; + const int VOLUME_TYPE_ASEC = 3; + const int VOLUME_TYPE_OBB = 4; + const int VOLUME_TYPE_STUB = 5; +} diff --git a/aosp/system/vold/tests/Android.bp b/aosp/system/vold/tests/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..0be8aeaf2de256f5407d9ccdce6d4320b3852b00 --- /dev/null +++ b/aosp/system/vold/tests/Android.bp @@ -0,0 +1,47 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_test { + name: "vold_tests", + defaults: [ + "vold_default_flags", + "vold_default_libs", + ], + + srcs: [ + "Utils_test.cpp", + "VoldNativeServiceValidation_test.cpp", + ], + static_libs: ["libvold"], + shared_libs: ["libbinder"] +} + +cc_fuzz { + name: "vold_native_service_fuzzer", + defaults: [ + "vold_default_flags", + "vold_default_libs", + "keystore2_use_latest_aidl_ndk_shared", + "service_fuzzer_defaults", + "fuzzer_disable_leaks" + ], + static_libs: [ + "libvold", + "android.security.maintenance-ndk", + "libkeymint_support", + ], + shared_libs: [ + "packagemanager_aidl-cpp", + ], + header_libs: ["libvold_headers"], + srcs: [ + "VoldFuzzer.cpp", + ], + corpus: ["vold_native_service_fuzzer_corpus/*"], + fuzz_config: { + cc: [ + "maco@google.com", + ], + } +} diff --git a/aosp/system/vold/vdc.cpp b/aosp/system/vold/vdc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9139a12e3eb5bfeb2b668b9e4249668df48fc395 --- /dev/null +++ b/aosp/system/vold/vdc.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "Utils.h" +#include "android/os/IVold.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static void usage(char* progname); + +static android::sp getServiceAggressive() { + android::sp res; + auto sm = android::defaultServiceManager(); + auto name = android::String16("vold"); + for (int i = 0; i < 5000; i++) { + res = sm->checkService(name); + if (res) { + LOG(VERBOSE) << "Waited " << (i * 10) << "ms for vold"; + break; + } + usleep(10000); // 10ms + } + return res; +} + +static void checkStatus(std::vector& cmd, android::binder::Status status) { + if (status.isOk()) return; + std::string command = ::android::base::Join(cmd, " "); + LOG(ERROR) << "Command: " << command << " Failed: " << status.toString8().c_str(); + exit(ENOTTY); +} + +static void bindkeys(std::vector& args, const android::sp& vold) { + std::string raw_bytes; + const char* seed_value; + + seed_value = getenv("SEED_VALUE"); + if (seed_value == NULL) { + LOG(ERROR) << "Empty seed"; + exit(EINVAL); + } + + android::status_t status = android::vold::HexToStr(seed_value, raw_bytes); + if (status != android::OK) { + LOG(ERROR) << "Extraction of seed failed: " << status; + exit(status); + } + + std::vector seed{raw_bytes.begin(), raw_bytes.end()}; + checkStatus(args, vold->setStorageBindingSeed(seed)); +} + +int main(int argc, char** argv) { + setenv("ANDROID_LOG_TAGS", "*:v", 1); + if (getppid() == 1) { + // If init is calling us then it's during boot and we should log to kmsg + android::base::InitLogging(argv, &android::base::KernelLogger); + } else { + android::base::InitLogging(argv, &android::base::StderrLogger); + } + std::vector args(argv + 1, argv + argc); + + if (args.size() > 0 && args[0] == "--wait") { + // Just ignore the --wait flag + args.erase(args.begin()); + } + + if (args.size() < 2) { + usage(argv[0]); + exit(5); + } + android::sp binder = getServiceAggressive(); + if (!binder) { + LOG(ERROR) << "Failed to obtain vold Binder"; + exit(EINVAL); + } + auto vold = android::interface_cast(binder); + + if (args[0] == "cryptfs" && args[1] == "enablefilecrypto") { + checkStatus(args, vold->fbeEnable()); + } else if (args[0] == "cryptfs" && args[1] == "init_user0") { + checkStatus(args, vold->initUser0()); + } else if (args[0] == "volume" && args[1] == "abort_fuse") { + checkStatus(args, vold->abortFuse()); + } else if (args[0] == "volume" && args[1] == "shutdown") { + checkStatus(args, vold->shutdown()); + } else if (args[0] == "volume" && args[1] == "reset") { + checkStatus(args, vold->reset()); + } else if (args[0] == "volume" && args[1] == "getStorageSize") { + int64_t size; + checkStatus(args, vold->getStorageSize(&size)); + LOG(INFO) << size; + } else if (args[0] == "cryptfs" && args[1] == "bindkeys") { + bindkeys(args, vold); + } else if (args[0] == "cryptfs" && args[1] == "mountFstab" && args.size() == 5) { + checkStatus(args, vold->mountFstab(args[2], args[3], args[4])); + } else if (args[0] == "cryptfs" && args[1] == "encryptFstab" && args.size() == 7) { + auto shouldFormat = android::base::ParseBool(args[4]); + if (shouldFormat == android::base::ParseBoolResult::kError) exit(EINVAL); + checkStatus(args, vold->encryptFstab(args[2], args[3], + shouldFormat == android::base::ParseBoolResult::kTrue, + args[5], args[6])); + } else if (args[0] == "checkpoint" && args[1] == "supportsCheckpoint" && args.size() == 2) { + bool supported = false; + checkStatus(args, vold->supportsCheckpoint(&supported)); + return supported ? 1 : 0; + } else if (args[0] == "checkpoint" && args[1] == "supportsBlockCheckpoint" && + args.size() == 2) { + bool supported = false; + checkStatus(args, vold->supportsBlockCheckpoint(&supported)); + return supported ? 1 : 0; + } else if (args[0] == "checkpoint" && args[1] == "supportsFileCheckpoint" && args.size() == 2) { + bool supported = false; + checkStatus(args, vold->supportsFileCheckpoint(&supported)); + return supported ? 1 : 0; + } else if (args[0] == "checkpoint" && args[1] == "startCheckpoint" && args.size() == 3) { + int retry; + if (!android::base::ParseInt(args[2], &retry)) exit(EINVAL); + checkStatus(args, vold->startCheckpoint(retry)); + } else if (args[0] == "checkpoint" && args[1] == "needsCheckpoint" && args.size() == 2) { + bool enabled = false; + checkStatus(args, vold->needsCheckpoint(&enabled)); + return enabled ? 1 : 0; + } else if (args[0] == "checkpoint" && args[1] == "needsRollback" && args.size() == 2) { + bool enabled = false; + checkStatus(args, vold->needsRollback(&enabled)); + return enabled ? 1 : 0; + } else if (args[0] == "checkpoint" && args[1] == "commitChanges" && args.size() == 2) { + checkStatus(args, vold->commitChanges()); + } else if (args[0] == "checkpoint" && args[1] == "prepareCheckpoint" && args.size() == 2) { + checkStatus(args, vold->prepareCheckpoint()); + } else if (args[0] == "checkpoint" && args[1] == "restoreCheckpoint" && args.size() == 3) { + checkStatus(args, vold->restoreCheckpoint(args[2])); + } else if (args[0] == "checkpoint" && args[1] == "restoreCheckpointPart" && args.size() == 4) { + int count; + if (!android::base::ParseInt(args[3], &count)) exit(EINVAL); + checkStatus(args, vold->restoreCheckpointPart(args[2], count)); + } else if (args[0] == "checkpoint" && args[1] == "markBootAttempt" && args.size() == 2) { + checkStatus(args, vold->markBootAttempt()); + } else if (args[0] == "checkpoint" && args[1] == "abortChanges" && args.size() == 4) { + int retry; + if (!android::base::ParseInt(args[2], &retry)) exit(EINVAL); + checkStatus(args, vold->abortChanges(args[2], retry != 0)); + } else if (args[0] == "checkpoint" && args[1] == "resetCheckpoint") { + checkStatus(args, vold->resetCheckpoint()); + } else if (args[0] == "keymaster" && args[1] == "earlyBootEnded") { + checkStatus(args, vold->earlyBootEnded()); + } else if (args[0] == "volume" && args[1] == "fixupAppDir") { + char *stop; + checkStatus(args, vold->fixupAppDir(args[2], std::strtol(args[3].c_str(), &stop, 10))); + } else if (args[0] == "volume" && args[1] == "setupAppDir") { + char *stop; + checkStatus(args, vold->setupAppDir(args[2], std::strtol(args[3].c_str(), &stop, 10))); + } else if (args[0] == "volume" && args[1] == "fixupAppDirRecursively") { + char *stop; + checkStatus(args, vold->fixupAppDirRecursively(args[2], std::strtol(args[3].c_str(), &stop, 10))); + } else if (args[0] == "volume" && args[1] == "fixupAllDirOfUser") { + char *stop; + checkStatus(args, vold->fixupAllDirOfUser(std::strtol(args[2].c_str(), &stop, 10))); + } else { + LOG(ERROR) << "Raw commands are no longer supported"; + exit(EINVAL); + } + return 0; +} + +static void usage(char* progname) { + LOG(INFO) << "Usage: " << progname << " [--wait] [args...]"; +} diff --git a/aosp/vendor/common/prebuild/Android.mk b/aosp/vendor/common/prebuild/Android.mk index a7f568505dcac4ae015b842dafef09bc9019e8cc..374ca8359dbe62c20ba57024f1cf7c83ca755bd9 100644 --- a/aosp/vendor/common/prebuild/Android.mk +++ b/aosp/vendor/common/prebuild/Android.mk @@ -45,7 +45,7 @@ else VA_MULTILIB := first endif - +ifeq ($(CONFIG_ENABLE_CME),y) cme_libs = \ libCPHMediaEngine \ libCaptureEngine \ @@ -68,7 +68,9 @@ $(foreach lib, $(cme_libs), \ system/lib/$(lib).so, \ , \ $(VA_MULTILIB))))) +endif +ifeq ($(CONFIG_ENABLE_HW_AUDIO),y) hal_libs = \ audio.primary.default @@ -79,6 +81,7 @@ $(foreach lib, $(hal_libs), \ system/vendor/lib/hw/$(lib).so, \ hw, \ $(VA_MULTILIB)))) +endif include $(CLEAR_VARS) LOCAL_MODULE := uInput @@ -88,3 +91,10 @@ LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_CHECK_ELF_FILES := false LOCAL_INIT_RC := system/etc/init/uinput.rc include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := appctrl +LOCAL_SRC_FILES := system/bin/appctrl +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_CHECK_ELF_FILES := false +include $(BUILD_PREBUILT) \ No newline at end of file diff --git a/aosp/vendor/common/prebuild/media_engine.mk b/aosp/vendor/common/prebuild/prebuild.mk similarity index 89% rename from aosp/vendor/common/prebuild/media_engine.mk rename to aosp/vendor/common/prebuild/prebuild.mk index d95afffa635b268917341a2931ca18fe8124425a..759bf9b277ca42d69cc088db646a8e084d480982 100644 --- a/aosp/vendor/common/prebuild/media_engine.mk +++ b/aosp/vendor/common/prebuild/prebuild.mk @@ -39,3 +39,9 @@ PRODUCT_PACKAGES += \ libopus.vendor endif +# common +PRODUCT_COPY_FILES += \ + $(CUR_PATH)/system/bin/buildOverlayfs.sh:system/bin/buildOverlayfs.sh + +PRODUCT_PACKAGES += \ + appctrl \ No newline at end of file diff --git a/aosp/vendor/isula/common.mk b/aosp/vendor/isula/common.mk index 42f0a116b8d30d554eb02e6d7693076079dd32a6..0954129fab433dc24c9b24993372a1aaf83681c4 100644 --- a/aosp/vendor/isula/common.mk +++ b/aosp/vendor/isula/common.mk @@ -14,7 +14,7 @@ $(call inherit-product, frameworks/native/build/phone-xhdpi-2048-dalvik-heap.mk) $(call inherit-product, vendor/isula/packages.mk) $(call inherit-product, vendor/isula/properties.mk) $(call inherit-product, vendor/isula/copyfiles.mk) -$(call inherit-product, vendor/common/prebuild/media_engine.mk) +$(call inherit-product, vendor/common/prebuild/prebuild.mk) # product config PRODUCT_AAPT_CONFIG := normal xhdpi diff --git a/aosp/vendor/isula/copyfiles.mk b/aosp/vendor/isula/copyfiles.mk index 984992b9aebe8e653ca1ff06765aa469a7f61c04..959ddf48eae74b1cb2cf78b14a62da4654af61fa 100644 --- a/aosp/vendor/isula/copyfiles.mk +++ b/aosp/vendor/isula/copyfiles.mk @@ -15,6 +15,10 @@ PRODUCT_COPY_FILES += \ PRODUCT_COPY_FILES += \ vendor/isula/media/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml \ vendor/isula/media/primary_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/primary_audio_policy_configuration.xml \ + vendor/isula/media/audio_policy_engine_configuration.xml:system/vendor/etc/audio_policy_engine_configuration.xml \ + vendor/isula/media/audio_policy_engine_default_stream_volumes.xml:system/vendor/etc/audio_policy_engine_default_stream_volumes.xml \ + vendor/isula/media/audio_policy_engine_product_strategies.xml:system/vendor/etc/audio_policy_engine_product_strategies.xml \ + vendor/isula/media/audio_policy_engine_stream_volumes.xml:system/vendor/etc/audio_policy_engine_stream_volumes.xml \ frameworks/av/services/audiopolicy/config/bluetooth_audio_policy_configuration_7_0.xml:$(TARGET_COPY_OUT_VENDOR)/etc/bluetooth_audio_policy_configuration_7_0.xml \ frameworks/av/services/audiopolicy/config/r_submix_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/r_submix_audio_policy_configuration.xml \ frameworks/av/services/audiopolicy/config/audio_policy_volumes.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_volumes.xml \ diff --git a/aosp/vendor/isula/media/audio_policy_engine_configuration.xml b/aosp/vendor/isula/media/audio_policy_engine_configuration.xml new file mode 100644 index 0000000000000000000000000000000000000000..4ca33b4525557375179a06f954d317a1de62bc4e --- /dev/null +++ b/aosp/vendor/isula/media/audio_policy_engine_configuration.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + diff --git a/aosp/vendor/isula/media/audio_policy_engine_default_stream_volumes.xml b/aosp/vendor/isula/media/audio_policy_engine_default_stream_volumes.xml new file mode 100644 index 0000000000000000000000000000000000000000..21e6dd58ac2bdd8578c4e832b47662e6a880a598 --- /dev/null +++ b/aosp/vendor/isula/media/audio_policy_engine_default_stream_volumes.xml @@ -0,0 +1,136 @@ + + + + + + + + 0,0 + 100,0 + + + 0,-9600 + 100,-9600 + + + + 1,-2400 + 33,-1800 + 66,-1200 + 100,-600 + + + + 1,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + 1,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + 1,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + 1,-4680 + 42,-2070 + 85,-540 + 100,0 + + + + 1,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + 1,-5800 + 20,-4000 + 60,-2100 + 100,-1000 + + + + 1,-12700 + 20,-8000 + 60,-4000 + 100,0 + + + + + + + + + 0,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + + 0,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + + 0,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + + 0,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + + 0,-5800 + 20,-4000 + 60,-2100 + 100,-1000 + + + + + 0,-12700 + 20,-8000 + 60,-4000 + 100,0 + + diff --git a/aosp/vendor/isula/media/audio_policy_engine_product_strategies.xml b/aosp/vendor/isula/media/audio_policy_engine_product_strategies.xml new file mode 100644 index 0000000000000000000000000000000000000000..a7388da43662c9fbcf4435f547c6c637303b3b68 --- /dev/null +++ b/aosp/vendor/isula/media/audio_policy_engine_product_strategies.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/aosp/vendor/isula/media/audio_policy_engine_stream_volumes.xml b/aosp/vendor/isula/media/audio_policy_engine_stream_volumes.xml new file mode 100644 index 0000000000000000000000000000000000000000..d5c38968e714e8fd1dbaeb7197347dc7bbcdb999 --- /dev/null +++ b/aosp/vendor/isula/media/audio_policy_engine_stream_volumes.xml @@ -0,0 +1,221 @@ + + + + + + + voice_call + 1 + 7 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + 0,-2700 + 33,-1800 + 66,-900 + 100,0 + + + + + + + system + 0 + 7 + + 1,-3000 + 33,-2600 + 66,-2200 + 100,-1800 + + + 1,-5100 + 57,-2800 + 71,-2500 + 85,-2300 + 100,-2100 + + + + + + + + + ring + 0 + 7 + + + + + + + + + music + 0 + 25 + + + + + + + + + alarm + 1 + 7 + + + + + + + + + notification + 0 + 7 + + + + + + + + + bluetooth_sco + 0 + 15 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + + + + enforced_audible + 0 + 7 + + 1,-3000 + 33,-2600 + 66,-2200 + 100,-1800 + + + 1,-3400 + 71,-2400 + 100,-2000 + + + + + + + + + dtmf + 0 + 15 + + 1,-3000 + 33,-2600 + 66,-2200 + 100,-1800 + + + 1,-4000 + 71,-2400 + 100,-1400 + + + + + + + + + tts + 0 + 15 + + + + + + + + + accessibility + 1 + 15 + + + + + + + + + assistant + 0 + 15 + + + + + + + + + diff --git a/aosp/vendor/isula/packages.mk b/aosp/vendor/isula/packages.mk index 68932268e899713c93c1442d28f8f9a387f2e1a2..5c5056e25a4f5ecbf33cab1a4b52ca03086be5fb 100644 --- a/aosp/vendor/isula/packages.mk +++ b/aosp/vendor/isula/packages.mk @@ -141,4 +141,4 @@ PRODUCT_PACKAGES += \ vslog.cfg \ # tools -PRODUCT_PACKAGES += curl +PRODUCT_PACKAGES += curl \ No newline at end of file diff --git a/aosp/vendor/isula/vagpu/Android.mk b/aosp/vendor/isula/vagpu/Android.mk index 00f61ad66bcd46a014e8287efbc462aac2f7a953..5238f9e538aa828899b657d9973415598eb06c25 100644 --- a/aosp/vendor/isula/vagpu/Android.mk +++ b/aosp/vendor/isula/vagpu/Android.mk @@ -143,7 +143,6 @@ $(foreach lib, $(hw_libs), \ um_libs := \ libVAegl \ -libVAOCL \ libsrv_um \ libusc \ libufwriter \ @@ -180,29 +179,6 @@ $(foreach config, $(va_config), \ , \ $(TARGET_OUT_VENDOR_ETC)))) -# VIDEO - - -# vame - -vame_libs := \ -libfn-log \ -libvacm \ -libvame \ -libvaml \ - -$(foreach lib, $(vame_libs), \ - $(if $(wildcard $(LOCAL_PATH)/system/vendor/lib64/vame/lib/$(lib).so), \ - $(eval $(call install-va-shared-libs, \ - $(lib), \ - system/vendor/lib64/vame/lib/$(lib).so, \ - , \ - vame/lib, \ - )))) - -vame_config := \ -vslog.cfg \ - $(foreach config, $(vame_config), \ $(if $(wildcard $(LOCAL_PATH)/system/vendor/etc/$(config)), \ $(eval $(call install-va-configs, \