1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.packageinstaller; 18 19 import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid; 20 21 import android.Manifest; 22 import android.annotation.Nullable; 23 import android.app.Activity; 24 import android.app.ActivityManager; 25 import android.content.ContentResolver; 26 import android.content.Intent; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.PackageInfo; 29 import android.content.pm.PackageInstaller; 30 import android.content.pm.PackageManager; 31 import android.content.pm.ProviderInfo; 32 import android.net.Uri; 33 import android.os.Build; 34 import android.os.Bundle; 35 import android.os.RemoteException; 36 import android.os.UserManager; 37 import android.util.Log; 38 39 import java.util.Arrays; 40 41 /** 42 * Select which activity is the first visible activity of the installation and forward the intent to 43 * it. 44 */ 45 public class InstallStart extends Activity { 46 private static final String LOG_TAG = InstallStart.class.getSimpleName(); 47 48 private static final String DOWNLOADS_AUTHORITY = "downloads"; 49 private PackageManager mPackageManager; 50 private UserManager mUserManager; 51 private boolean mAbortInstall = false; 52 53 @Override onCreate(@ullable Bundle savedInstanceState)54 protected void onCreate(@Nullable Bundle savedInstanceState) { 55 super.onCreate(savedInstanceState); 56 mPackageManager = getPackageManager(); 57 mUserManager = getSystemService(UserManager.class); 58 Intent intent = getIntent(); 59 String callingPackage = getCallingPackage(); 60 String callingAttributionTag = null; 61 62 final boolean isSessionInstall = 63 PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction()); 64 65 // If the activity was started via a PackageInstaller session, we retrieve the calling 66 // package from that session 67 final int sessionId = (isSessionInstall 68 ? intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1) 69 : -1); 70 if (callingPackage == null && sessionId != -1) { 71 PackageInstaller packageInstaller = getPackageManager().getPackageInstaller(); 72 PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId); 73 callingPackage = (sessionInfo != null) ? sessionInfo.getInstallerPackageName() : null; 74 callingAttributionTag = 75 (sessionInfo != null) ? sessionInfo.getInstallerAttributionTag() : null; 76 } 77 78 final ApplicationInfo sourceInfo = getSourceInfo(callingPackage); 79 final int originatingUid = getOriginatingUid(sourceInfo); 80 boolean isTrustedSource = false; 81 if (sourceInfo != null 82 && (sourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { 83 isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false); 84 } 85 86 if (!isTrustedSource && originatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) { 87 final int targetSdkVersion = getMaxTargetSdkVersionForUid(this, originatingUid); 88 if (targetSdkVersion < 0) { 89 Log.w(LOG_TAG, "Cannot get target sdk version for uid " + originatingUid); 90 // Invalid originating uid supplied. Abort install. 91 mAbortInstall = true; 92 } else if (targetSdkVersion >= Build.VERSION_CODES.O && !isUidRequestingPermission( 93 originatingUid, Manifest.permission.REQUEST_INSTALL_PACKAGES)) { 94 Log.e(LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission " 95 + Manifest.permission.REQUEST_INSTALL_PACKAGES); 96 mAbortInstall = true; 97 } 98 } 99 if (mAbortInstall) { 100 setResult(RESULT_CANCELED); 101 finish(); 102 return; 103 } 104 105 Intent nextActivity = new Intent(intent); 106 nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT 107 | Intent.FLAG_GRANT_READ_URI_PERMISSION); 108 109 // The the installation source as the nextActivity thinks this activity is the source, hence 110 // set the originating UID and sourceInfo explicitly 111 nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage); 112 nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_ATTRIBUTION_TAG, 113 callingAttributionTag); 114 nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo); 115 nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid); 116 117 if (isSessionInstall) { 118 nextActivity.setClass(this, PackageInstallerActivity.class); 119 } else { 120 Uri packageUri = intent.getData(); 121 122 if (packageUri != null && packageUri.getScheme().equals( 123 ContentResolver.SCHEME_CONTENT)) { 124 // [IMPORTANT] This path is deprecated, but should still work. Only necessary 125 // features should be added. 126 127 // Copy file to prevent it from being changed underneath this process 128 nextActivity.setClass(this, InstallStaging.class); 129 } else if (packageUri != null && packageUri.getScheme().equals( 130 PackageInstallerActivity.SCHEME_PACKAGE)) { 131 nextActivity.setClass(this, PackageInstallerActivity.class); 132 } else { 133 Intent result = new Intent(); 134 result.putExtra(Intent.EXTRA_INSTALL_RESULT, 135 PackageManager.INSTALL_FAILED_INVALID_URI); 136 setResult(RESULT_FIRST_USER, result); 137 138 nextActivity = null; 139 } 140 } 141 142 if (nextActivity != null) { 143 startActivity(nextActivity); 144 } 145 finish(); 146 } 147 isUidRequestingPermission(int uid, String permission)148 private boolean isUidRequestingPermission(int uid, String permission) { 149 final String[] packageNames = mPackageManager.getPackagesForUid(uid); 150 if (packageNames == null) { 151 return false; 152 } 153 for (final String packageName : packageNames) { 154 final PackageInfo packageInfo; 155 try { 156 packageInfo = mPackageManager.getPackageInfo(packageName, 157 PackageManager.GET_PERMISSIONS); 158 } catch (PackageManager.NameNotFoundException e) { 159 // Ignore and try the next package 160 continue; 161 } 162 if (packageInfo.requestedPermissions != null 163 && Arrays.asList(packageInfo.requestedPermissions).contains(permission)) { 164 return true; 165 } 166 } 167 return false; 168 } 169 170 /** 171 * @return the ApplicationInfo for the installation source (the calling package), if available 172 */ getSourceInfo(@ullable String callingPackage)173 private ApplicationInfo getSourceInfo(@Nullable String callingPackage) { 174 if (callingPackage != null) { 175 try { 176 return getPackageManager().getApplicationInfo(callingPackage, 0); 177 } catch (PackageManager.NameNotFoundException ex) { 178 // ignore 179 } 180 } 181 return null; 182 } 183 184 /** 185 * Get the originating uid if possible, or 186 * {@link android.content.pm.PackageInstaller.SessionParams#UID_UNKNOWN} if not available 187 * 188 * @param sourceInfo The source of this installation 189 * @return The UID of the installation source or UID_UNKNOWN 190 */ getOriginatingUid(@ullable ApplicationInfo sourceInfo)191 private int getOriginatingUid(@Nullable ApplicationInfo sourceInfo) { 192 // The originating uid from the intent. We only trust/use this if it comes from either 193 // the document manager app or the downloads provider 194 final int uidFromIntent = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID, 195 PackageInstaller.SessionParams.UID_UNKNOWN); 196 197 final int callingUid; 198 if (sourceInfo != null) { 199 callingUid = sourceInfo.uid; 200 } else { 201 try { 202 callingUid = ActivityManager.getService() 203 .getLaunchedFromUid(getActivityToken()); 204 } catch (RemoteException ex) { 205 // Cannot reach ActivityManager. Aborting install. 206 Log.e(LOG_TAG, "Could not determine the launching uid."); 207 mAbortInstall = true; 208 return PackageInstaller.SessionParams.UID_UNKNOWN; 209 } 210 } 211 if (checkPermission(Manifest.permission.MANAGE_DOCUMENTS, -1, callingUid) 212 == PackageManager.PERMISSION_GRANTED) { 213 return uidFromIntent; 214 } 215 if (isSystemDownloadsProvider(callingUid)) { 216 return uidFromIntent; 217 } 218 // We don't trust uid from the intent. Use the calling uid instead. 219 return callingUid; 220 } 221 isSystemDownloadsProvider(int uid)222 private boolean isSystemDownloadsProvider(int uid) { 223 final ProviderInfo downloadProviderPackage = getPackageManager().resolveContentProvider( 224 DOWNLOADS_AUTHORITY, 0); 225 if (downloadProviderPackage == null) { 226 // There seems to be no currently enabled downloads provider on the system. 227 return false; 228 } 229 final ApplicationInfo appInfo = downloadProviderPackage.applicationInfo; 230 return (appInfo.isSystemApp() && uid == appInfo.uid); 231 } 232 } 233