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.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.content.res.Configuration;
     31 import android.graphics.Color;
     32 import android.graphics.drawable.ColorDrawable;
     33 import android.os.Bundle;
     34 import android.os.Handler;
     35 import android.os.IBinder;
     36 import android.os.Message;
     37 import android.os.RemoteException;
     38 import android.os.ServiceManager;
     39 import android.os.UserHandle;
     40 import android.os.UserManager;
     41 import android.provider.Settings;
     42 import android.util.Log;
     43 import android.util.TypedValue;
     44 import android.view.KeyEvent;
     45 import android.view.View;
     46 import android.view.View.OnClickListener;
     47 import android.widget.Button;
     48 import android.widget.ProgressBar;
     49 import android.widget.TextView;
     50 import android.widget.Toast;
     51 
     52 import java.util.List;
     53 
     54 /**
     55  * This activity corresponds to a download progress screen that is displayed
     56  * when an application is uninstalled. The result of the application uninstall
     57  * is indicated in the result code that gets set to 0 or 1. The application gets launched
     58  * by an intent with the intent's class name explicitly set to UninstallAppProgress and expects
     59  * the application object of the application to uninstall.
     60  */
     61 public class UninstallAppProgress extends Activity implements OnClickListener {
     62     private final String TAG="UninstallAppProgress";
     63 
     64     private ApplicationInfo mAppInfo;
     65     private boolean mAllUsers;
     66     private UserHandle mUser;
     67     private IBinder mCallback;
     68 
     69     private Button mOkButton;
     70     private Button mDeviceManagerButton;
     71     private Button mUsersButton;
     72     private volatile int mResultCode = -1;
     73 
     74     /**
     75      * If initView was called. We delay this call to not have to call it at all if the uninstall is
     76      * quick
     77      */
     78     private boolean mIsViewInitialized;
     79 
     80     /** Amount of time to wait until we show the UI */
     81     private static final int QUICK_INSTALL_DELAY_MILLIS = 500;
     82 
     83     private static final int UNINSTALL_COMPLETE = 1;
     84     private static final int UNINSTALL_IS_SLOW = 2;
     85 
     86     private boolean isProfileOfOrSame(UserManager userManager, int userId, int profileId) {
     87         if (userId == profileId) {
     88             return true;
     89         }
     90         UserInfo parentUser = userManager.getProfileParent(profileId);
     91         return parentUser != null && parentUser.id == userId;
     92     }
     93 
     94     private Handler mHandler = new Handler() {
     95         public void handleMessage(Message msg) {
     96             if (isFinishing() || isDestroyed()) {
     97                 return;
     98             }
     99 
    100             switch (msg.what) {
    101                 case UNINSTALL_IS_SLOW:
    102                     initView();
    103                     break;
    104                 case UNINSTALL_COMPLETE:
    105                     mHandler.removeMessages(UNINSTALL_IS_SLOW);
    106 
    107                     if (msg.arg1 != PackageManager.DELETE_SUCCEEDED) {
    108                         initView();
    109                     }
    110 
    111                     mResultCode = msg.arg1;
    112                     final String packageName = (String) msg.obj;
    113 
    114                     if (mCallback != null) {
    115                         final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub
    116                                 .asInterface(mCallback);
    117                         try {
    118                             observer.onPackageDeleted(mAppInfo.packageName, mResultCode,
    119                                     packageName);
    120                         } catch (RemoteException ignored) {
    121                         }
    122                         finish();
    123                         return;
    124                     }
    125 
    126                     if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
    127                         Intent result = new Intent();
    128                         result.putExtra(Intent.EXTRA_INSTALL_RESULT, mResultCode);
    129                         setResult(mResultCode == PackageManager.DELETE_SUCCEEDED
    130                                 ? Activity.RESULT_OK : Activity.RESULT_FIRST_USER,
    131                                         result);
    132                         finish();
    133                         return;
    134                     }
    135 
    136                     // Update the status text
    137                     final String statusText;
    138                     switch (msg.arg1) {
    139                         case PackageManager.DELETE_SUCCEEDED:
    140                             statusText = getString(R.string.uninstall_done);
    141                             // Show a Toast and finish the activity
    142                             Context ctx = getBaseContext();
    143                             Toast.makeText(ctx, statusText, Toast.LENGTH_LONG).show();
    144                             setResultAndFinish(mResultCode);
    145                             return;
    146                         case PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER: {
    147                             UserManager userManager =
    148                                     (UserManager) getSystemService(Context.USER_SERVICE);
    149                             IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
    150                                     ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
    151                             // Find out if the package is an active admin for some non-current user.
    152                             int myUserId = UserHandle.myUserId();
    153                             UserInfo otherBlockingUser = null;
    154                             for (UserInfo user : userManager.getUsers()) {
    155                                 // We only catch the case when the user in question is neither the
    156                                 // current user nor its profile.
    157                                 if (isProfileOfOrSame(userManager, myUserId, user.id)) continue;
    158 
    159                                 try {
    160                                     if (dpm.packageHasActiveAdmins(packageName, user.id)) {
    161                                         otherBlockingUser = user;
    162                                         break;
    163                                     }
    164                                 } catch (RemoteException e) {
    165                                     Log.e(TAG, "Failed to talk to package manager", e);
    166                                 }
    167                             }
    168                             if (otherBlockingUser == null) {
    169                                 Log.d(TAG, "Uninstall failed because " + packageName
    170                                         + " is a device admin");
    171                                 mDeviceManagerButton.setVisibility(View.VISIBLE);
    172                                 statusText = getString(
    173                                         R.string.uninstall_failed_device_policy_manager);
    174                             } else {
    175                                 Log.d(TAG, "Uninstall failed because " + packageName
    176                                         + " is a device admin of user " + otherBlockingUser);
    177                                 mDeviceManagerButton.setVisibility(View.GONE);
    178                                 statusText = String.format(
    179                                         getString(R.string.uninstall_failed_device_policy_manager_of_user),
    180                                         otherBlockingUser.name);
    181                             }
    182                             break;
    183                         }
    184                         case PackageManager.DELETE_FAILED_OWNER_BLOCKED: {
    185                             UserManager userManager =
    186                                     (UserManager) getSystemService(Context.USER_SERVICE);
    187                             IPackageManager packageManager = IPackageManager.Stub.asInterface(
    188                                     ServiceManager.getService("package"));
    189                             List<UserInfo> users = userManager.getUsers();
    190                             int blockingUserId = UserHandle.USER_NULL;
    191                             for (int i = 0; i < users.size(); ++i) {
    192                                 final UserInfo user = users.get(i);
    193                                 try {
    194                                     if (packageManager.getBlockUninstallForUser(packageName,
    195                                             user.id)) {
    196                                         blockingUserId = user.id;
    197                                         break;
    198                                     }
    199                                 } catch (RemoteException e) {
    200                                     // Shouldn't happen.
    201                                     Log.e(TAG, "Failed to talk to package manager", e);
    202                                 }
    203                             }
    204                             int myUserId = UserHandle.myUserId();
    205                             if (isProfileOfOrSame(userManager, myUserId, blockingUserId)) {
    206                                 mDeviceManagerButton.setVisibility(View.VISIBLE);
    207                             } else {
    208                                 mDeviceManagerButton.setVisibility(View.GONE);
    209                                 mUsersButton.setVisibility(View.VISIBLE);
    210                             }
    211                             // TODO: b/25442806
    212                             if (blockingUserId == UserHandle.USER_SYSTEM) {
    213                                 statusText = getString(R.string.uninstall_blocked_device_owner);
    214                             } else if (blockingUserId == UserHandle.USER_NULL) {
    215                                 Log.d(TAG, "Uninstall failed for " + packageName + " with code "
    216                                         + msg.arg1 + " no blocking user");
    217                                 statusText = getString(R.string.uninstall_failed);
    218                             } else {
    219                                 statusText = mAllUsers
    220                                         ? getString(R.string.uninstall_all_blocked_profile_owner) :
    221                                         getString(R.string.uninstall_blocked_profile_owner);
    222                             }
    223                             break;
    224                         }
    225                         default:
    226                             Log.d(TAG, "Uninstall failed for " + packageName + " with code "
    227                                     + msg.arg1);
    228                             statusText = getString(R.string.uninstall_failed);
    229                             break;
    230                     }
    231                     findViewById(R.id.progress_view).setVisibility(View.GONE);
    232                     findViewById(R.id.status_view).setVisibility(View.VISIBLE);
    233                     ((TextView)findViewById(R.id.status_text)).setText(statusText);
    234                     findViewById(R.id.ok_panel).setVisibility(View.VISIBLE);
    235                     break;
    236                 default:
    237                     break;
    238             }
    239         }
    240     };
    241 
    242     @Override
    243     public void onCreate(Bundle icicle) {
    244         super.onCreate(icicle);
    245 
    246         Intent intent = getIntent();
    247         mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
    248         mCallback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);
    249 
    250         // This currently does not support going through a onDestroy->onCreate cycle. Hence if that
    251         // happened, just fail the operation for mysterious reasons.
    252         if (icicle != null) {
    253             mResultCode = PackageManager.DELETE_FAILED_INTERNAL_ERROR;
    254 
    255             if (mCallback != null) {
    256                 final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub
    257                         .asInterface(mCallback);
    258                 try {
    259                     observer.onPackageDeleted(mAppInfo.packageName, mResultCode, null);
    260                 } catch (RemoteException ignored) {
    261                 }
    262                 finish();
    263             } else {
    264                 setResultAndFinish(mResultCode);
    265             }
    266 
    267             return;
    268         }
    269 
    270         mAllUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
    271         if (mAllUsers && !UserManager.get(this).isAdminUser()) {
    272             throw new SecurityException("Only admin user can request uninstall for all users");
    273         }
    274         mUser = intent.getParcelableExtra(Intent.EXTRA_USER);
    275         if (mUser == null) {
    276             mUser = android.os.Process.myUserHandle();
    277         } else {
    278             UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
    279             List<UserHandle> profiles = userManager.getUserProfiles();
    280             if (!profiles.contains(mUser)) {
    281                 throw new SecurityException("User " + android.os.Process.myUserHandle() + " can't "
    282                         + "request uninstall for user " + mUser);
    283             }
    284         }
    285 
    286         PackageDeleteObserver observer = new PackageDeleteObserver();
    287 
    288         // Make window transparent until initView is called. In many cases we can avoid showing the
    289         // UI at all as the app is uninstalled very quickly. If we show the UI and instantly remove
    290         // it, it just looks like a flicker.
    291         getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
    292         getWindow().setStatusBarColor(Color.TRANSPARENT);
    293         getWindow().setNavigationBarColor(Color.TRANSPARENT);
    294 
    295         getPackageManager().deletePackageAsUser(mAppInfo.packageName, observer,
    296                 mAllUsers ? PackageManager.DELETE_ALL_USERS : 0, mUser.getIdentifier());
    297 
    298         mHandler.sendMessageDelayed(mHandler.obtainMessage(UNINSTALL_IS_SLOW),
    299                 QUICK_INSTALL_DELAY_MILLIS);
    300     }
    301 
    302     class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
    303         public void packageDeleted(String packageName, int returnCode) {
    304             Message msg = mHandler.obtainMessage(UNINSTALL_COMPLETE);
    305             msg.arg1 = returnCode;
    306             msg.obj = packageName;
    307             mHandler.sendMessage(msg);
    308         }
    309     }
    310 
    311     void setResultAndFinish(int retCode) {
    312         setResult(retCode);
    313         finish();
    314     }
    315 
    316     public void initView() {
    317         if (mIsViewInitialized) {
    318             return;
    319         }
    320         mIsViewInitialized = true;
    321 
    322         // We set the window background to translucent in constructor, revert this
    323         TypedValue attribute = new TypedValue();
    324         getTheme().resolveAttribute(android.R.attr.windowBackground, attribute, true);
    325         if (attribute.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
    326                 attribute.type <= TypedValue.TYPE_LAST_COLOR_INT) {
    327             getWindow().setBackgroundDrawable(new ColorDrawable(attribute.data));
    328         } else {
    329             getWindow().setBackgroundDrawable(getResources().getDrawable(attribute.resourceId,
    330                     getTheme()));
    331         }
    332 
    333         getTheme().resolveAttribute(android.R.attr.navigationBarColor, attribute, true);
    334         getWindow().setNavigationBarColor(attribute.data);
    335 
    336         getTheme().resolveAttribute(android.R.attr.statusBarColor, attribute, true);
    337         getWindow().setStatusBarColor(attribute.data);
    338 
    339         boolean isUpdate = ((mAppInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
    340         setTitle(isUpdate ? R.string.uninstall_update_title : R.string.uninstall_application_title);
    341 
    342         setContentView(R.layout.uninstall_progress);
    343         // Initialize views
    344         View snippetView = findViewById(R.id.app_snippet);
    345         PackageUtil.initSnippetForInstalledApp(this, mAppInfo, snippetView);
    346         mDeviceManagerButton = (Button) findViewById(R.id.device_manager_button);
    347         mUsersButton = (Button) findViewById(R.id.users_button);
    348         mDeviceManagerButton.setVisibility(View.GONE);
    349         mDeviceManagerButton.setOnClickListener(new OnClickListener() {
    350             @Override
    351             public void onClick(View v) {
    352                 Intent intent = new Intent();
    353                 intent.setClassName("com.android.settings",
    354                         "com.android.settings.Settings$DeviceAdminSettingsActivity");
    355                 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
    356                 startActivity(intent);
    357                 finish();
    358             }
    359         });
    360         mUsersButton.setVisibility(View.GONE);
    361         mUsersButton.setOnClickListener(new OnClickListener() {
    362             @Override
    363             public void onClick(View v) {
    364                 Intent intent = new Intent(Settings.ACTION_USER_SETTINGS);
    365                 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
    366                 startActivity(intent);
    367                 finish();
    368             }
    369         });
    370         // Hide button till progress is being displayed
    371         mOkButton = (Button) findViewById(R.id.ok_button);
    372         mOkButton.setOnClickListener(this);
    373     }
    374 
    375     public void onClick(View v) {
    376         if(v == mOkButton) {
    377             Log.i(TAG, "Finished uninstalling pkg: " + mAppInfo.packageName);
    378             setResultAndFinish(mResultCode);
    379         }
    380     }
    381 
    382     @Override
    383     public boolean dispatchKeyEvent(KeyEvent ev) {
    384         if (ev.getKeyCode() == KeyEvent.KEYCODE_BACK) {
    385             if (mResultCode == -1) {
    386                 // Ignore back key when installation is in progress
    387                 return true;
    388             } else {
    389                 // If installation is done, just set the result code
    390                 setResult(mResultCode);
    391             }
    392         }
    393         return super.dispatchKeyEvent(ev);
    394     }
    395 }
    396