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