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             if (isUpdate) {
     78                 messageBuilder.append(getString(R.string.uninstall_update_text));
     79             } else {
     80                 UserManager userManager = UserManager.get(getActivity());
     81                 if (dialogInfo.allUsers && userManager.getUserCount() >= 2) {
     82                     messageBuilder.append(getString(R.string.uninstall_application_text_all_users));
     83                 } else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) {
     84                     UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier());
     85                     messageBuilder.append(
     86                             getString(R.string.uninstall_application_text_user, userInfo.name));
     87                 } else {
     88                     messageBuilder.append(getString(R.string.uninstall_application_text));
     89                 }
     90             }
     91 
     92             dialogBuilder.setTitle(appLabel);
     93             dialogBuilder.setIcon(dialogInfo.appInfo.loadIcon(pm));
     94             dialogBuilder.setPositiveButton(android.R.string.ok, this);
     95             dialogBuilder.setNegativeButton(android.R.string.cancel, this);
     96             dialogBuilder.setMessage(messageBuilder.toString());
     97             return dialogBuilder.create();
     98         }
     99 
    100         @Override
    101         public void onClick(DialogInterface dialog, int which) {
    102             if (which == Dialog.BUTTON_POSITIVE) {
    103                 ((UninstallerActivity) getActivity()).startUninstallProgress();
    104             } else {
    105                 ((UninstallerActivity) getActivity()).dispatchAborted();
    106             }
    107         }
    108 
    109         @Override
    110         public void onDismiss(DialogInterface dialog) {
    111             super.onDismiss(dialog);
    112             getActivity().finish();
    113         }
    114     }
    115 
    116     public static class AppNotFoundDialogFragment extends DialogFragment {
    117 
    118         @Override
    119         public Dialog onCreateDialog(Bundle savedInstanceState) {
    120             return new AlertDialog.Builder(getActivity())
    121                     .setTitle(R.string.app_not_found_dlg_title)
    122                     .setMessage(R.string.app_not_found_dlg_text)
    123                     .setNeutralButton(android.R.string.ok, null)
    124                     .create();
    125         }
    126 
    127         @Override
    128         public void onDismiss(DialogInterface dialog) {
    129             super.onDismiss(dialog);
    130             ((UninstallerActivity) getActivity()).dispatchAborted();
    131             getActivity().setResult(Activity.RESULT_FIRST_USER);
    132             getActivity().finish();
    133         }
    134     }
    135 
    136     static class DialogInfo {
    137         ApplicationInfo appInfo;
    138         ActivityInfo activityInfo;
    139         boolean allUsers;
    140         UserHandle user;
    141         IBinder callback;
    142     }
    143 
    144     private String mPackageName;
    145     private DialogInfo mDialogInfo;
    146 
    147     @Override
    148     public void onCreate(Bundle icicle) {
    149         super.onCreate(icicle);
    150         // Get intent information.
    151         // We expect an intent with URI of the form package://<packageName>#<className>
    152         // className is optional; if specified, it is the activity the user chose to uninstall
    153         final Intent intent = getIntent();
    154         final Uri packageUri = intent.getData();
    155         if (packageUri == null) {
    156             Log.e(TAG, "No package URI in intent");
    157             showAppNotFound();
    158             return;
    159         }
    160         mPackageName = packageUri.getEncodedSchemeSpecificPart();
    161         if (mPackageName == null) {
    162             Log.e(TAG, "Invalid package name in URI: " + packageUri);
    163             showAppNotFound();
    164             return;
    165         }
    166 
    167         final IPackageManager pm = IPackageManager.Stub.asInterface(
    168                 ServiceManager.getService("package"));
    169 
    170         mDialogInfo = new DialogInfo();
    171 
    172         mDialogInfo.user = intent.getParcelableExtra(Intent.EXTRA_USER);
    173         if (mDialogInfo.user == null) {
    174             mDialogInfo.user = android.os.Process.myUserHandle();
    175         }
    176 
    177         mDialogInfo.allUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
    178         mDialogInfo.callback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);
    179 
    180         try {
    181             mDialogInfo.appInfo = pm.getApplicationInfo(mPackageName,
    182                     PackageManager.GET_UNINSTALLED_PACKAGES, mDialogInfo.user.getIdentifier());
    183         } catch (RemoteException e) {
    184             Log.e(TAG, "Unable to get packageName. Package manager is dead?");
    185         }
    186 
    187         if (mDialogInfo.appInfo == null) {
    188             Log.e(TAG, "Invalid packageName: " + mPackageName);
    189             showAppNotFound();
    190             return;
    191         }
    192 
    193         // The class name may have been specified (e.g. when deleting an app from all apps)
    194         final String className = packageUri.getFragment();
    195         if (className != null) {
    196             try {
    197                 mDialogInfo.activityInfo = pm.getActivityInfo(
    198                         new ComponentName(mPackageName, className), 0,
    199                         mDialogInfo.user.getIdentifier());
    200             } catch (RemoteException e) {
    201                 Log.e(TAG, "Unable to get className. Package manager is dead?");
    202                 // Continue as the ActivityInfo isn't critical.
    203             }
    204         }
    205 
    206         showConfirmationDialog();
    207     }
    208 
    209     private void showConfirmationDialog() {
    210         showDialogFragment(new UninstallAlertDialogFragment());
    211     }
    212 
    213     private void showAppNotFound() {
    214         showDialogFragment(new AppNotFoundDialogFragment());
    215     }
    216 
    217     private void showDialogFragment(DialogFragment fragment) {
    218         FragmentTransaction ft = getFragmentManager().beginTransaction();
    219         Fragment prev = getFragmentManager().findFragmentByTag("dialog");
    220         if (prev != null) {
    221             ft.remove(prev);
    222         }
    223         fragment.show(ft, "dialog");
    224     }
    225 
    226     void startUninstallProgress() {
    227         Intent newIntent = new Intent(Intent.ACTION_VIEW);
    228         newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user);
    229         newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
    230         newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);
    231         newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
    232         if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
    233             newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
    234             newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    235         }
    236         newIntent.setClass(this, UninstallAppProgress.class);
    237         startActivity(newIntent);
    238     }
    239 
    240     void dispatchAborted() {
    241         if (mDialogInfo != null && mDialogInfo.callback != null) {
    242             final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub.asInterface(
    243                     mDialogInfo.callback);
    244             try {
    245                 observer.onPackageDeleted(mPackageName,
    246                         PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user");
    247             } catch (RemoteException ignored) {
    248             }
    249         }
    250     }
    251 }
    252