1 /*
2  * Copyright (C) 2023 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 android.Manifest.permission;
20 import static android.content.pm.PackageManager.GET_PERMISSIONS;
21 import static android.content.pm.PackageManager.MATCH_ARCHIVED_PACKAGES;
22 
23 import android.app.Activity;
24 import android.app.DialogFragment;
25 import android.app.Fragment;
26 import android.app.FragmentTransaction;
27 import android.content.IntentSender;
28 import android.content.pm.InstallSourceInfo;
29 import android.content.pm.PackageInstaller;
30 import android.content.pm.PackageManager;
31 import android.os.Bundle;
32 import android.os.Process;
33 import android.text.TextUtils;
34 import android.util.Log;
35 
36 import androidx.annotation.NonNull;
37 
38 import java.io.IOException;
39 import java.util.Arrays;
40 import java.util.Objects;
41 
42 public class UnarchiveActivity extends Activity {
43 
44     public static final String EXTRA_UNARCHIVE_INTENT_SENDER =
45             "android.content.pm.extra.UNARCHIVE_INTENT_SENDER";
46     static final String APP_TITLE = "com.android.packageinstaller.unarchive.app_title";
47     static final String INSTALLER_TITLE = "com.android.packageinstaller.unarchive.installer_title";
48 
49     private static final String TAG = "UnarchiveActivity";
50 
51     private String mPackageName;
52     private IntentSender mIntentSender;
53 
54     @Override
onCreate(Bundle icicle)55     public void onCreate(Bundle icicle) {
56         super.onCreate(null);
57 
58         int callingUid = getLaunchedFromUid();
59         if (callingUid == Process.INVALID_UID) {
60             // Cannot reach Package/ActivityManager. Aborting uninstall.
61             Log.e(TAG, "Could not determine the launching uid.");
62 
63             setResult(Activity.RESULT_FIRST_USER);
64             finish();
65             return;
66         }
67 
68         String callingPackage = getPackageNameForUid(callingUid);
69         if (callingPackage == null) {
70             Log.e(TAG, "Package not found for originating uid " + callingUid);
71             setResult(Activity.RESULT_FIRST_USER);
72             finish();
73             return;
74         }
75 
76         // We don't check the AppOpsManager here for REQUEST_INSTALL_PACKAGES because the requester
77         // is not the source of the installation.
78         boolean hasRequestInstallPermission = Arrays.asList(getRequestedPermissions(callingPackage))
79                 .contains(permission.REQUEST_INSTALL_PACKAGES);
80         boolean hasInstallPermission = getBaseContext().checkPermission(permission.INSTALL_PACKAGES,
81                 0 /* random value for pid */, callingUid) == PackageManager.PERMISSION_GRANTED;
82         if (!hasRequestInstallPermission && !hasInstallPermission) {
83             Log.e(TAG, "Uid " + callingUid + " does not have "
84                     + permission.REQUEST_INSTALL_PACKAGES + " or "
85                     + permission.INSTALL_PACKAGES);
86             setResult(Activity.RESULT_FIRST_USER);
87             finish();
88             return;
89         }
90 
91         Bundle extras = getIntent().getExtras();
92         mPackageName = extras.getString(PackageInstaller.EXTRA_PACKAGE_NAME);
93         mIntentSender = extras.getParcelable(EXTRA_UNARCHIVE_INTENT_SENDER, IntentSender.class);
94         Objects.requireNonNull(mPackageName);
95         Objects.requireNonNull(mIntentSender);
96 
97         PackageManager pm = getPackageManager();
98         try {
99             String appTitle = pm.getApplicationInfo(mPackageName,
100                     PackageManager.ApplicationInfoFlags.of(
101                             MATCH_ARCHIVED_PACKAGES)).loadLabel(pm).toString();
102             String installerTitle = getResponsibleInstallerTitle(pm,
103                     pm.getInstallSourceInfo(mPackageName));
104             showDialogFragment(appTitle, installerTitle);
105         } catch (PackageManager.NameNotFoundException e) {
106             Log.e(TAG, "Invalid packageName: " + e.getMessage());
107         }
108     }
109 
getResponsibleInstallerTitle(PackageManager pm, InstallSourceInfo installSource)110     private String getResponsibleInstallerTitle(PackageManager pm,
111             InstallSourceInfo installSource)
112             throws PackageManager.NameNotFoundException {
113         String packageName = TextUtils.isEmpty(installSource.getUpdateOwnerPackageName())
114                 ? installSource.getInstallingPackageName()
115                 : installSource.getUpdateOwnerPackageName();
116         if (packageName == null) {
117             // Should be unreachable.
118             Log.e(TAG, "Installer not found.");
119             setResult(Activity.RESULT_FIRST_USER);
120             finish();
121             return "";
122         }
123         return pm.getApplicationInfo(packageName, /* flags= */ 0).loadLabel(pm).toString();
124     }
125 
126     @NonNull
getRequestedPermissions(String callingPackage)127     private String[] getRequestedPermissions(String callingPackage) {
128         String[] requestedPermissions = null;
129         try {
130             requestedPermissions = getPackageManager()
131                     .getPackageInfo(callingPackage, GET_PERMISSIONS).requestedPermissions;
132         } catch (PackageManager.NameNotFoundException e) {
133             // Should be unreachable because we've just fetched the packageName above.
134             Log.e(TAG, "Package not found for " + callingPackage);
135         }
136         return requestedPermissions == null ? new String[]{} : requestedPermissions;
137     }
138 
startUnarchive()139     void startUnarchive() {
140         try {
141             getPackageManager().getPackageInstaller().requestUnarchive(mPackageName, mIntentSender);
142         } catch (PackageManager.NameNotFoundException | IOException e) {
143             Log.e(TAG, "RequestUnarchive failed with %s." + e.getMessage());
144         }
145     }
146 
showDialogFragment(String appTitle, String installerAppTitle)147     private void showDialogFragment(String appTitle, String installerAppTitle) {
148         FragmentTransaction ft = getFragmentManager().beginTransaction();
149         Fragment prev = getFragmentManager().findFragmentByTag("dialog");
150         if (prev != null) {
151             ft.remove(prev);
152         }
153 
154         Bundle args = new Bundle();
155         args.putString(APP_TITLE, appTitle);
156         args.putString(INSTALLER_TITLE, installerAppTitle);
157         DialogFragment fragment = new UnarchiveFragment();
158         fragment.setArguments(args);
159         fragment.show(ft, "dialog");
160     }
161 
getPackageNameForUid(int sourceUid)162     private String getPackageNameForUid(int sourceUid) {
163         String[] packagesForUid = getPackageManager().getPackagesForUid(sourceUid);
164         if (packagesForUid == null) {
165             return null;
166         }
167         return packagesForUid[0];
168     }
169 }
170