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