Fix NullPointerException in PIA when installing from Files app
* Added a null check while fetching packages by UID.
* Removed dependency on Intent.EXTRA_ORIGINATING_UID sent by DocumentsManager and DownloadsProvider since they were sent to PIA as INVALID_UID. Instead, PIA uses callingUid to determine if the install request comes from the above mentioned sources to bypass REQUEST_INSTALL_PACKAGES permission check.
* PIA iterates over all packages with a sharedUid to check if any of the packages in sharedUid have access to a target package having a content provider.
Bug: 277074364
Bug: 271500460
Test: adb push file.apk /sdcard/directory
open preinstalled files app and install the apk
Change-Id: Ie2f8d251c558b677759cf4c05ebb016c743c4439
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index ac32020..d0f4e21 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -77,7 +77,21 @@
}
final ApplicationInfo sourceInfo = getSourceInfo(callingPackage);
- final int originatingUid = getOriginatingUid(sourceInfo);
+ // Uid of the source package, coming from ActivityManager
+ int callingUid = getLaunchedFromUid();
+ if (callingUid == Process.INVALID_UID) {
+ // Cannot reach ActivityManager. Aborting install.
+ Log.e(LOG_TAG, "Could not determine the launching uid.");
+ }
+ // Uid of the source package, with a preference to uid from ApplicationInfo
+ final int originatingUid = sourceInfo != null ? sourceInfo.uid : callingUid;
+
+ if (callingUid == Process.INVALID_UID && sourceInfo == null) {
+ mAbortInstall = true;
+ }
+
+ boolean isDocumentsManager = checkPermission(Manifest.permission.MANAGE_DOCUMENTS,
+ -1, callingUid) == PackageManager.PERMISSION_GRANTED;
boolean isTrustedSource = false;
if (sourceInfo != null && sourceInfo.isPrivilegedApp()) {
isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false) || (
@@ -86,7 +100,8 @@
== PackageManager.PERMISSION_GRANTED);
}
- if (!isTrustedSource && originatingUid != Process.INVALID_UID) {
+ if (!isTrustedSource && !isSystemDownloadsProvider(callingUid) && !isDocumentsManager
+ && originatingUid != Process.INVALID_UID) {
final int targetSdkVersion = getMaxTargetSdkVersionForUid(this, originatingUid);
if (targetSdkVersion < 0) {
Log.w(LOG_TAG, "Cannot get target sdk version for uid " + originatingUid);
@@ -144,7 +159,7 @@
if (packageUri != null
&& packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT)
- && canPackageQuery(originatingUid, packageUri)) {
+ && canPackageQuery(callingUid, packageUri)) {
// [IMPORTANT] This path is deprecated, but should still work. Only necessary
// features should be added.
@@ -212,41 +227,6 @@
return null;
}
- /**
- * Get the originating uid if possible, or {@link Process#INVALID_UID} if not available
- *
- * @param sourceInfo The source of this installation
- * @return The UID of the installation source or INVALID_UID
- */
- private int getOriginatingUid(@Nullable ApplicationInfo sourceInfo) {
- // The originating uid from the intent. We only trust/use this if it comes from either
- // the document manager app or the downloads provider
- final int uidFromIntent = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
- Process.INVALID_UID);
-
- final int callingUid;
- if (sourceInfo != null) {
- callingUid = sourceInfo.uid;
- } else {
- callingUid = getLaunchedFromUid();
- if (callingUid == Process.INVALID_UID) {
- // Cannot reach ActivityManager. Aborting install.
- Log.e(LOG_TAG, "Could not determine the launching uid.");
- mAbortInstall = true;
- return Process.INVALID_UID;
- }
- }
- if (checkPermission(Manifest.permission.MANAGE_DOCUMENTS, -1, callingUid)
- == PackageManager.PERMISSION_GRANTED) {
- return uidFromIntent;
- }
- if (isSystemDownloadsProvider(callingUid)) {
- return uidFromIntent;
- }
- // We don't trust uid from the intent. Use the calling uid instead.
- return callingUid;
- }
-
private boolean isSystemDownloadsProvider(int uid) {
final ProviderInfo downloadProviderPackage = getPackageManager().resolveContentProvider(
DOWNLOADS_AUTHORITY, 0);
@@ -260,8 +240,7 @@
}
@NonNull
- private boolean canPackageQuery(int originatingUid, Uri packageUri) {
- String callingPackage = mPackageManager.getPackagesForUid(originatingUid)[0];
+ private boolean canPackageQuery(int callingUid, Uri packageUri) {
ProviderInfo info = mPackageManager.resolveContentProvider(packageUri.getAuthority(),
PackageManager.ComponentInfoFlags.of(0));
if (info == null) {
@@ -269,11 +248,20 @@
}
String targetPackage = info.packageName;
- try {
- return mPackageManager.canPackageQuery(callingPackage, targetPackage);
- } catch (PackageManager.NameNotFoundException e) {
+ String[] callingPackages = mPackageManager.getPackagesForUid(callingUid);
+ if (callingPackages == null) {
return false;
}
+ for (String callingPackage: callingPackages) {
+ try {
+ if (mPackageManager.canPackageQuery(callingPackage, targetPackage)) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // no-op
+ }
+ }
+ return false;
}
private boolean isCallerSessionOwner(int originatingUid, int sessionId) {