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.annotation.Nullable;
23 import android.app.Activity;
24 import android.app.ActivityManager;
25 import android.app.AppGlobals;
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.content.pm.UserInfo;
34 import android.net.Uri;
35 import android.os.Build;
36 import android.os.Bundle;
37 import android.os.RemoteException;
38 import android.os.UserManager;
39 import android.permission.IPermissionManager;
40 import android.util.Log;
41 
42 import java.util.List;
43 
44 /**
45  * Select which activity is the first visible activity of the installation and forward the intent to
46  * it.
47  */
48 public 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;
53     private IPermissionManager mIPermissionManager;
54     private UserManager mUserManager;
55     private boolean mAbortInstall = false;
56 
57     @Override
onCreate(@ullable Bundle savedInstanceState)58     protected void onCreate(@Nullable Bundle savedInstanceState) {
59         super.onCreate(savedInstanceState);
60         mIPackageManager = AppGlobals.getPackageManager();
61         mIPermissionManager = AppGlobals.getPermissionManager();
62         mUserManager = getSystemService(UserManager.class);
63         Intent intent = getIntent();
64         String callingPackage = getCallingPackage();
65 
66         final boolean isSessionInstall =
67                 PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());
68 
69         // If the activity was started via a PackageInstaller session, we retrieve the calling
70         // package from that session
71         final int sessionId = (isSessionInstall
72                 ? intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1)
73                 : -1);
74         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 
116         if (isSessionInstall) {
117             nextActivity.setClass(this, PackageInstallerActivity.class);
118         } else {
119             Uri packageUri = intent.getData();
120 
121             if (packageUri != null && packageUri.getScheme().equals(
122                     ContentResolver.SCHEME_CONTENT)) {
123                 // [IMPORTANT] This path is deprecated, but should still work. Only necessary
124                 // features should be added.
125 
126                 // 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 
declaresAppOpPermission(int uid, String permission)147     private boolean declaresAppOpPermission(int uid, String permission) {
148         try {
149             final String[] packages = mIPermissionManager.getAppOpPermissionPackages(permission);
150             if (packages == null) {
151                 return false;
152             }
153             final List<UserInfo> users = mUserManager.getUsers();
154             for (String packageName : packages) {
155                 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
162                     }
163                 }
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      */
getSourceInfo(@ullable String callingPackage)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      */
getOriginatingUid(@ullable ApplicationInfo sourceInfo)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 
isSystemDownloadsProvider(int uid)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 }
238