1 /* 2 ** 3 ** Copyright 2007, The Android Open Source Project 4 ** 5 ** Licensed under the Apache License, Version 2.0 (the "License"); 6 ** you may not use this file except in compliance with the License. 7 ** You may obtain a copy of the License at 8 ** 9 ** http://www.apache.org/licenses/LICENSE-2.0 10 ** 11 ** Unless required by applicable law or agreed to in writing, software 12 ** distributed under the License is distributed on an "AS IS" BASIS, 13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 ** See the License for the specific language governing permissions and 15 ** limitations under the License. 16 */ 17 package com.android.packageinstaller; 18 19 import static android.app.AppOpsManager.MODE_ALLOWED; 20 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; 21 22 import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid; 23 24 import android.Manifest; 25 import android.annotation.NonNull; 26 import android.annotation.StringRes; 27 import android.app.Activity; 28 import android.app.ActivityManager; 29 import android.app.ActivityThread; 30 import android.app.AppGlobals; 31 import android.app.AppOpsManager; 32 import android.app.DialogFragment; 33 import android.app.Fragment; 34 import android.app.FragmentTransaction; 35 import android.app.Notification; 36 import android.app.NotificationChannel; 37 import android.app.NotificationManager; 38 import android.app.PendingIntent; 39 import android.content.ComponentName; 40 import android.content.Context; 41 import android.content.Intent; 42 import android.content.pm.ActivityInfo; 43 import android.content.pm.ApplicationInfo; 44 import android.content.pm.IPackageDeleteObserver2; 45 import android.content.pm.IPackageManager; 46 import android.content.pm.PackageInstaller; 47 import android.content.pm.PackageManager; 48 import android.content.pm.VersionedPackage; 49 import android.content.res.Configuration; 50 import android.net.Uri; 51 import android.os.Build; 52 import android.os.Bundle; 53 import android.os.IBinder; 54 import android.os.RemoteException; 55 import android.os.ServiceManager; 56 import android.os.UserHandle; 57 import android.os.UserManager; 58 import android.util.Log; 59 60 import com.android.packageinstaller.handheld.ErrorDialogFragment; 61 import com.android.packageinstaller.handheld.UninstallAlertDialogFragment; 62 import com.android.packageinstaller.television.ErrorFragment; 63 import com.android.packageinstaller.television.UninstallAlertFragment; 64 import com.android.packageinstaller.television.UninstallAppProgress; 65 66 import java.util.List; 67 68 /* 69 * This activity presents UI to uninstall an application. Usually launched with intent 70 * Intent.ACTION_UNINSTALL_PKG_COMMAND and attribute 71 * com.android.packageinstaller.PackageName set to the application package name 72 */ 73 public class UninstallerActivity extends Activity { 74 private static final String TAG = "UninstallerActivity"; 75 76 private static final String UNINSTALLING_CHANNEL = "uninstalling"; 77 78 public static class DialogInfo { 79 public ApplicationInfo appInfo; 80 public ActivityInfo activityInfo; 81 public boolean allUsers; 82 public UserHandle user; 83 public IBinder callback; 84 } 85 86 private String mPackageName; 87 private DialogInfo mDialogInfo; 88 89 @Override onCreate(Bundle icicle)90 public void onCreate(Bundle icicle) { 91 getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); 92 93 // Never restore any state, esp. never create any fragments. The data in the fragment might 94 // be stale, if e.g. the app was uninstalled while the activity was destroyed. 95 super.onCreate(null); 96 97 try { 98 int callingUid = ActivityManager.getService().getLaunchedFromUid(getActivityToken()); 99 100 String callingPackage = getPackageNameForUid(callingUid); 101 if (callingPackage == null) { 102 Log.e(TAG, "Package not found for originating uid " + callingUid); 103 setResult(Activity.RESULT_FIRST_USER); 104 finish(); 105 return; 106 } else { 107 AppOpsManager appOpsManager = (AppOpsManager) getSystemService( 108 Context.APP_OPS_SERVICE); 109 if (appOpsManager.noteOpNoThrow( 110 AppOpsManager.OPSTR_REQUEST_DELETE_PACKAGES, callingUid, callingPackage) 111 != MODE_ALLOWED) { 112 Log.e(TAG, "Install from uid " + callingUid + " disallowed by AppOps"); 113 setResult(Activity.RESULT_FIRST_USER); 114 finish(); 115 return; 116 } 117 } 118 119 if (getMaxTargetSdkVersionForUid(this, callingUid) 120 >= Build.VERSION_CODES.P && AppGlobals.getPackageManager().checkUidPermission( 121 Manifest.permission.REQUEST_DELETE_PACKAGES, callingUid) 122 != PackageManager.PERMISSION_GRANTED 123 && AppGlobals.getPackageManager().checkUidPermission( 124 Manifest.permission.DELETE_PACKAGES, callingUid) 125 != PackageManager.PERMISSION_GRANTED) { 126 Log.e(TAG, "Uid " + callingUid + " does not have " 127 + Manifest.permission.REQUEST_DELETE_PACKAGES + " or " 128 + Manifest.permission.DELETE_PACKAGES); 129 130 setResult(Activity.RESULT_FIRST_USER); 131 finish(); 132 return; 133 } 134 } catch (RemoteException ex) { 135 // Cannot reach Package/ActivityManager. Aborting uninstall. 136 Log.e(TAG, "Could not determine the launching uid."); 137 138 setResult(Activity.RESULT_FIRST_USER); 139 finish(); 140 return; 141 } 142 143 // Get intent information. 144 // We expect an intent with URI of the form package://<packageName>#<className> 145 // className is optional; if specified, it is the activity the user chose to uninstall 146 final Intent intent = getIntent(); 147 final Uri packageUri = intent.getData(); 148 if (packageUri == null) { 149 Log.e(TAG, "No package URI in intent"); 150 showAppNotFound(); 151 return; 152 } 153 mPackageName = packageUri.getEncodedSchemeSpecificPart(); 154 if (mPackageName == null) { 155 Log.e(TAG, "Invalid package name in URI: " + packageUri); 156 showAppNotFound(); 157 return; 158 } 159 160 final IPackageManager pm = IPackageManager.Stub.asInterface( 161 ServiceManager.getService("package")); 162 163 mDialogInfo = new DialogInfo(); 164 165 mDialogInfo.allUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false); 166 if (mDialogInfo.allUsers && !UserManager.get(this).isAdminUser()) { 167 Log.e(TAG, "Only admin user can request uninstall for all users"); 168 showUserIsNotAllowed(); 169 return; 170 } 171 mDialogInfo.user = intent.getParcelableExtra(Intent.EXTRA_USER); 172 if (mDialogInfo.user == null) { 173 mDialogInfo.user = android.os.Process.myUserHandle(); 174 } else { 175 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); 176 List<UserHandle> profiles = userManager.getUserProfiles(); 177 if (!profiles.contains(mDialogInfo.user)) { 178 Log.e(TAG, "User " + android.os.Process.myUserHandle() + " can't request uninstall " 179 + "for user " + mDialogInfo.user); 180 showUserIsNotAllowed(); 181 return; 182 } 183 } 184 185 mDialogInfo.callback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK); 186 187 try { 188 mDialogInfo.appInfo = pm.getApplicationInfo(mPackageName, 189 PackageManager.MATCH_ANY_USER, mDialogInfo.user.getIdentifier()); 190 } catch (RemoteException e) { 191 Log.e(TAG, "Unable to get packageName. Package manager is dead?"); 192 } 193 194 if (mDialogInfo.appInfo == null) { 195 Log.e(TAG, "Invalid packageName: " + mPackageName); 196 showAppNotFound(); 197 return; 198 } 199 200 // The class name may have been specified (e.g. when deleting an app from all apps) 201 final String className = packageUri.getFragment(); 202 if (className != null) { 203 try { 204 mDialogInfo.activityInfo = pm.getActivityInfo( 205 new ComponentName(mPackageName, className), 0, 206 mDialogInfo.user.getIdentifier()); 207 } catch (RemoteException e) { 208 Log.e(TAG, "Unable to get className. Package manager is dead?"); 209 // Continue as the ActivityInfo isn't critical. 210 } 211 } 212 213 showConfirmationDialog(); 214 } 215 getDialogInfo()216 public DialogInfo getDialogInfo() { 217 return mDialogInfo; 218 } 219 showConfirmationDialog()220 private void showConfirmationDialog() { 221 if (isTv()) { 222 showContentFragment(new UninstallAlertFragment(), 0, 0); 223 } else { 224 showDialogFragment(new UninstallAlertDialogFragment(), 0, 0); 225 } 226 } 227 showAppNotFound()228 private void showAppNotFound() { 229 if (isTv()) { 230 showContentFragment(new ErrorFragment(), R.string.app_not_found_dlg_title, 231 R.string.app_not_found_dlg_text); 232 } else { 233 showDialogFragment(new ErrorDialogFragment(), R.string.app_not_found_dlg_title, 234 R.string.app_not_found_dlg_text); 235 } 236 } 237 showUserIsNotAllowed()238 private void showUserIsNotAllowed() { 239 if (isTv()) { 240 showContentFragment(new ErrorFragment(), 241 R.string.user_is_not_allowed_dlg_title, R.string.user_is_not_allowed_dlg_text); 242 } else { 243 showDialogFragment(new ErrorDialogFragment(), 0, R.string.user_is_not_allowed_dlg_text); 244 } 245 } 246 showGenericError()247 private void showGenericError() { 248 if (isTv()) { 249 showContentFragment(new ErrorFragment(), 250 R.string.generic_error_dlg_title, R.string.generic_error_dlg_text); 251 } else { 252 showDialogFragment(new ErrorDialogFragment(), 0, R.string.generic_error_dlg_text); 253 } 254 } 255 isTv()256 private boolean isTv() { 257 return (getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK) 258 == Configuration.UI_MODE_TYPE_TELEVISION; 259 } 260 showContentFragment(@onNull Fragment fragment, @StringRes int title, @StringRes int text)261 private void showContentFragment(@NonNull Fragment fragment, @StringRes int title, 262 @StringRes int text) { 263 Bundle args = new Bundle(); 264 args.putInt(ErrorFragment.TITLE, title); 265 args.putInt(ErrorFragment.TEXT, text); 266 fragment.setArguments(args); 267 268 getFragmentManager().beginTransaction() 269 .replace(android.R.id.content, fragment) 270 .commit(); 271 } 272 showDialogFragment(@onNull DialogFragment fragment, @StringRes int title, @StringRes int text)273 private void showDialogFragment(@NonNull DialogFragment fragment, 274 @StringRes int title, @StringRes int text) { 275 FragmentTransaction ft = getFragmentManager().beginTransaction(); 276 Fragment prev = getFragmentManager().findFragmentByTag("dialog"); 277 if (prev != null) { 278 ft.remove(prev); 279 } 280 281 Bundle args = new Bundle(); 282 if (title != 0) { 283 args.putInt(ErrorDialogFragment.TITLE, title); 284 } 285 args.putInt(ErrorDialogFragment.TEXT, text); 286 287 fragment.setArguments(args); 288 fragment.show(ft, "dialog"); 289 } 290 startUninstallProgress(boolean keepData)291 public void startUninstallProgress(boolean keepData) { 292 boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false); 293 CharSequence label = mDialogInfo.appInfo.loadSafeLabel(getPackageManager()); 294 295 if (isTv()) { 296 Intent newIntent = new Intent(Intent.ACTION_VIEW); 297 newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user); 298 newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers); 299 newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback); 300 newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo); 301 302 if (returnResult) { 303 newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true); 304 newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); 305 } 306 307 newIntent.setClass(this, UninstallAppProgress.class); 308 startActivity(newIntent); 309 } else if (returnResult || mDialogInfo.callback != null || getCallingActivity() != null) { 310 Intent newIntent = new Intent(this, UninstallUninstalling.class); 311 312 newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user); 313 newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers); 314 newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo); 315 newIntent.putExtra(UninstallUninstalling.EXTRA_APP_LABEL, label); 316 newIntent.putExtra(UninstallUninstalling.EXTRA_KEEP_DATA, keepData); 317 newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback); 318 319 if (returnResult) { 320 newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true); 321 } 322 323 if (returnResult || getCallingActivity() != null) { 324 newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); 325 } 326 327 startActivity(newIntent); 328 } else { 329 int uninstallId; 330 try { 331 uninstallId = UninstallEventReceiver.getNewId(this); 332 } catch (EventResultPersister.OutOfIdsException e) { 333 showGenericError(); 334 return; 335 } 336 337 Intent broadcastIntent = new Intent(this, UninstallFinish.class); 338 339 broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); 340 broadcastIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers); 341 broadcastIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo); 342 broadcastIntent.putExtra(UninstallFinish.EXTRA_APP_LABEL, label); 343 broadcastIntent.putExtra(UninstallFinish.EXTRA_UNINSTALL_ID, uninstallId); 344 345 PendingIntent pendingIntent = 346 PendingIntent.getBroadcast(this, uninstallId, broadcastIntent, 347 PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); 348 349 NotificationManager notificationManager = getSystemService(NotificationManager.class); 350 NotificationChannel uninstallingChannel = new NotificationChannel(UNINSTALLING_CHANNEL, 351 getString(R.string.uninstalling_notification_channel), 352 NotificationManager.IMPORTANCE_MIN); 353 notificationManager.createNotificationChannel(uninstallingChannel); 354 355 Notification uninstallingNotification = 356 (new Notification.Builder(this, UNINSTALLING_CHANNEL)) 357 .setSmallIcon(R.drawable.ic_remove).setProgress(0, 1, true) 358 .setContentTitle(getString(R.string.uninstalling_app, label)).setOngoing(true) 359 .build(); 360 361 notificationManager.notify(uninstallId, uninstallingNotification); 362 363 try { 364 Log.i(TAG, "Uninstalling extras=" + broadcastIntent.getExtras()); 365 366 int flags = mDialogInfo.allUsers ? PackageManager.DELETE_ALL_USERS : 0; 367 flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0; 368 369 ActivityThread.getPackageManager().getPackageInstaller().uninstall( 370 new VersionedPackage(mDialogInfo.appInfo.packageName, 371 PackageManager.VERSION_CODE_HIGHEST), 372 getPackageName(), flags, pendingIntent.getIntentSender(), 373 mDialogInfo.user.getIdentifier()); 374 } catch (Exception e) { 375 notificationManager.cancel(uninstallId); 376 377 Log.e(TAG, "Cannot start uninstall", e); 378 showGenericError(); 379 } 380 } 381 } 382 dispatchAborted()383 public void dispatchAborted() { 384 if (mDialogInfo != null && mDialogInfo.callback != null) { 385 final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub.asInterface( 386 mDialogInfo.callback); 387 try { 388 observer.onPackageDeleted(mPackageName, 389 PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user"); 390 } catch (RemoteException ignored) { 391 } 392 } 393 } 394 getPackageNameForUid(int sourceUid)395 private String getPackageNameForUid(int sourceUid) { 396 String[] packagesForUid = getPackageManager().getPackagesForUid(sourceUid); 397 if (packagesForUid == null) { 398 return null; 399 } 400 return packagesForUid[0]; 401 } 402 } 403