• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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