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.app.AppGlobals; 26 import android.content.ContentResolver; 27 import android.content.Intent; 28 import android.content.pm.ApplicationInfo; 29 import android.content.pm.IPackageManager; 30 import android.content.pm.PackageInstaller; 31 import android.content.pm.PackageManager; 32 import android.content.pm.ProviderInfo; 33 import android.content.pm.UserInfo; 34 import android.net.Uri; 35 import android.os.Build; 36 import android.os.Bundle; 37 import android.os.RemoteException; 38 import android.os.UserManager; 39 import android.permission.IPermissionManager; 40 import android.util.Log; 41 42 import java.util.List; 43 44 /** 45 * Select which activity is the first visible activity of the installation and forward the intent to 46 * it. 47 */ 48 public class InstallStart extends Activity { 49 private static final String LOG_TAG = InstallStart.class.getSimpleName(); 50 51 private static final String DOWNLOADS_AUTHORITY = "downloads"; 52 private IPackageManager mIPackageManager; 53 private IPermissionManager mIPermissionManager; 54 private UserManager mUserManager; 55 private boolean mAbortInstall = false; 56 57 @Override onCreate(@ullable Bundle savedInstanceState)58 protected void onCreate(@Nullable Bundle savedInstanceState) { 59 super.onCreate(savedInstanceState); 60 mIPackageManager = AppGlobals.getPackageManager(); 61 mIPermissionManager = AppGlobals.getPermissionManager(); 62 mUserManager = getSystemService(UserManager.class); 63 Intent intent = getIntent(); 64 String callingPackage = getCallingPackage(); 65 66 final boolean isSessionInstall = 67 PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction()); 68 69 // If the activity was started via a PackageInstaller session, we retrieve the calling 70 // package from that session 71 final int sessionId = (isSessionInstall 72 ? intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1) 73 : -1); 74 if (callingPackage == null && sessionId != -1) { 75 PackageInstaller packageInstaller = getPackageManager().getPackageInstaller(); 76 PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId); 77 callingPackage = (sessionInfo != null) ? sessionInfo.getInstallerPackageName() : null; 78 } 79 80 final ApplicationInfo sourceInfo = getSourceInfo(callingPackage); 81 final int originatingUid = getOriginatingUid(sourceInfo); 82 boolean isTrustedSource = false; 83 if (sourceInfo != null 84 && (sourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { 85 isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false); 86 } 87 88 if (!isTrustedSource && originatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) { 89 final int targetSdkVersion = getMaxTargetSdkVersionForUid(this, originatingUid); 90 if (targetSdkVersion < 0) { 91 Log.w(LOG_TAG, "Cannot get target sdk version for uid " + originatingUid); 92 // Invalid originating uid supplied. Abort install. 93 mAbortInstall = true; 94 } else if (targetSdkVersion >= Build.VERSION_CODES.O && !declaresAppOpPermission( 95 originatingUid, Manifest.permission.REQUEST_INSTALL_PACKAGES)) { 96 Log.e(LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission " 97 + Manifest.permission.REQUEST_INSTALL_PACKAGES); 98 mAbortInstall = true; 99 } 100 } 101 if (mAbortInstall) { 102 setResult(RESULT_CANCELED); 103 finish(); 104 return; 105 } 106 107 Intent nextActivity = new Intent(intent); 108 nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); 109 110 // The the installation source as the nextActivity thinks this activity is the source, hence 111 // set the originating UID and sourceInfo explicitly 112 nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage); 113 nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo); 114 nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid); 115 116 if (isSessionInstall) { 117 nextActivity.setClass(this, PackageInstallerActivity.class); 118 } else { 119 Uri packageUri = intent.getData(); 120 121 if (packageUri != null && packageUri.getScheme().equals( 122 ContentResolver.SCHEME_CONTENT)) { 123 // [IMPORTANT] This path is deprecated, but should still work. Only necessary 124 // features should be added. 125 126 // Copy file to prevent it from being changed underneath this process 127 nextActivity.setClass(this, InstallStaging.class); 128 } else if (packageUri != null && packageUri.getScheme().equals( 129 PackageInstallerActivity.SCHEME_PACKAGE)) { 130 nextActivity.setClass(this, PackageInstallerActivity.class); 131 } else { 132 Intent result = new Intent(); 133 result.putExtra(Intent.EXTRA_INSTALL_RESULT, 134 PackageManager.INSTALL_FAILED_INVALID_URI); 135 setResult(RESULT_FIRST_USER, result); 136 137 nextActivity = null; 138 } 139 } 140 141 if (nextActivity != null) { 142 startActivity(nextActivity); 143 } 144 finish(); 145 } 146 declaresAppOpPermission(int uid, String permission)147 private boolean declaresAppOpPermission(int uid, String permission) { 148 try { 149 final String[] packages = mIPermissionManager.getAppOpPermissionPackages(permission); 150 if (packages == null) { 151 return false; 152 } 153 final List<UserInfo> users = mUserManager.getUsers(); 154 for (String packageName : packages) { 155 for (UserInfo user : users) { 156 try { 157 if (uid == getPackageManager().getPackageUidAsUser(packageName, user.id)) { 158 return true; 159 } 160 } catch (PackageManager.NameNotFoundException e) { 161 // Ignore and try the next package 162 } 163 } 164 } 165 } catch (RemoteException rexc) { 166 // If remote package manager cannot be reached, install will likely fail anyway. 167 } 168 return false; 169 } 170 171 /** 172 * @return the ApplicationInfo for the installation source (the calling package), if available 173 */ getSourceInfo(@ullable String callingPackage)174 private ApplicationInfo getSourceInfo(@Nullable String callingPackage) { 175 if (callingPackage != null) { 176 try { 177 return getPackageManager().getApplicationInfo(callingPackage, 0); 178 } catch (PackageManager.NameNotFoundException ex) { 179 // ignore 180 } 181 } 182 return null; 183 } 184 185 /** 186 * Get the originating uid if possible, or 187 * {@link android.content.pm.PackageInstaller.SessionParams#UID_UNKNOWN} if not available 188 * 189 * @param sourceInfo The source of this installation 190 * @return The UID of the installation source or UID_UNKNOWN 191 */ getOriginatingUid(@ullable ApplicationInfo sourceInfo)192 private int getOriginatingUid(@Nullable ApplicationInfo sourceInfo) { 193 // The originating uid from the intent. We only trust/use this if it comes from either 194 // the document manager app or the downloads provider 195 final int uidFromIntent = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID, 196 PackageInstaller.SessionParams.UID_UNKNOWN); 197 198 final int callingUid; 199 if (sourceInfo != null) { 200 callingUid = sourceInfo.uid; 201 } else { 202 try { 203 callingUid = ActivityManager.getService() 204 .getLaunchedFromUid(getActivityToken()); 205 } catch (RemoteException ex) { 206 // Cannot reach ActivityManager. Aborting install. 207 Log.e(LOG_TAG, "Could not determine the launching uid."); 208 mAbortInstall = true; 209 return PackageInstaller.SessionParams.UID_UNKNOWN; 210 } 211 } 212 try { 213 if (mIPackageManager.checkUidPermission(Manifest.permission.MANAGE_DOCUMENTS, 214 callingUid) == PackageManager.PERMISSION_GRANTED) { 215 return uidFromIntent; 216 } 217 } catch (RemoteException rexc) { 218 // Ignore. Should not happen. 219 } 220 if (isSystemDownloadsProvider(callingUid)) { 221 return uidFromIntent; 222 } 223 // We don't trust uid from the intent. Use the calling uid instead. 224 return callingUid; 225 } 226 isSystemDownloadsProvider(int uid)227 private boolean isSystemDownloadsProvider(int uid) { 228 final ProviderInfo downloadProviderPackage = getPackageManager().resolveContentProvider( 229 DOWNLOADS_AUTHORITY, 0); 230 if (downloadProviderPackage == null) { 231 // There seems to be no currently enabled downloads provider on the system. 232 return false; 233 } 234 final ApplicationInfo appInfo = downloadProviderPackage.applicationInfo; 235 return (appInfo.isSystemApp() && uid == appInfo.uid); 236 } 237 } 238