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