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.app.Activity; 20 import android.app.ActivityThread; 21 import android.app.AlertDialog; 22 import android.app.Dialog; 23 import android.app.DialogFragment; 24 import android.app.Fragment; 25 import android.app.FragmentTransaction; 26 import android.app.PendingIntent; 27 import android.content.Intent; 28 import android.content.pm.ApplicationInfo; 29 import android.content.pm.IPackageDeleteObserver2; 30 import android.content.pm.PackageInstaller; 31 import android.content.pm.PackageManager; 32 import android.content.pm.VersionedPackage; 33 import android.os.Bundle; 34 import android.os.IBinder; 35 import android.os.RemoteException; 36 import android.os.UserHandle; 37 import android.support.annotation.Nullable; 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 54 private int mUninstallId; 55 private ApplicationInfo mAppInfo; 56 private IBinder mCallback; 57 private boolean mReturnResult; 58 private String mLabel; 59 60 @Override onCreate(@ullable Bundle savedInstanceState)61 protected void onCreate(@Nullable Bundle savedInstanceState) { 62 super.onCreate(savedInstanceState); 63 64 setFinishOnTouchOutside(false); 65 66 mAppInfo = getIntent().getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); 67 mCallback = getIntent().getIBinderExtra(PackageInstaller.EXTRA_CALLBACK); 68 mReturnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false); 69 mLabel = getIntent().getStringExtra(EXTRA_APP_LABEL); 70 71 try { 72 if (savedInstanceState == null) { 73 boolean allUsers = getIntent().getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, 74 false); 75 UserHandle user = getIntent().getParcelableExtra(Intent.EXTRA_USER); 76 77 // Show dialog, which is the whole UI 78 FragmentTransaction transaction = getFragmentManager().beginTransaction(); 79 Fragment prev = getFragmentManager().findFragmentByTag("dialog"); 80 if (prev != null) { 81 transaction.remove(prev); 82 } 83 DialogFragment dialog = new UninstallUninstallingFragment(); 84 dialog.setCancelable(false); 85 dialog.show(transaction, "dialog"); 86 87 mUninstallId = UninstallEventReceiver.addObserver(this, 88 EventResultPersister.GENERATE_NEW_ID, this); 89 90 Intent broadcastIntent = new Intent(BROADCAST_ACTION); 91 broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); 92 broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mUninstallId); 93 broadcastIntent.setPackage(getPackageName()); 94 95 PendingIntent pendingIntent = PendingIntent.getBroadcast(this, mUninstallId, 96 broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT); 97 98 try { 99 ActivityThread.getPackageManager().getPackageInstaller().uninstall( 100 new VersionedPackage(mAppInfo.packageName, 101 PackageManager.VERSION_CODE_HIGHEST), 102 getPackageName(), allUsers ? PackageManager.DELETE_ALL_USERS : 0, 103 pendingIntent.getIntentSender(), user.getIdentifier()); 104 } catch (RemoteException e) { 105 e.rethrowFromSystemServer(); 106 } 107 } else { 108 mUninstallId = savedInstanceState.getInt(UNINSTALL_ID); 109 UninstallEventReceiver.addObserver(this, mUninstallId, this); 110 } 111 } catch (EventResultPersister.OutOfIdsException | IllegalArgumentException e) { 112 Log.e(LOG_TAG, "Fails to start uninstall", e); 113 onResult(PackageInstaller.STATUS_FAILURE, PackageManager.DELETE_FAILED_INTERNAL_ERROR, 114 null); 115 } 116 } 117 118 @Override onSaveInstanceState(Bundle outState)119 protected void onSaveInstanceState(Bundle outState) { 120 super.onSaveInstanceState(outState); 121 122 outState.putInt(UNINSTALL_ID, mUninstallId); 123 } 124 125 @Override onBackPressed()126 public void onBackPressed() { 127 // do nothing 128 } 129 130 @Override onResult(int status, int legacyStatus, @Nullable String message)131 public void onResult(int status, int legacyStatus, @Nullable String message) { 132 if (mCallback != null) { 133 // The caller will be informed about the result via a callback 134 final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub 135 .asInterface(mCallback); 136 try { 137 observer.onPackageDeleted(mAppInfo.packageName, legacyStatus, message); 138 } catch (RemoteException ignored) { 139 } 140 } else if (mReturnResult) { 141 // The caller will be informed about the result and might decide to display it 142 Intent result = new Intent(); 143 144 result.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus); 145 setResult(status == PackageInstaller.STATUS_SUCCESS ? Activity.RESULT_OK 146 : Activity.RESULT_FIRST_USER, result); 147 } else { 148 // This is the rare case that the caller did not ask for the result, but wanted to be 149 // notified via onActivityResult when the installation finishes 150 if (status != PackageInstaller.STATUS_SUCCESS) { 151 Toast.makeText(this, getString(R.string.uninstall_failed_app, mLabel), 152 Toast.LENGTH_LONG).show(); 153 } 154 } 155 finish(); 156 } 157 158 @Override onDestroy()159 protected void onDestroy() { 160 UninstallEventReceiver.removeObserver(this, mUninstallId); 161 162 super.onDestroy(); 163 } 164 165 /** 166 * Dialog that shows that the app is uninstalling. 167 */ 168 public static class UninstallUninstallingFragment extends DialogFragment { 169 @Override onCreateDialog(Bundle savedInstanceState)170 public Dialog onCreateDialog(Bundle savedInstanceState) { 171 AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); 172 173 dialogBuilder.setCancelable(false); 174 dialogBuilder.setMessage(getActivity().getString(R.string.uninstalling_app, 175 ((UninstallUninstalling) getActivity()).mLabel)); 176 177 Dialog dialog = dialogBuilder.create(); 178 dialog.setCanceledOnTouchOutside(false); 179 180 return dialog; 181 } 182 } 183 } 184