blob: 6fbee16e3dae382d376df8ce10aaebfd508cd5ad [file] [log] [blame]
Philip P. Moltmannd85d9412018-07-25 11:23:02 -07001/*
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
17package com.android.packageinstaller;
18
19import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
20
21import android.Manifest;
Philip P. Moltmannf325e282018-08-01 09:57:01 -070022import android.annotation.Nullable;
Philip P. Moltmannd85d9412018-07-25 11:23:02 -070023import android.app.Activity;
24import android.app.ActivityManager;
25import android.app.AppGlobals;
26import android.content.ContentResolver;
27import android.content.Intent;
28import android.content.pm.ApplicationInfo;
29import android.content.pm.IPackageManager;
30import android.content.pm.PackageInstaller;
31import android.content.pm.PackageManager;
32import android.content.pm.ProviderInfo;
Evan Severson0461f6f2020-04-15 12:44:39 -070033import android.content.pm.UserInfo;
Philip P. Moltmannd85d9412018-07-25 11:23:02 -070034import android.net.Uri;
35import android.os.Build;
36import android.os.Bundle;
37import android.os.RemoteException;
Evan Severson0461f6f2020-04-15 12:44:39 -070038import android.os.UserManager;
Todd Kennedy8f135982019-07-02 07:35:15 -070039import android.permission.IPermissionManager;
Philip P. Moltmannd85d9412018-07-25 11:23:02 -070040import android.util.Log;
41
Evan Severson0461f6f2020-04-15 12:44:39 -070042import java.util.List;
43
Philip P. Moltmannd85d9412018-07-25 11:23:02 -070044/**
45 * Select which activity is the first visible activity of the installation and forward the intent to
46 * it.
47 */
48public class InstallStart extends Activity {
49 private static final String LOG_TAG = InstallStart.class.getSimpleName();
50
51 private static final String DOWNLOADS_AUTHORITY = "downloads";
52 private IPackageManager mIPackageManager;
Todd Kennedy8f135982019-07-02 07:35:15 -070053 private IPermissionManager mIPermissionManager;
Evan Severson0461f6f2020-04-15 12:44:39 -070054 private UserManager mUserManager;
Philip P. Moltmannd85d9412018-07-25 11:23:02 -070055 private boolean mAbortInstall = false;
56
57 @Override
58 protected void onCreate(@Nullable Bundle savedInstanceState) {
59 super.onCreate(savedInstanceState);
60 mIPackageManager = AppGlobals.getPackageManager();
Todd Kennedy8f135982019-07-02 07:35:15 -070061 mIPermissionManager = AppGlobals.getPermissionManager();
Evan Severson0461f6f2020-04-15 12:44:39 -070062 mUserManager = getSystemService(UserManager.class);
Philip P. Moltmannd85d9412018-07-25 11:23:02 -070063 Intent intent = getIntent();
64 String callingPackage = getCallingPackage();
65
Suprabh Shukla21b13102018-10-10 14:30:08 -070066 final boolean isSessionInstall =
67 PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());
68
Philip P. Moltmannd85d9412018-07-25 11:23:02 -070069 // If the activity was started via a PackageInstaller session, we retrieve the calling
70 // package from that session
Suprabh Shukla21b13102018-10-10 14:30:08 -070071 final int sessionId = (isSessionInstall
72 ? intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1)
73 : -1);
Philip P. Moltmannd85d9412018-07-25 11:23:02 -070074 if (callingPackage == null && sessionId != -1) {
75 PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
76 PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId);
77 callingPackage = (sessionInfo != null) ? sessionInfo.getInstallerPackageName() : null;
78 }
79
80 final ApplicationInfo sourceInfo = getSourceInfo(callingPackage);
81 final int originatingUid = getOriginatingUid(sourceInfo);
82 boolean isTrustedSource = false;
83 if (sourceInfo != null
84 && (sourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
85 isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
86 }
87
88 if (!isTrustedSource && originatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
89 final int targetSdkVersion = getMaxTargetSdkVersionForUid(this, originatingUid);
90 if (targetSdkVersion < 0) {
91 Log.w(LOG_TAG, "Cannot get target sdk version for uid " + originatingUid);
92 // Invalid originating uid supplied. Abort install.
93 mAbortInstall = true;
94 } else if (targetSdkVersion >= Build.VERSION_CODES.O && !declaresAppOpPermission(
95 originatingUid, Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
96 Log.e(LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission "
97 + Manifest.permission.REQUEST_INSTALL_PACKAGES);
98 mAbortInstall = true;
99 }
100 }
101 if (mAbortInstall) {
102 setResult(RESULT_CANCELED);
103 finish();
104 return;
105 }
106
107 Intent nextActivity = new Intent(intent);
108 nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
109
110 // The the installation source as the nextActivity thinks this activity is the source, hence
111 // set the originating UID and sourceInfo explicitly
112 nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
113 nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
114 nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);
115
Suprabh Shukla21b13102018-10-10 14:30:08 -0700116 if (isSessionInstall) {
Philip P. Moltmannd85d9412018-07-25 11:23:02 -0700117 nextActivity.setClass(this, PackageInstallerActivity.class);
118 } else {
119 Uri packageUri = intent.getData();
120
Philip P. Moltmann254c3e4b2019-04-12 09:41:57 -0700121 if (packageUri != null && packageUri.getScheme().equals(
122 ContentResolver.SCHEME_CONTENT)) {
Philip P. Moltmannfe460102018-09-25 10:22:32 -0700123 // [IMPORTANT] This path is deprecated, but should still work. Only necessary
124 // features should be added.
125
Philip P. Moltmannd85d9412018-07-25 11:23:02 -0700126 // Copy file to prevent it from being changed underneath this process
127 nextActivity.setClass(this, InstallStaging.class);
128 } else if (packageUri != null && packageUri.getScheme().equals(
129 PackageInstallerActivity.SCHEME_PACKAGE)) {
130 nextActivity.setClass(this, PackageInstallerActivity.class);
131 } else {
132 Intent result = new Intent();
133 result.putExtra(Intent.EXTRA_INSTALL_RESULT,
134 PackageManager.INSTALL_FAILED_INVALID_URI);
135 setResult(RESULT_FIRST_USER, result);
136
137 nextActivity = null;
138 }
139 }
140
141 if (nextActivity != null) {
142 startActivity(nextActivity);
143 }
144 finish();
145 }
146
147 private boolean declaresAppOpPermission(int uid, String permission) {
148 try {
Todd Kennedy8f135982019-07-02 07:35:15 -0700149 final String[] packages = mIPermissionManager.getAppOpPermissionPackages(permission);
Philip P. Moltmannd85d9412018-07-25 11:23:02 -0700150 if (packages == null) {
151 return false;
152 }
Evan Severson0461f6f2020-04-15 12:44:39 -0700153 final List<UserInfo> users = mUserManager.getUsers();
Philip P. Moltmannd85d9412018-07-25 11:23:02 -0700154 for (String packageName : packages) {
Evan Severson0461f6f2020-04-15 12:44:39 -0700155 for (UserInfo user : users) {
156 try {
157 if (uid == getPackageManager().getPackageUidAsUser(packageName, user.id)) {
158 return true;
159 }
160 } catch (PackageManager.NameNotFoundException e) {
161 // Ignore and try the next package
Philip P. Moltmannd85d9412018-07-25 11:23:02 -0700162 }
Philip P. Moltmannd85d9412018-07-25 11:23:02 -0700163 }
164 }
165 } catch (RemoteException rexc) {
166 // If remote package manager cannot be reached, install will likely fail anyway.
167 }
168 return false;
169 }
170
171 /**
172 * @return the ApplicationInfo for the installation source (the calling package), if available
173 */
174 private ApplicationInfo getSourceInfo(@Nullable String callingPackage) {
175 if (callingPackage != null) {
176 try {
177 return getPackageManager().getApplicationInfo(callingPackage, 0);
178 } catch (PackageManager.NameNotFoundException ex) {
179 // ignore
180 }
181 }
182 return null;
183 }
184
185 /**
186 * Get the originating uid if possible, or
187 * {@link android.content.pm.PackageInstaller.SessionParams#UID_UNKNOWN} if not available
188 *
189 * @param sourceInfo The source of this installation
190 * @return The UID of the installation source or UID_UNKNOWN
191 */
192 private int getOriginatingUid(@Nullable ApplicationInfo sourceInfo) {
193 // The originating uid from the intent. We only trust/use this if it comes from either
194 // the document manager app or the downloads provider
195 final int uidFromIntent = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
196 PackageInstaller.SessionParams.UID_UNKNOWN);
197
198 final int callingUid;
199 if (sourceInfo != null) {
200 callingUid = sourceInfo.uid;
201 } else {
202 try {
203 callingUid = ActivityManager.getService()
204 .getLaunchedFromUid(getActivityToken());
205 } catch (RemoteException ex) {
206 // Cannot reach ActivityManager. Aborting install.
207 Log.e(LOG_TAG, "Could not determine the launching uid.");
208 mAbortInstall = true;
209 return PackageInstaller.SessionParams.UID_UNKNOWN;
210 }
211 }
212 try {
213 if (mIPackageManager.checkUidPermission(Manifest.permission.MANAGE_DOCUMENTS,
214 callingUid) == PackageManager.PERMISSION_GRANTED) {
215 return uidFromIntent;
216 }
217 } catch (RemoteException rexc) {
218 // Ignore. Should not happen.
219 }
220 if (isSystemDownloadsProvider(callingUid)) {
221 return uidFromIntent;
222 }
223 // We don't trust uid from the intent. Use the calling uid instead.
224 return callingUid;
225 }
226
227 private boolean isSystemDownloadsProvider(int uid) {
228 final ProviderInfo downloadProviderPackage = getPackageManager().resolveContentProvider(
229 DOWNLOADS_AUTHORITY, 0);
230 if (downloadProviderPackage == null) {
231 // There seems to be no currently enabled downloads provider on the system.
232 return false;
233 }
234 final ApplicationInfo appInfo = downloadProviderPackage.applicationInfo;
235 return (appInfo.isSystemApp() && uid == appInfo.uid);
236 }
237}