/* * Copyright (c) 2016 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.acra.util; import android.app.Activity; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.os.Process; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import org.acra.ACRA; import org.acra.builder.LastActivityManager; import org.acra.config.ACRAConfiguration; import org.acra.sender.SenderService; import java.util.List; import static org.acra.ACRA.LOG_TAG; /** * Takes care of cleaning up a process and killing it. * * @author F43nd1r * @since 4.9.2 */ public final class ProcessFinisher { private final Context context; private final ACRAConfiguration config; private final LastActivityManager lastActivityManager; public ProcessFinisher(@NonNull Context context, @NonNull ACRAConfiguration config, @NonNull LastActivityManager lastActivityManager) { this.context = context; this.config = config; this.lastActivityManager = lastActivityManager; } public void endApplication(@Nullable Thread uncaughtExceptionThread) { finishLastActivity(uncaughtExceptionThread); stopServices(); killProcessAndExit(); } public void finishLastActivity(@Nullable Thread uncaughtExceptionThread) { // Trying to solve https://github.com/ACRA/acra/issues/42#issuecomment-12134144 // Determine the current/last Activity that was started and close // it. Activity#finish (and maybe it's parent too). final Activity lastActivity = lastActivityManager.getLastActivity(); if (lastActivity != null) { if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Finishing the last Activity prior to killing the Process"); lastActivity.runOnUiThread(new Runnable() { @Override public void run() { lastActivity.finish(); if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Finished " + lastActivity.getClass()); } }); // A crashed activity won't continue its lifecycle. So we only wait if something else crashed if (uncaughtExceptionThread != lastActivity.getMainLooper().getThread()) { lastActivityManager.waitForActivityStop(100); } lastActivityManager.clearLastActivity(); } } private void stopServices() { if (config.stopServicesOnCrash()) { final ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); final List<ActivityManager.RunningServiceInfo> runningServices = activityManager.getRunningServices(Integer.MAX_VALUE); final int pid = Process.myPid(); for (ActivityManager.RunningServiceInfo serviceInfo : runningServices) { if (serviceInfo.pid == pid && !SenderService.class.getName().equals(serviceInfo.service.getClassName())) { try { final Intent intent = new Intent(); intent.setComponent(serviceInfo.service); context.stopService(intent); } catch (SecurityException e) { if (ACRA.DEV_LOGGING) ACRA.log.d(ACRA.LOG_TAG, "Unable to stop Service " + serviceInfo.service.getClassName() + ". Permission denied"); } } } } } private void killProcessAndExit() { android.os.Process.killProcess(android.os.Process.myPid()); System.exit(10); } }