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