• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ActivityThread;
21 import android.app.DialogFragment;
22 import android.app.Fragment;
23 import android.app.FragmentTransaction;
24 import android.app.Notification;
25 import android.app.NotificationChannel;
26 import android.app.NotificationManager;
27 import android.app.PendingIntent;
28 import android.content.ComponentName;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.pm.ActivityInfo;
32 import android.content.pm.ApplicationInfo;
33 import android.content.pm.IPackageDeleteObserver2;
34 import android.content.pm.IPackageManager;
35 import android.content.pm.PackageInstaller;
36 import android.content.pm.PackageManager;
37 import android.content.pm.VersionedPackage;
38 import android.content.res.Configuration;
39 import android.net.Uri;
40 import android.os.Bundle;
41 import android.os.IBinder;
42 import android.os.RemoteException;
43 import android.os.ServiceManager;
44 import android.os.UserHandle;
45 import android.os.UserManager;
46 import android.support.annotation.NonNull;
47 import android.support.annotation.StringRes;
48 import android.util.Log;
49 
50 import com.android.packageinstaller.handheld.ErrorDialogFragment;
51 import com.android.packageinstaller.handheld.UninstallAlertDialogFragment;
52 import com.android.packageinstaller.television.ErrorFragment;
53 import com.android.packageinstaller.television.UninstallAlertFragment;
54 import com.android.packageinstaller.television.UninstallAppProgress;
55 
56 import java.util.List;
57 
58 /*
59  * This activity presents UI to uninstall an application. Usually launched with intent
60  * Intent.ACTION_UNINSTALL_PKG_COMMAND and attribute
61  * com.android.packageinstaller.PackageName set to the application package name
62  */
63 public class UninstallerActivity extends Activity {
64     private static final String TAG = "UninstallerActivity";
65 
66     private static final String UNINSTALLING_CHANNEL = "uninstalling";
67 
68     public static class DialogInfo {
69         public ApplicationInfo appInfo;
70         public ActivityInfo activityInfo;
71         public boolean allUsers;
72         public UserHandle user;
73         public IBinder callback;
74     }
75 
76     private String mPackageName;
77     private DialogInfo mDialogInfo;
78 
79     @Override
onCreate(Bundle icicle)80     public void onCreate(Bundle icicle) {
81         super.onCreate(icicle);
82         // Get intent information.
83         // We expect an intent with URI of the form package://<packageName>#<className>
84         // className is optional; if specified, it is the activity the user chose to uninstall
85         final Intent intent = getIntent();
86         final Uri packageUri = intent.getData();
87         if (packageUri == null) {
88             Log.e(TAG, "No package URI in intent");
89             showAppNotFound();
90             return;
91         }
92         mPackageName = packageUri.getEncodedSchemeSpecificPart();
93         if (mPackageName == null) {
94             Log.e(TAG, "Invalid package name in URI: " + packageUri);
95             showAppNotFound();
96             return;
97         }
98 
99         final IPackageManager pm = IPackageManager.Stub.asInterface(
100                 ServiceManager.getService("package"));
101 
102         mDialogInfo = new DialogInfo();
103 
104         mDialogInfo.allUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
105         if (mDialogInfo.allUsers && !UserManager.get(this).isAdminUser()) {
106             Log.e(TAG, "Only admin user can request uninstall for all users");
107             showUserIsNotAllowed();
108             return;
109         }
110         mDialogInfo.user = intent.getParcelableExtra(Intent.EXTRA_USER);
111         if (mDialogInfo.user == null) {
112             mDialogInfo.user = android.os.Process.myUserHandle();
113         } else {
114             UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
115             List<UserHandle> profiles = userManager.getUserProfiles();
116             if (!profiles.contains(mDialogInfo.user)) {
117                 Log.e(TAG, "User " + android.os.Process.myUserHandle() + " can't request uninstall "
118                         + "for user " + mDialogInfo.user);
119                 showUserIsNotAllowed();
120                 return;
121             }
122         }
123 
124         mDialogInfo.callback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);
125 
126         try {
127             mDialogInfo.appInfo = pm.getApplicationInfo(mPackageName,
128                     PackageManager.MATCH_ANY_USER, mDialogInfo.user.getIdentifier());
129         } catch (RemoteException e) {
130             Log.e(TAG, "Unable to get packageName. Package manager is dead?");
131         }
132 
133         if (mDialogInfo.appInfo == null) {
134             Log.e(TAG, "Invalid packageName: " + mPackageName);
135             showAppNotFound();
136             return;
137         }
138 
139         // The class name may have been specified (e.g. when deleting an app from all apps)
140         final String className = packageUri.getFragment();
141         if (className != null) {
142             try {
143                 mDialogInfo.activityInfo = pm.getActivityInfo(
144                         new ComponentName(mPackageName, className), 0,
145                         mDialogInfo.user.getIdentifier());
146             } catch (RemoteException e) {
147                 Log.e(TAG, "Unable to get className. Package manager is dead?");
148                 // Continue as the ActivityInfo isn't critical.
149             }
150         }
151 
152         showConfirmationDialog();
153     }
154 
getDialogInfo()155     public DialogInfo getDialogInfo() {
156         return mDialogInfo;
157     }
158 
showConfirmationDialog()159     private void showConfirmationDialog() {
160         if (isTv()) {
161             showContentFragment(new UninstallAlertFragment(), 0, 0);
162         } else {
163             showDialogFragment(new UninstallAlertDialogFragment(), 0, 0);
164         }
165     }
166 
showAppNotFound()167     private void showAppNotFound() {
168         if (isTv()) {
169             showContentFragment(new ErrorFragment(), R.string.app_not_found_dlg_title,
170                     R.string.app_not_found_dlg_text);
171         } else {
172             showDialogFragment(new ErrorDialogFragment(), R.string.app_not_found_dlg_title,
173                     R.string.app_not_found_dlg_text);
174         }
175     }
176 
showUserIsNotAllowed()177     private void showUserIsNotAllowed() {
178         if (isTv()) {
179             showContentFragment(new ErrorFragment(),
180                     R.string.user_is_not_allowed_dlg_title, R.string.user_is_not_allowed_dlg_text);
181         } else {
182             showDialogFragment(new ErrorDialogFragment(), 0, R.string.user_is_not_allowed_dlg_text);
183         }
184     }
185 
showGenericError()186     private void showGenericError() {
187         if (isTv()) {
188             showContentFragment(new ErrorFragment(),
189                     R.string.generic_error_dlg_title, R.string.generic_error_dlg_text);
190         } else {
191             showDialogFragment(new ErrorDialogFragment(), 0, R.string.generic_error_dlg_text);
192         }
193     }
194 
isTv()195     private boolean isTv() {
196         return (getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK)
197                 == Configuration.UI_MODE_TYPE_TELEVISION;
198     }
199 
showContentFragment(@onNull Fragment fragment, @StringRes int title, @StringRes int text)200     private void showContentFragment(@NonNull Fragment fragment, @StringRes int title,
201             @StringRes int text) {
202         Bundle args = new Bundle();
203         args.putInt(ErrorFragment.TITLE, title);
204         args.putInt(ErrorFragment.TEXT, text);
205         fragment.setArguments(args);
206 
207         getFragmentManager().beginTransaction()
208                 .replace(android.R.id.content, fragment)
209                 .commit();
210     }
211 
showDialogFragment(@onNull DialogFragment fragment, @StringRes int title, @StringRes int text)212     private void showDialogFragment(@NonNull DialogFragment fragment,
213             @StringRes int title, @StringRes int text) {
214         FragmentTransaction ft = getFragmentManager().beginTransaction();
215         Fragment prev = getFragmentManager().findFragmentByTag("dialog");
216         if (prev != null) {
217             ft.remove(prev);
218         }
219 
220         Bundle args = new Bundle();
221         if (title != 0) {
222             args.putInt(ErrorDialogFragment.TITLE, title);
223         }
224         args.putInt(ErrorDialogFragment.TEXT, text);
225 
226         fragment.setArguments(args);
227         fragment.show(ft, "dialog");
228     }
229 
startUninstallProgress()230     public void startUninstallProgress() {
231         boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
232         CharSequence label = mDialogInfo.appInfo.loadSafeLabel(getPackageManager());
233 
234         if (isTv()) {
235             Intent newIntent = new Intent(Intent.ACTION_VIEW);
236             newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user);
237             newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
238             newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);
239             newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
240 
241             if (returnResult) {
242                 newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
243                 newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
244             }
245 
246             newIntent.setClass(this, UninstallAppProgress.class);
247             startActivity(newIntent);
248         } else if (returnResult || mDialogInfo.callback != null || getCallingActivity() != null) {
249             Intent newIntent = new Intent(this, UninstallUninstalling.class);
250 
251             newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user);
252             newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
253             newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
254             newIntent.putExtra(UninstallUninstalling.EXTRA_APP_LABEL, label);
255             newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);
256 
257             if (returnResult) {
258                 newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
259             }
260 
261             if (returnResult || getCallingActivity() != null) {
262                 newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
263             }
264 
265             startActivity(newIntent);
266         } else {
267             int uninstallId;
268             try {
269                 uninstallId = UninstallEventReceiver.getNewId(this);
270             } catch (EventResultPersister.OutOfIdsException e) {
271                 showGenericError();
272                 return;
273             }
274 
275             Intent broadcastIntent = new Intent(this, UninstallFinish.class);
276 
277             broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
278             broadcastIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
279             broadcastIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
280             broadcastIntent.putExtra(UninstallFinish.EXTRA_APP_LABEL, label);
281             broadcastIntent.putExtra(UninstallFinish.EXTRA_UNINSTALL_ID, uninstallId);
282 
283             PendingIntent pendingIntent = PendingIntent.getBroadcast(this, uninstallId,
284                     broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
285 
286             NotificationManager notificationManager = getSystemService(NotificationManager.class);
287             NotificationChannel uninstallingChannel = new NotificationChannel(UNINSTALLING_CHANNEL,
288                     getString(R.string.uninstalling_notification_channel),
289                     NotificationManager.IMPORTANCE_MIN);
290             notificationManager.createNotificationChannel(uninstallingChannel);
291 
292             Notification uninstallingNotification =
293                     (new Notification.Builder(this, UNINSTALLING_CHANNEL))
294                     .setSmallIcon(R.drawable.ic_remove).setProgress(0, 1, true)
295                     .setContentTitle(getString(R.string.uninstalling_app, label)).setOngoing(true)
296                     .build();
297 
298             notificationManager.notify(uninstallId, uninstallingNotification);
299 
300             try {
301                 Log.i(TAG, "Uninstalling extras=" + broadcastIntent.getExtras());
302 
303                 ActivityThread.getPackageManager().getPackageInstaller().uninstall(
304                         new VersionedPackage(mDialogInfo.appInfo.packageName,
305                                 PackageManager.VERSION_CODE_HIGHEST),
306                         getPackageName(), mDialogInfo.allUsers
307                                 ? PackageManager.DELETE_ALL_USERS : 0,
308                         pendingIntent.getIntentSender(), mDialogInfo.user.getIdentifier());
309             } catch (Exception e) {
310                 notificationManager.cancel(uninstallId);
311 
312                 Log.e(TAG, "Cannot start uninstall", e);
313                 showGenericError();
314             }
315         }
316     }
317 
dispatchAborted()318     public void dispatchAborted() {
319         if (mDialogInfo != null && mDialogInfo.callback != null) {
320             final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub.asInterface(
321                     mDialogInfo.callback);
322             try {
323                 observer.onPackageDeleted(mPackageName,
324                         PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user");
325             } catch (RemoteException ignored) {
326             }
327         }
328     }
329 }
330