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