• 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.admin.IDevicePolicyManager;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.ApplicationInfo;
24 import android.content.pm.IPackageDeleteObserver;
25 import android.content.pm.IPackageDeleteObserver2;
26 import android.content.pm.IPackageManager;
27 import android.content.pm.PackageInstaller;
28 import android.content.pm.PackageManager;
29 import android.content.pm.UserInfo;
30 import android.os.Bundle;
31 import android.os.Handler;
32 import android.os.IBinder;
33 import android.os.Message;
34 import android.os.RemoteException;
35 import android.os.ServiceManager;
36 import android.os.UserHandle;
37 import android.os.UserManager;
38 import android.util.Log;
39 import android.view.KeyEvent;
40 import android.view.View;
41 import android.view.View.OnClickListener;
42 import android.widget.Button;
43 import android.widget.ProgressBar;
44 import android.widget.TextView;
45 import android.widget.Toast;
46 
47 import java.util.List;
48 
49 /**
50  * This activity corresponds to a download progress screen that is displayed
51  * when an application is uninstalled. The result of the application uninstall
52  * is indicated in the result code that gets set to 0 or 1. The application gets launched
53  * by an intent with the intent's class name explicitly set to UninstallAppProgress and expects
54  * the application object of the application to uninstall.
55  */
56 public class UninstallAppProgress extends Activity implements OnClickListener {
57     private final String TAG="UninstallAppProgress";
58 
59     private ApplicationInfo mAppInfo;
60     private boolean mAllUsers;
61     private UserHandle mUser;
62     private IBinder mCallback;
63 
64     private TextView mStatusTextView;
65     private Button mOkButton;
66     private Button mDeviceManagerButton;
67     private ProgressBar mProgressBar;
68     private View mOkPanel;
69     private volatile int mResultCode = -1;
70 
71     private static final int UNINSTALL_COMPLETE = 1;
72 
isProfileOfOrSame(UserManager userManager, int userId, int profileId)73     private boolean isProfileOfOrSame(UserManager userManager, int userId, int profileId) {
74         if (userId == profileId) {
75             return true;
76         }
77         UserInfo parentUser = userManager.getProfileParent(profileId);
78         return parentUser != null && parentUser.id == userId;
79     }
80 
81     private Handler mHandler = new Handler() {
82         public void handleMessage(Message msg) {
83             switch (msg.what) {
84                 case UNINSTALL_COMPLETE:
85                     mResultCode = msg.arg1;
86                     final String packageName = (String) msg.obj;
87 
88                     if (mCallback != null) {
89                         final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub
90                                 .asInterface(mCallback);
91                         try {
92                             observer.onPackageDeleted(mAppInfo.packageName, mResultCode,
93                                     packageName);
94                         } catch (RemoteException ignored) {
95                         }
96                         finish();
97                         return;
98                     }
99 
100                     if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
101                         Intent result = new Intent();
102                         result.putExtra(Intent.EXTRA_INSTALL_RESULT, mResultCode);
103                         setResult(mResultCode == PackageManager.DELETE_SUCCEEDED
104                                 ? Activity.RESULT_OK : Activity.RESULT_FIRST_USER,
105                                         result);
106                         finish();
107                         return;
108                     }
109 
110                     // Update the status text
111                     final String statusText;
112                     switch (msg.arg1) {
113                         case PackageManager.DELETE_SUCCEEDED:
114                             statusText = getString(R.string.uninstall_done);
115                             // Show a Toast and finish the activity
116                             Context ctx = getBaseContext();
117                             Toast.makeText(ctx, statusText, Toast.LENGTH_LONG).show();
118                             setResultAndFinish(mResultCode);
119                             return;
120                         case PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER: {
121                             UserManager userManager =
122                                     (UserManager) getSystemService(Context.USER_SERVICE);
123                             IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
124                                     ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
125                             // Find out if the package is an active admin for some non-current user.
126                             int myUserId = UserHandle.myUserId();
127                             UserInfo otherBlockingUser = null;
128                             for (UserInfo user : userManager.getUsers()) {
129                                 // We only catch the case when the user in question is neither the
130                                 // current user nor its profile.
131                                 if (isProfileOfOrSame(userManager, myUserId, user.id)) continue;
132 
133                                 try {
134                                     if (dpm.packageHasActiveAdmins(packageName, user.id)) {
135                                         otherBlockingUser = user;
136                                         break;
137                                     }
138                                 } catch (RemoteException e) {
139                                     Log.e(TAG, "Failed to talk to package manager", e);
140                                 }
141                             }
142                             if (otherBlockingUser == null) {
143                                 Log.d(TAG, "Uninstall failed because " + packageName
144                                         + " is a device admin");
145                                 mDeviceManagerButton.setVisibility(View.VISIBLE);
146                                 statusText = getString(
147                                         R.string.uninstall_failed_device_policy_manager);
148                             } else {
149                                 Log.d(TAG, "Uninstall failed because " + packageName
150                                         + " is a device admin of user " + otherBlockingUser);
151                                 mDeviceManagerButton.setVisibility(View.GONE);
152                                 statusText = String.format(
153                                         getString(R.string.uninstall_failed_device_policy_manager_of_user),
154                                         otherBlockingUser.name);
155                             }
156                             break;
157                         }
158                         case PackageManager.DELETE_FAILED_OWNER_BLOCKED: {
159                             UserManager userManager =
160                                     (UserManager) getSystemService(Context.USER_SERVICE);
161                             IPackageManager packageManager = IPackageManager.Stub.asInterface(
162                                     ServiceManager.getService("package"));
163                             List<UserInfo> users = userManager.getUsers();
164                             int blockingUserId = UserHandle.USER_NULL;
165                             for (int i = 0; i < users.size(); ++i) {
166                                 final UserInfo user = users.get(i);
167                                 try {
168                                     if (packageManager.getBlockUninstallForUser(packageName,
169                                             user.id)) {
170                                         blockingUserId = user.id;
171                                         break;
172                                     }
173                                 } catch (RemoteException e) {
174                                     // Shouldn't happen.
175                                     Log.e(TAG, "Failed to talk to package manager", e);
176                                 }
177                             }
178                             int myUserId = UserHandle.myUserId();
179                             if (isProfileOfOrSame(userManager, myUserId, blockingUserId)) {
180                                 mDeviceManagerButton.setVisibility(View.VISIBLE);
181                             } else {
182                                 mDeviceManagerButton.setVisibility(View.GONE);
183                             }
184                             if (blockingUserId == UserHandle.USER_OWNER) {
185                                 statusText = getString(R.string.uninstall_blocked_device_owner);
186                             } else if (blockingUserId == UserHandle.USER_NULL) {
187                                 Log.d(TAG, "Uninstall failed for " + packageName + " with code "
188                                         + msg.arg1 + " no blocking user");
189                                 statusText = getString(R.string.uninstall_failed);
190                             } else {
191                                 statusText = getString(R.string.uninstall_blocked_profile_owner);
192                             }
193                             break;
194                         }
195                         default:
196                             Log.d(TAG, "Uninstall failed for " + packageName + " with code "
197                                     + msg.arg1);
198                             statusText = getString(R.string.uninstall_failed);
199                             break;
200                     }
201                     mStatusTextView.setText(statusText);
202 
203                     // Hide the progress bar; Show the ok button
204                     mProgressBar.setVisibility(View.INVISIBLE);
205                     mOkPanel.setVisibility(View.VISIBLE);
206                     break;
207                 default:
208                     break;
209             }
210         }
211     };
212 
213     @Override
onCreate(Bundle icicle)214     public void onCreate(Bundle icicle) {
215         super.onCreate(icicle);
216         Intent intent = getIntent();
217         mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
218         mAllUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
219         if (mAllUsers && UserHandle.myUserId() != UserHandle.USER_OWNER) {
220             throw new SecurityException("Only owner user can request uninstall for all users");
221         }
222         mUser = intent.getParcelableExtra(Intent.EXTRA_USER);
223         if (mUser == null) {
224             mUser = android.os.Process.myUserHandle();
225         } else {
226             UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
227             List<UserHandle> profiles = userManager.getUserProfiles();
228             if (!profiles.contains(mUser)) {
229                 throw new SecurityException("User " + android.os.Process.myUserHandle() + " can't "
230                         + "request uninstall for user " + mUser);
231             }
232         }
233         mCallback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);
234         initView();
235     }
236 
237     class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
packageDeleted(String packageName, int returnCode)238         public void packageDeleted(String packageName, int returnCode) {
239             Message msg = mHandler.obtainMessage(UNINSTALL_COMPLETE);
240             msg.arg1 = returnCode;
241             msg.obj = packageName;
242             mHandler.sendMessage(msg);
243         }
244     }
245 
setResultAndFinish(int retCode)246     void setResultAndFinish(int retCode) {
247         setResult(retCode);
248         finish();
249     }
250 
initView()251     public void initView() {
252         boolean isUpdate = ((mAppInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
253         setTitle(isUpdate ? R.string.uninstall_update_title : R.string.uninstall_application_title);
254 
255         setContentView(R.layout.uninstall_progress);
256         // Initialize views
257         View snippetView = findViewById(R.id.app_snippet);
258         PackageUtil.initSnippetForInstalledApp(this, mAppInfo, snippetView);
259         mStatusTextView = (TextView) findViewById(R.id.center_text);
260         mStatusTextView.setText(R.string.uninstalling);
261         mDeviceManagerButton = (Button) findViewById(R.id.device_manager_button);
262         mDeviceManagerButton.setVisibility(View.GONE);
263         mDeviceManagerButton.setOnClickListener(new OnClickListener() {
264             @Override
265             public void onClick(View v) {
266                 Intent intent = new Intent();
267                 intent.setClassName("com.android.settings",
268                         "com.android.settings.Settings$DeviceAdminSettingsActivity");
269                 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
270                 startActivity(intent);
271                 finish();
272             }
273         });
274         mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
275         mProgressBar.setIndeterminate(true);
276         // Hide button till progress is being displayed
277         mOkPanel = (View) findViewById(R.id.ok_panel);
278         mOkButton = (Button) findViewById(R.id.ok_button);
279         mOkButton.setOnClickListener(this);
280         mOkPanel.setVisibility(View.INVISIBLE);
281         IPackageManager packageManager =
282                 IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
283         PackageDeleteObserver observer = new PackageDeleteObserver();
284         try {
285             packageManager.deletePackageAsUser(mAppInfo.packageName, observer,
286                     mUser.getIdentifier(),
287                     mAllUsers ? PackageManager.DELETE_ALL_USERS : 0);
288         } catch (RemoteException e) {
289             // Shouldn't happen.
290             Log.e(TAG, "Failed to talk to package manager", e);
291         }
292     }
293 
onClick(View v)294     public void onClick(View v) {
295         if(v == mOkButton) {
296             Log.i(TAG, "Finished uninstalling pkg: " + mAppInfo.packageName);
297             setResultAndFinish(mResultCode);
298         }
299     }
300 
301     @Override
dispatchKeyEvent(KeyEvent ev)302     public boolean dispatchKeyEvent(KeyEvent ev) {
303         if (ev.getKeyCode() == KeyEvent.KEYCODE_BACK) {
304             if (mResultCode == -1) {
305                 // Ignore back key when installation is in progress
306                 return true;
307             } else {
308                 // If installation is done, just set the result code
309                 setResult(mResultCode);
310             }
311         }
312         return super.dispatchKeyEvent(ev);
313     }
314 }
315