Home | History | Annotate | Download | only in packageinstaller
      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.ContentResolver;
     25 import android.content.Intent;
     26 import android.content.pm.ApplicationInfo;
     27 import android.content.pm.IPackageManager;
     28 import android.content.pm.PackageInstaller;
     29 import android.content.pm.PackageManager;
     30 import android.net.Uri;
     31 import android.os.Build;
     32 import android.os.Bundle;
     33 import android.os.RemoteException;
     34 import android.support.annotation.Nullable;
     35 import android.util.Log;
     36 
     37 import com.android.internal.annotations.VisibleForTesting;
     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 IActivityManager mIActivityManager;
     48     private IPackageManager mIPackageManager;
     49     private boolean mAbortInstall = false;
     50 
     51     @Override
     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 && (packageUri.getScheme().equals(ContentResolver.SCHEME_FILE)
    109                     || packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT))) {
    110                 // Copy file to prevent it from being changed underneath this process
    111                 nextActivity.setClass(this, InstallStaging.class);
    112             } else if (packageUri != null && packageUri.getScheme().equals(
    113                     PackageInstallerActivity.SCHEME_PACKAGE)) {
    114                 nextActivity.setClass(this, PackageInstallerActivity.class);
    115             } else {
    116                 Intent result = new Intent();
    117                 result.putExtra(Intent.EXTRA_INSTALL_RESULT,
    118                         PackageManager.INSTALL_FAILED_INVALID_URI);
    119                 setResult(RESULT_FIRST_USER, result);
    120 
    121                 nextActivity = null;
    122             }
    123         }
    124 
    125         if (nextActivity != null) {
    126             startActivity(nextActivity);
    127         }
    128         finish();
    129     }
    130 
    131     private boolean declaresAppOpPermission(int uid, String permission) {
    132         try {
    133             final String[] packages = mIPackageManager.getAppOpPermissionPackages(permission);
    134             if (packages == null) {
    135                 return false;
    136             }
    137             for (String packageName : packages) {
    138                 try {
    139                     if (uid == getPackageManager().getPackageUid(packageName, 0)) {
    140                         return true;
    141                     }
    142                 } catch (PackageManager.NameNotFoundException e) {
    143                     // Ignore and try the next package
    144                 }
    145             }
    146         } catch (RemoteException rexc) {
    147             // If remote package manager cannot be reached, install will likely fail anyway.
    148         }
    149         return false;
    150     }
    151 
    152     private int getMaxTargetSdkVersionForUid(int uid) {
    153         final String[] packages = getPackageManager().getPackagesForUid(uid);
    154         int targetSdkVersion = -1;
    155         if (packages != null) {
    156             for (String packageName : packages) {
    157                 try {
    158                     ApplicationInfo info = getPackageManager().getApplicationInfo(packageName, 0);
    159                     targetSdkVersion = Math.max(targetSdkVersion, info.targetSdkVersion);
    160                 } catch (PackageManager.NameNotFoundException e) {
    161                     // Ignore and try the next package
    162                 }
    163             }
    164         }
    165         return targetSdkVersion;
    166     }
    167 
    168     /**
    169      * @return the ApplicationInfo for the installation source (the calling package), if available
    170      */
    171     private ApplicationInfo getSourceInfo(@Nullable String callingPackage) {
    172         if (callingPackage != null) {
    173             try {
    174                 return getPackageManager().getApplicationInfo(callingPackage, 0);
    175             } catch (PackageManager.NameNotFoundException ex) {
    176                 // ignore
    177             }
    178         }
    179         return null;
    180     }
    181 
    182     /**
    183      * Get the originating uid if possible, or
    184      * {@link android.content.pm.PackageInstaller.SessionParams#UID_UNKNOWN} if not available
    185      *
    186      * @param sourceInfo The source of this installation
    187      * @return The UID of the installation source or UID_UNKNOWN
    188      */
    189     private int getOriginatingUid(@Nullable ApplicationInfo sourceInfo) {
    190         // The originating uid from the intent. We only trust/use this if it comes from either
    191         // the document manager app or the downloads provider
    192         final int uidFromIntent = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
    193                 PackageInstaller.SessionParams.UID_UNKNOWN);
    194 
    195         final int callingUid;
    196         if (sourceInfo != null) {
    197             callingUid = sourceInfo.uid;
    198         } else {
    199             try {
    200                 callingUid = getIActivityManager()
    201                         .getLaunchedFromUid(getActivityToken());
    202             } catch (RemoteException ex) {
    203                 // Cannot reach ActivityManager. Aborting install.
    204                 Log.e(LOG_TAG, "Could not determine the launching uid.");
    205                 mAbortInstall = true;
    206                 return PackageInstaller.SessionParams.UID_UNKNOWN;
    207             }
    208         }
    209         try {
    210             if (mIPackageManager.checkUidPermission(Manifest.permission.MANAGE_DOCUMENTS,
    211                     callingUid) == PackageManager.PERMISSION_GRANTED) {
    212                 return uidFromIntent;
    213             }
    214         } catch (RemoteException rexc) {
    215             // Ignore. Should not happen.
    216         }
    217         if (isSystemDownloadsProvider(callingUid)) {
    218             return uidFromIntent;
    219         }
    220         // We don't trust uid from the intent. Use the calling uid instead.
    221         return callingUid;
    222     }
    223 
    224     private boolean isSystemDownloadsProvider(int uid) {
    225         final String downloadProviderPackage = getPackageManager().resolveContentProvider(
    226                 DOWNLOADS_AUTHORITY, 0).getComponentName().getPackageName();
    227         if (downloadProviderPackage == null) {
    228             return false;
    229         }
    230         try {
    231             ApplicationInfo applicationInfo = getPackageManager().getApplicationInfo(
    232                     downloadProviderPackage, 0);
    233             return (applicationInfo.isSystemApp() && uid == applicationInfo.uid);
    234         } catch (PackageManager.NameNotFoundException ex) {
    235             return false;
    236         }
    237     }
    238 
    239     private IActivityManager getIActivityManager() {
    240         if (mIActivityManager == null) {
    241             return ActivityManager.getService();
    242         }
    243         return mIActivityManager;
    244     }
    245 
    246     @VisibleForTesting
    247     void injectIActivityManager(IActivityManager iActivityManager) {
    248         mIActivityManager = iActivityManager;
    249     }
    250 }
    251