1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.packageinstaller; 18 19 import android.annotation.Nullable; 20 import android.app.Activity; 21 import android.app.ActivityThread; 22 import android.app.AlertDialog; 23 import android.app.Dialog; 24 import android.app.DialogFragment; 25 import android.app.Fragment; 26 import android.app.FragmentTransaction; 27 import android.app.PendingIntent; 28 import android.content.Intent; 29 import android.content.pm.ApplicationInfo; 30 import android.content.pm.IPackageDeleteObserver2; 31 import android.content.pm.PackageInstaller; 32 import android.content.pm.PackageManager; 33 import android.content.pm.VersionedPackage; 34 import android.os.Bundle; 35 import android.os.IBinder; 36 import android.os.RemoteException; 37 import android.os.UserHandle; 38 import android.util.Log; 39 import android.widget.Toast; 40 41 /** 42 * Start an uninstallation, show a dialog while uninstalling and return result to the caller. 43 */ 44 public class UninstallUninstalling extends Activity implements 45 EventResultPersister.EventResultObserver { 46 private static final String LOG_TAG = UninstallUninstalling.class.getSimpleName(); 47 48 private static final String UNINSTALL_ID = "com.android.packageinstaller.UNINSTALL_ID"; 49 private static final String BROADCAST_ACTION = 50 "com.android.packageinstaller.ACTION_UNINSTALL_COMMIT"; 51 52 static final String EXTRA_APP_LABEL = "com.android.packageinstaller.extra.APP_LABEL"; 53 static final String EXTRA_KEEP_DATA = "com.android.packageinstaller.extra.KEEP_DATA"; 54 55 private int mUninstallId; 56 private ApplicationInfo mAppInfo; 57 private IBinder mCallback; 58 private boolean mReturnResult; 59 private String mLabel; 60 61 @Override onCreate(@ullable Bundle savedInstanceState)62 protected void onCreate(@Nullable Bundle savedInstanceState) { 63 super.onCreate(savedInstanceState); 64 65 setFinishOnTouchOutside(false); 66 67 mAppInfo = getIntent().getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); 68 mCallback = getIntent().getIBinderExtra(PackageInstaller.EXTRA_CALLBACK); 69 mReturnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false); 70 mLabel = getIntent().getStringExtra(EXTRA_APP_LABEL); 71 72 try { 73 if (savedInstanceState == null) { 74 boolean allUsers = getIntent().getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, 75 false); 76 boolean keepData = getIntent().getBooleanExtra(EXTRA_KEEP_DATA, false); 77 UserHandle user = getIntent().getParcelableExtra(Intent.EXTRA_USER); 78 79 // Show dialog, which is the whole UI 80 FragmentTransaction transaction = getFragmentManager().beginTransaction(); 81 Fragment prev = getFragmentManager().findFragmentByTag("dialog"); 82 if (prev != null) { 83 transaction.remove(prev); 84 } 85 DialogFragment dialog = new UninstallUninstallingFragment(); 86 dialog.setCancelable(false); 87 dialog.show(transaction, "dialog"); 88 89 mUninstallId = UninstallEventReceiver.addObserver(this, 90 EventResultPersister.GENERATE_NEW_ID, this); 91 92 Intent broadcastIntent = new Intent(BROADCAST_ACTION); 93 broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); 94 broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mUninstallId); 95 broadcastIntent.setPackage(getPackageName()); 96 97 PendingIntent pendingIntent = PendingIntent.getBroadcast(this, mUninstallId, 98 broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT 99 | PendingIntent.FLAG_MUTABLE); 100 101 int flags = allUsers ? PackageManager.DELETE_ALL_USERS : 0; 102 flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0; 103 104 try { 105 ActivityThread.getPackageManager().getPackageInstaller().uninstall( 106 new VersionedPackage(mAppInfo.packageName, 107 PackageManager.VERSION_CODE_HIGHEST), 108 getPackageName(), flags, pendingIntent.getIntentSender(), 109 user.getIdentifier()); 110 } catch (RemoteException e) { 111 e.rethrowFromSystemServer(); 112 } 113 } else { 114 mUninstallId = savedInstanceState.getInt(UNINSTALL_ID); 115 UninstallEventReceiver.addObserver(this, mUninstallId, this); 116 } 117 } catch (EventResultPersister.OutOfIdsException | IllegalArgumentException e) { 118 Log.e(LOG_TAG, "Fails to start uninstall", e); 119 onResult(PackageInstaller.STATUS_FAILURE, PackageManager.DELETE_FAILED_INTERNAL_ERROR, 120 null); 121 } 122 } 123 124 @Override onSaveInstanceState(Bundle outState)125 protected void onSaveInstanceState(Bundle outState) { 126 super.onSaveInstanceState(outState); 127 128 outState.putInt(UNINSTALL_ID, mUninstallId); 129 } 130 131 @Override onBackPressed()132 public void onBackPressed() { 133 // do nothing 134 } 135 136 @Override onResult(int status, int legacyStatus, @Nullable String message)137 public void onResult(int status, int legacyStatus, @Nullable String message) { 138 if (mCallback != null) { 139 // The caller will be informed about the result via a callback 140 final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub 141 .asInterface(mCallback); 142 try { 143 observer.onPackageDeleted(mAppInfo.packageName, legacyStatus, message); 144 } catch (RemoteException ignored) { 145 } 146 } else if (mReturnResult) { 147 // The caller will be informed about the result and might decide to display it 148 Intent result = new Intent(); 149 150 result.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus); 151 setResult(status == PackageInstaller.STATUS_SUCCESS ? Activity.RESULT_OK 152 : Activity.RESULT_FIRST_USER, result); 153 } else { 154 // This is the rare case that the caller did not ask for the result, but wanted to be 155 // notified via onActivityResult when the installation finishes 156 if (status != PackageInstaller.STATUS_SUCCESS) { 157 Toast.makeText(this, getString(R.string.uninstall_failed_app, mLabel), 158 Toast.LENGTH_LONG).show(); 159 } 160 } 161 finish(); 162 } 163 164 @Override onDestroy()165 protected void onDestroy() { 166 UninstallEventReceiver.removeObserver(this, mUninstallId); 167 168 super.onDestroy(); 169 } 170 171 /** 172 * Dialog that shows that the app is uninstalling. 173 */ 174 public static class UninstallUninstallingFragment extends DialogFragment { 175 @Override onCreateDialog(Bundle savedInstanceState)176 public Dialog onCreateDialog(Bundle savedInstanceState) { 177 AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); 178 179 dialogBuilder.setCancelable(false); 180 dialogBuilder.setMessage(getActivity().getString(R.string.uninstalling_app, 181 ((UninstallUninstalling) getActivity()).mLabel)); 182 183 Dialog dialog = dialogBuilder.create(); 184 dialog.setCanceledOnTouchOutside(false); 185 186 return dialog; 187 } 188 } 189 } 190