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 100 int flags = allUsers ? PackageManager.DELETE_ALL_USERS : 0; 101 flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0; 102 103 try { 104 ActivityThread.getPackageManager().getPackageInstaller().uninstall( 105 new VersionedPackage(mAppInfo.packageName, 106 PackageManager.VERSION_CODE_HIGHEST), 107 getPackageName(), flags, pendingIntent.getIntentSender(), 108 user.getIdentifier()); 109 } catch (RemoteException e) { 110 e.rethrowFromSystemServer(); 111 } 112 } else { 113 mUninstallId = savedInstanceState.getInt(UNINSTALL_ID); 114 UninstallEventReceiver.addObserver(this, mUninstallId, this); 115 } 116 } catch (EventResultPersister.OutOfIdsException | IllegalArgumentException e) { 117 Log.e(LOG_TAG, "Fails to start uninstall", e); 118 onResult(PackageInstaller.STATUS_FAILURE, PackageManager.DELETE_FAILED_INTERNAL_ERROR, 119 null); 120 } 121 } 122 123 @Override onSaveInstanceState(Bundle outState)124 protected void onSaveInstanceState(Bundle outState) { 125 super.onSaveInstanceState(outState); 126 127 outState.putInt(UNINSTALL_ID, mUninstallId); 128 } 129 130 @Override onBackPressed()131 public void onBackPressed() { 132 // do nothing 133 } 134 135 @Override onResult(int status, int legacyStatus, @Nullable String message)136 public void onResult(int status, int legacyStatus, @Nullable String message) { 137 if (mCallback != null) { 138 // The caller will be informed about the result via a callback 139 final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub 140 .asInterface(mCallback); 141 try { 142 observer.onPackageDeleted(mAppInfo.packageName, legacyStatus, message); 143 } catch (RemoteException ignored) { 144 } 145 } else if (mReturnResult) { 146 // The caller will be informed about the result and might decide to display it 147 Intent result = new Intent(); 148 149 result.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus); 150 setResult(status == PackageInstaller.STATUS_SUCCESS ? Activity.RESULT_OK 151 : Activity.RESULT_FIRST_USER, result); 152 } else { 153 // This is the rare case that the caller did not ask for the result, but wanted to be 154 // notified via onActivityResult when the installation finishes 155 if (status != PackageInstaller.STATUS_SUCCESS) { 156 Toast.makeText(this, getString(R.string.uninstall_failed_app, mLabel), 157 Toast.LENGTH_LONG).show(); 158 } 159 } 160 finish(); 161 } 162 163 @Override onDestroy()164 protected void onDestroy() { 165 UninstallEventReceiver.removeObserver(this, mUninstallId); 166 167 super.onDestroy(); 168 } 169 170 /** 171 * Dialog that shows that the app is uninstalling. 172 */ 173 public static class UninstallUninstallingFragment extends DialogFragment { 174 @Override onCreateDialog(Bundle savedInstanceState)175 public Dialog onCreateDialog(Bundle savedInstanceState) { 176 AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); 177 178 dialogBuilder.setCancelable(false); 179 dialogBuilder.setMessage(getActivity().getString(R.string.uninstalling_app, 180 ((UninstallUninstalling) getActivity()).mLabel)); 181 182 Dialog dialog = dialogBuilder.create(); 183 dialog.setCanceledOnTouchOutside(false); 184 185 return dialog; 186 } 187 } 188 } 189