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