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