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