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 android.app.Activity;
     20 import android.app.AlertDialog;
     21 import android.app.Dialog;
     22 import android.app.DialogFragment;
     23 import android.app.Fragment;
     24 import android.app.FragmentTransaction;
     25 import android.content.ComponentName;
     26 import android.content.DialogInterface;
     27 import android.content.Intent;
     28 import android.content.pm.ActivityInfo;
     29 import android.content.pm.ApplicationInfo;
     30 import android.content.pm.IPackageDeleteObserver2;
     31 import android.content.pm.IPackageManager;
     32 import android.content.pm.PackageInstaller;
     33 import android.content.pm.PackageManager;
     34 import android.content.pm.UserInfo;
     35 import android.net.Uri;
     36 import android.os.Bundle;
     37 import android.os.IBinder;
     38 import android.os.RemoteException;
     39 import android.os.ServiceManager;
     40 import android.os.UserHandle;
     41 import android.os.UserManager;
     42 import android.util.Log;
     43 
     44 /*
     45  * This activity presents UI to uninstall an application. Usually launched with intent
     46  * Intent.ACTION_UNINSTALL_PKG_COMMAND and attribute
     47  * com.android.packageinstaller.PackageName set to the application package name
     48  */
     49 public class UninstallerActivity extends Activity {
     50     private static final String TAG = "UninstallerActivity";
     51 
     52     public static class UninstallAlertDialogFragment extends DialogFragment implements
     53             DialogInterface.OnClickListener {
     54 
     55         @Override
     56         public Dialog onCreateDialog(Bundle savedInstanceState) {
     57             final PackageManager pm = getActivity().getPackageManager();
     58             final DialogInfo dialogInfo = ((UninstallerActivity) getActivity()).mDialogInfo;
     59             final CharSequence appLabel = dialogInfo.appInfo.loadLabel(pm);
     60 
     61             AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
     62             StringBuilder messageBuilder = new StringBuilder();
     63 
     64             // If the Activity label differs from the App label, then make sure the user
     65             // knows the Activity belongs to the App being uninstalled.
     66             if (dialogInfo.activityInfo != null) {
     67                 final CharSequence activityLabel = dialogInfo.activityInfo.loadLabel(pm);
     68                 if (!activityLabel.equals(appLabel)) {
     69                     messageBuilder.append(
     70                             getString(R.string.uninstall_activity_text, activityLabel));
     71                     messageBuilder.append(" ").append(appLabel).append(".\n\n");
     72                 }
     73             }
     74 
     75             final boolean isUpdate =
     76                     ((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
     77             UserManager userManager = UserManager.get(getActivity());
     78             if (isUpdate) {
     79                 if (isSingleUser(userManager)) {
     80                     messageBuilder.append(getString(R.string.uninstall_update_text));
     81                 } else {
     82                     messageBuilder.append(getString(R.string.uninstall_update_text_multiuser));
     83                 }
     84             } else {
     85                 if (dialogInfo.allUsers && !isSingleUser(userManager)) {
     86                     messageBuilder.append(getString(R.string.uninstall_application_text_all_users));
     87                 } else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) {
     88                     UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier());
     89                     messageBuilder.append(
     90                             getString(R.string.uninstall_application_text_user, userInfo.name));
     91                 } else {
     92                     messageBuilder.append(getString(R.string.uninstall_application_text));
     93                 }
     94             }
     95 
     96             dialogBuilder.setTitle(appLabel);
     97             dialogBuilder.setIcon(dialogInfo.appInfo.loadIcon(pm));
     98             dialogBuilder.setPositiveButton(android.R.string.ok, this);
     99             dialogBuilder.setNegativeButton(android.R.string.cancel, this);
    100             dialogBuilder.setMessage(messageBuilder.toString());
    101             return dialogBuilder.create();
    102         }
    103 
    104         @Override
    105         public void onClick(DialogInterface dialog, int which) {
    106             if (which == Dialog.BUTTON_POSITIVE) {
    107                 ((UninstallerActivity) getActivity()).startUninstallProgress();
    108             } else {
    109                 ((UninstallerActivity) getActivity()).dispatchAborted();
    110             }
    111         }
    112 
    113         @Override
    114         public void onDismiss(DialogInterface dialog) {
    115             super.onDismiss(dialog);
    116             if (isAdded()) {
    117                 getActivity().finish();
    118             }
    119         }
    120 
    121         /**
    122          * Returns whether there is only one user on this device, not including
    123          * the system-only user.
    124          */
    125         private boolean isSingleUser(UserManager userManager) {
    126             final int userCount = userManager.getUserCount();
    127             return userCount == 1
    128                     || (UserManager.isSplitSystemUser() && userCount == 2);
    129         }
    130     }
    131 
    132     public static class AppNotFoundDialogFragment extends DialogFragment {
    133 
    134         @Override
    135         public Dialog onCreateDialog(Bundle savedInstanceState) {
    136             return new AlertDialog.Builder(getActivity())
    137                     .setTitle(R.string.app_not_found_dlg_title)
    138                     .setMessage(R.string.app_not_found_dlg_text)
    139                     .setNeutralButton(android.R.string.ok, null)
    140                     .create();
    141         }
    142 
    143         @Override
    144         public void onDismiss(DialogInterface dialog) {
    145             super.onDismiss(dialog);
    146             if (isAdded()) {
    147                 ((UninstallerActivity) getActivity()).dispatchAborted();
    148                 getActivity().setResult(Activity.RESULT_FIRST_USER);
    149                 getActivity().finish();
    150             }
    151         }
    152     }
    153 
    154     static class DialogInfo {
    155         ApplicationInfo appInfo;
    156         ActivityInfo activityInfo;
    157         boolean allUsers;
    158         UserHandle user;
    159         IBinder callback;
    160     }
    161 
    162     private String mPackageName;
    163     private DialogInfo mDialogInfo;
    164 
    165     @Override
    166     public void onCreate(Bundle icicle) {
    167         super.onCreate(icicle);
    168         // Get intent information.
    169         // We expect an intent with URI of the form package://<packageName>#<className>
    170         // className is optional; if specified, it is the activity the user chose to uninstall
    171         final Intent intent = getIntent();
    172         final Uri packageUri = intent.getData();
    173         if (packageUri == null) {
    174             Log.e(TAG, "No package URI in intent");
    175             showAppNotFound();
    176             return;
    177         }
    178         mPackageName = packageUri.getEncodedSchemeSpecificPart();
    179         if (mPackageName == null) {
    180             Log.e(TAG, "Invalid package name in URI: " + packageUri);
    181             showAppNotFound();
    182             return;
    183         }
    184 
    185         final IPackageManager pm = IPackageManager.Stub.asInterface(
    186                 ServiceManager.getService("package"));
    187 
    188         mDialogInfo = new DialogInfo();
    189 
    190         mDialogInfo.user = intent.getParcelableExtra(Intent.EXTRA_USER);
    191         if (mDialogInfo.user == null) {
    192             mDialogInfo.user = android.os.Process.myUserHandle();
    193         }
    194 
    195         mDialogInfo.allUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
    196         mDialogInfo.callback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);
    197 
    198         try {
    199             mDialogInfo.appInfo = pm.getApplicationInfo(mPackageName,
    200                     PackageManager.GET_UNINSTALLED_PACKAGES, mDialogInfo.user.getIdentifier());
    201         } catch (RemoteException e) {
    202             Log.e(TAG, "Unable to get packageName. Package manager is dead?");
    203         }
    204 
    205         if (mDialogInfo.appInfo == null) {
    206             Log.e(TAG, "Invalid packageName: " + mPackageName);
    207             showAppNotFound();
    208             return;
    209         }
    210 
    211         // The class name may have been specified (e.g. when deleting an app from all apps)
    212         final String className = packageUri.getFragment();
    213         if (className != null) {
    214             try {
    215                 mDialogInfo.activityInfo = pm.getActivityInfo(
    216                         new ComponentName(mPackageName, className), 0,
    217                         mDialogInfo.user.getIdentifier());
    218             } catch (RemoteException e) {
    219                 Log.e(TAG, "Unable to get className. Package manager is dead?");
    220                 // Continue as the ActivityInfo isn't critical.
    221             }
    222         }
    223 
    224         showConfirmationDialog();
    225     }
    226 
    227     private void showConfirmationDialog() {
    228         showDialogFragment(new UninstallAlertDialogFragment());
    229     }
    230 
    231     private void showAppNotFound() {
    232         showDialogFragment(new AppNotFoundDialogFragment());
    233     }
    234 
    235     private void showDialogFragment(DialogFragment fragment) {
    236         FragmentTransaction ft = getFragmentManager().beginTransaction();
    237         Fragment prev = getFragmentManager().findFragmentByTag("dialog");
    238         if (prev != null) {
    239             ft.remove(prev);
    240         }
    241         fragment.show(ft, "dialog");
    242     }
    243 
    244     void startUninstallProgress() {
    245         Intent newIntent = new Intent(Intent.ACTION_VIEW);
    246         newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user);
    247         newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
    248         newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);
    249         newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
    250         if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
    251             newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
    252             newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    253         }
    254         newIntent.setClass(this, UninstallAppProgress.class);
    255         startActivity(newIntent);
    256     }
    257 
    258     void dispatchAborted() {
    259         if (mDialogInfo != null && mDialogInfo.callback != null) {
    260             final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub.asInterface(
    261                     mDialogInfo.callback);
    262             try {
    263                 observer.onPackageDeleted(mPackageName,
    264                         PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user");
    265             } catch (RemoteException ignored) {
    266             }
    267         }
    268     }
    269 }
    270