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