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