blob: 50e7bbc0e798dcb04c7cfa0aa36f12dedd774206 [file] [log] [blame]
The Android Open Source Project91ec61c2009-03-03 19:32:31 -08001/*
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*/
17package com.android.packageinstaller;
18
The Android Open Source Project91ec61c2009-03-03 19:32:31 -080019import android.app.Activity;
20import android.app.AlertDialog;
21import android.app.Dialog;
Adam Lesinski3e3e1122014-08-29 11:30:47 -070022import android.app.DialogFragment;
23import android.app.Fragment;
24import android.app.FragmentTransaction;
Patrick Dubroy362660b2010-09-02 18:33:09 -070025import android.content.ComponentName;
The Android Open Source Project91ec61c2009-03-03 19:32:31 -080026import android.content.DialogInterface;
27import android.content.Intent;
Patrick Dubroy362660b2010-09-02 18:33:09 -070028import android.content.pm.ActivityInfo;
The Android Open Source Project91ec61c2009-03-03 19:32:31 -080029import android.content.pm.ApplicationInfo;
Jeff Sharkey3aeb5052014-08-07 17:40:20 -070030import android.content.pm.IPackageDeleteObserver2;
Kenny Guy596ce642014-06-03 17:50:38 +010031import android.content.pm.IPackageManager;
Jeff Sharkey3aeb5052014-08-07 17:40:20 -070032import android.content.pm.PackageInstaller;
The Android Open Source Project91ec61c2009-03-03 19:32:31 -080033import android.content.pm.PackageManager;
Adam Lesinski3e3e1122014-08-29 11:30:47 -070034import android.content.pm.UserInfo;
The Android Open Source Project91ec61c2009-03-03 19:32:31 -080035import android.net.Uri;
36import android.os.Bundle;
Jeff Sharkey3aeb5052014-08-07 17:40:20 -070037import android.os.IBinder;
Kenny Guy596ce642014-06-03 17:50:38 +010038import android.os.RemoteException;
39import android.os.ServiceManager;
40import android.os.UserHandle;
Dianne Hackborn8124c242012-10-31 17:16:35 -070041import android.os.UserManager;
The Android Open Source Project91ec61c2009-03-03 19:32:31 -080042import android.util.Log;
Patrick Dubroy362660b2010-09-02 18:33:09 -070043
The Android Open Source Project91ec61c2009-03-03 19:32:31 -080044/*
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 */
Adam Lesinski3e3e1122014-08-29 11:30:47 -070049public class UninstallerActivity extends Activity {
The Android Open Source Project91ec61c2009-03-03 19:32:31 -080050 private static final String TAG = "UninstallerActivity";
Jeff Sharkey3aeb5052014-08-07 17:40:20 -070051
Adam Lesinski3e3e1122014-08-29 11:30:47 -070052 public static class UninstallAlertDialogFragment extends DialogFragment implements
53 DialogInterface.OnClickListener {
Jeff Sharkey3aeb5052014-08-07 17:40:20 -070054
Adam Lesinski3e3e1122014-08-29 11:30:47 -070055 @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);
Jeff Sharkey3aeb5052014-08-07 17:40:20 -070060
Adam Lesinski3e3e1122014-08-29 11:30:47 -070061 AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
62 StringBuilder messageBuilder = new StringBuilder();
The Android Open Source Project91ec61c2009-03-03 19:32:31 -080063
Adam Lesinski3e3e1122014-08-29 11:30:47 -070064 // 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 }
Suchi Amalapurapud9b773b2009-09-01 14:35:25 -070074
Adam Lesinski3e3e1122014-08-29 11:30:47 -070075 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);
zhuqk6f032ac2015-09-09 11:01:30 +0800112 Activity activity = getActivity();
113 if(activity != null)
114 activity.finish();
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700115 }
116 }
117
118 public static class AppNotFoundDialogFragment extends DialogFragment {
119
120 @Override
121 public Dialog onCreateDialog(Bundle savedInstanceState) {
122 return new AlertDialog.Builder(getActivity())
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800123 .setTitle(R.string.app_not_found_dlg_title)
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800124 .setMessage(R.string.app_not_found_dlg_text)
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700125 .setNeutralButton(android.R.string.ok, null)
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800126 .create();
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800127 }
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700128
129 @Override
130 public void onDismiss(DialogInterface dialog) {
131 super.onDismiss(dialog);
132 ((UninstallerActivity) getActivity()).dispatchAborted();
133 getActivity().setResult(Activity.RESULT_FIRST_USER);
134 getActivity().finish();
135 }
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800136 }
137
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700138 static class DialogInfo {
139 ApplicationInfo appInfo;
140 ActivityInfo activityInfo;
141 boolean allUsers;
142 UserHandle user;
143 IBinder callback;
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800144 }
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800145
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700146 private DialogInfo mDialogInfo;
147
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800148 @Override
149 public void onCreate(Bundle icicle) {
150 super.onCreate(icicle);
Patrick Dubroy362660b2010-09-02 18:33:09 -0700151 // Get intent information.
152 // We expect an intent with URI of the form package://<packageName>#<className>
153 // className is optional; if specified, it is the activity the user chose to uninstall
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800154 final Intent intent = getIntent();
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700155 final Uri packageUri = intent.getData();
156 if (packageUri == null) {
157 Log.e(TAG, "No package URI in intent");
158 showAppNotFound();
159 return;
160 }
161 final String packageName = packageUri.getEncodedSchemeSpecificPart();
162 if (packageName == null) {
163 Log.e(TAG, "Invalid package name in URI: " + packageUri);
164 showAppNotFound();
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800165 return;
166 }
Patrick Dubroy362660b2010-09-02 18:33:09 -0700167
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700168 final IPackageManager pm = IPackageManager.Stub.asInterface(
169 ServiceManager.getService("package"));
Jeff Sharkey3aeb5052014-08-07 17:40:20 -0700170
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700171 mDialogInfo = new DialogInfo();
172
173 mDialogInfo.user = intent.getParcelableExtra(Intent.EXTRA_USER);
174 if (mDialogInfo.user == null) {
175 mDialogInfo.user = android.os.Process.myUserHandle();
Kenny Guy596ce642014-06-03 17:50:38 +0100176 }
Kenny Guy596ce642014-06-03 17:50:38 +0100177
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700178 mDialogInfo.allUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
179 mDialogInfo.callback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);
180
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800181 try {
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700182 mDialogInfo.appInfo = pm.getApplicationInfo(packageName,
183 PackageManager.GET_UNINSTALLED_PACKAGES, mDialogInfo.user.getIdentifier());
Kenny Guy596ce642014-06-03 17:50:38 +0100184 } catch (RemoteException e) {
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700185 Log.e(TAG, "Unable to get packageName. Package manager is dead?");
186 }
187
188 if (mDialogInfo.appInfo == null) {
189 Log.e(TAG, "Invalid packageName: " + packageName);
190 showAppNotFound();
191 return;
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800192 }
Patrick Dubroy362660b2010-09-02 18:33:09 -0700193
194 // The class name may have been specified (e.g. when deleting an app from all apps)
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700195 final String className = packageUri.getFragment();
Patrick Dubroy362660b2010-09-02 18:33:09 -0700196 if (className != null) {
197 try {
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700198 mDialogInfo.activityInfo = pm.getActivityInfo(
199 new ComponentName(packageName, className), 0,
200 mDialogInfo.user.getIdentifier());
Kenny Guy596ce642014-06-03 17:50:38 +0100201 } catch (RemoteException e) {
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700202 Log.e(TAG, "Unable to get className. Package manager is dead?");
203 // Continue as the ActivityInfo isn't critical.
Patrick Dubroy362660b2010-09-02 18:33:09 -0700204 }
205 }
206
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700207 showConfirmationDialog();
208 }
Patrick Dubroy362660b2010-09-02 18:33:09 -0700209
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700210 private void showConfirmationDialog() {
211 showDialogFragment(new UninstallAlertDialogFragment());
212 }
Patrick Dubroy362660b2010-09-02 18:33:09 -0700213
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700214 private void showAppNotFound() {
215 showDialogFragment(new AppNotFoundDialogFragment());
216 }
Patrick Dubroy362660b2010-09-02 18:33:09 -0700217
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700218 private void showDialogFragment(DialogFragment fragment) {
219 FragmentTransaction ft = getFragmentManager().beginTransaction();
220 Fragment prev = getFragmentManager().findFragmentByTag("dialog");
221 if (prev != null) {
222 ft.remove(prev);
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800223 }
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700224 fragment.show(ft, "dialog");
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800225 }
Jeff Sharkey3aeb5052014-08-07 17:40:20 -0700226
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700227 void startUninstallProgress() {
228 Intent newIntent = new Intent(Intent.ACTION_VIEW);
229 newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user);
230 newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
231 newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);
232 newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
233 if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
234 newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
235 newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800236 }
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700237 newIntent.setClass(this, UninstallAppProgress.class);
238 startActivity(newIntent);
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800239 }
Suchi Amalapurapu7672c992009-07-28 17:41:25 -0700240
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700241 void dispatchAborted() {
242 if (mDialogInfo != null && mDialogInfo.callback != null) {
Jeff Sharkey3aeb5052014-08-07 17:40:20 -0700243 final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub.asInterface(
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700244 mDialogInfo.callback);
Jeff Sharkey3aeb5052014-08-07 17:40:20 -0700245 try {
Adam Lesinski3e3e1122014-08-29 11:30:47 -0700246 observer.onPackageDeleted(mDialogInfo.appInfo.packageName,
Jeff Sharkey3aeb5052014-08-07 17:40:20 -0700247 PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user");
248 } catch (RemoteException ignored) {
249 }
250 }
Suchi Amalapurapu7672c992009-07-28 17:41:25 -0700251 }
The Android Open Source Project91ec61c2009-03-03 19:32:31 -0800252}