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