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 DialogInfo mDialogInfo;
    145 
    146     @Override
    147     public void onCreate(Bundle icicle) {
    148         super.onCreate(icicle);
    149         // Get intent information.
    150         // We expect an intent with URI of the form package://<packageName>#<className>
    151         // className is optional; if specified, it is the activity the user chose to uninstall
    152         final Intent intent = getIntent();
    153         final Uri packageUri = intent.getData();
    154         if (packageUri == null) {
    155             Log.e(TAG, "No package URI in intent");
    156             showAppNotFound();
    157             return;
    158         }
    159         final String packageName = packageUri.getEncodedSchemeSpecificPart();
    160         if (packageName == null) {
    161             Log.e(TAG, "Invalid package name in URI: " + packageUri);
    162             showAppNotFound();
    163             return;
    164         }
    165 
    166         final IPackageManager pm = IPackageManager.Stub.asInterface(
    167                 ServiceManager.getService("package"));
    168 
    169         mDialogInfo = new DialogInfo();
    170 
    171         mDialogInfo.user = intent.getParcelableExtra(Intent.EXTRA_USER);
    172         if (mDialogInfo.user == null) {
    173             mDialogInfo.user = android.os.Process.myUserHandle();
    174         }
    175 
    176         mDialogInfo.allUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
    177         mDialogInfo.callback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);
    178 
    179         try {
    180             mDialogInfo.appInfo = pm.getApplicationInfo(packageName,
    181                     PackageManager.GET_UNINSTALLED_PACKAGES, mDialogInfo.user.getIdentifier());
    182         } catch (RemoteException e) {
    183             Log.e(TAG, "Unable to get packageName. Package manager is dead?");
    184         }
    185 
    186         if (mDialogInfo.appInfo == null) {
    187             Log.e(TAG, "Invalid packageName: " + packageName);
    188             showAppNotFound();
    189             return;
    190         }
    191 
    192         // The class name may have been specified (e.g. when deleting an app from all apps)
    193         final String className = packageUri.getFragment();
    194         if (className != null) {
    195             try {
    196                 mDialogInfo.activityInfo = pm.getActivityInfo(
    197                         new ComponentName(packageName, className), 0,
    198                         mDialogInfo.user.getIdentifier());
    199             } catch (RemoteException e) {
    200                 Log.e(TAG, "Unable to get className. Package manager is dead?");
    201                 // Continue as the ActivityInfo isn't critical.
    202             }
    203         }
    204 
    205         showConfirmationDialog();
    206     }
    207 
    208     private void showConfirmationDialog() {
    209         showDialogFragment(new UninstallAlertDialogFragment());
    210     }
    211 
    212     private void showAppNotFound() {
    213         showDialogFragment(new AppNotFoundDialogFragment());
    214     }
    215 
    216     private void showDialogFragment(DialogFragment fragment) {
    217         FragmentTransaction ft = getFragmentManager().beginTransaction();
    218         Fragment prev = getFragmentManager().findFragmentByTag("dialog");
    219         if (prev != null) {
    220             ft.remove(prev);
    221         }
    222         fragment.show(ft, "dialog");
    223     }
    224 
    225     void startUninstallProgress() {
    226         Intent newIntent = new Intent(Intent.ACTION_VIEW);
    227         newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user);
    228         newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
    229         newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);
    230         newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
    231         if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
    232             newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
    233             newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    234         }
    235         newIntent.setClass(this, UninstallAppProgress.class);
    236         startActivity(newIntent);
    237     }
    238 
    239     void dispatchAborted() {
    240         if (mDialogInfo != null && mDialogInfo.callback != null) {
    241             final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub.asInterface(
    242                     mDialogInfo.callback);
    243             try {
    244                 observer.onPackageDeleted(mDialogInfo.appInfo.packageName,
    245                         PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user");
    246             } catch (RemoteException ignored) {
    247             }
    248         }
    249     }
    250 }
    251