Home | History | Annotate | Download | only in packageinstaller
      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