All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.shell.HeapDumpReceiver Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 15-robolectric-12650502
Show newest version
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * 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 com.android.shell;

import static com.android.shell.BugreportProgressService.isTv;

import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.FileUtils;
import android.os.Process;
import android.text.format.DateUtils;
import android.util.Log;

import java.io.File;

/**
 * Receiver that handles finished heap dumps.
 */
public class HeapDumpReceiver extends BroadcastReceiver {
    private static final String TAG = "HeapDumpReceiver";

    /**
     * Broadcast action to determine when to delete a specific dump heap. Must include a {@link
     * HeapDumpActivity#KEY_URI} String extra.
     */
    static final String ACTION_DELETE_HEAP_DUMP = "com.android.shell.action.DELETE_HEAP_DUMP";

    /** Broadcast sent when heap dump collection has been completed. */
    private static final String ACTION_HEAP_DUMP_FINISHED =
            "com.android.internal.intent.action.HEAP_DUMP_FINISHED";

    /** The process we are reporting */
    static final String EXTRA_PROCESS_NAME = "com.android.internal.extra.heap_dump.PROCESS_NAME";

    /** The size limit the process reached. */
    static final String EXTRA_SIZE_BYTES = "com.android.internal.extra.heap_dump.SIZE_BYTES";

    /** Whether the user initiated the dump or not. */
    static final String EXTRA_IS_USER_INITIATED =
            "com.android.internal.extra.heap_dump.IS_USER_INITIATED";

    /** Optional name of package to directly launch. */
    static final String EXTRA_REPORT_PACKAGE =
            "com.android.internal.extra.heap_dump.REPORT_PACKAGE";

    private static final String NOTIFICATION_CHANNEL_ID = "heapdumps";
    private static final int NOTIFICATION_ID = 2019;

    /**
     * Always keep heap dumps taken in the last week.
     */
    private static final long MIN_KEEP_AGE_MS = DateUtils.WEEK_IN_MILLIS;

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "onReceive(): " + intent);
        final String action = intent.getAction();
        if (action == null) {
            Log.e(TAG, "null action received");
            return;
        }
        switch (action) {
            case Intent.ACTION_BOOT_COMPLETED:
                cleanupOldFiles(context);
                break;
            case ACTION_DELETE_HEAP_DUMP:
                deleteHeapDump(context, intent.getStringExtra(HeapDumpActivity.KEY_URI));
                break;
            case ACTION_HEAP_DUMP_FINISHED:
                showDumpNotification(context, intent);
                break;
        }
    }

    private void cleanupOldFiles(Context context) {
        final PendingResult result = goAsync();
        new AsyncTask() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    Log.d(TAG, "Deleting from " + new File(context.getFilesDir(), "heapdumps"));
                    FileUtils.deleteOlderFiles(new File(context.getFilesDir(), "heapdumps"), 0,
                            MIN_KEEP_AGE_MS);
                } catch (RuntimeException e) {
                    Log.e(TAG, "Couldn't delete old files", e);
                }
                result.finish();
                return null;
            }
        }.execute();
    }

    private void deleteHeapDump(Context context, @Nullable final String uri) {
        if (uri == null) {
            Log.e(TAG, "null URI for delete heap dump intent");
            return;
        }
        final PendingResult result = goAsync();
        new AsyncTask() {
            @Override
            protected Void doInBackground(Void... params) {
                context.getContentResolver().delete(Uri.parse(uri), null, null);
                result.finish();
                return null;
            }
        }.execute();
    }

    private void showDumpNotification(Context context, Intent intent) {
        final boolean isUserInitiated = intent.getBooleanExtra(
                EXTRA_IS_USER_INITIATED, false);
        final String procName = intent.getStringExtra(EXTRA_PROCESS_NAME);
        final int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);

        final String reportPackage = intent.getStringExtra(
                EXTRA_REPORT_PACKAGE);
        final long size = intent.getLongExtra(EXTRA_SIZE_BYTES, 0);

        if (procName == null) {
            Log.e(TAG, "No process name sent over");
            return;
        }

        NotificationManager nm = NotificationManager.from(context);
        nm.createNotificationChannel(
                new NotificationChannel(NOTIFICATION_CHANNEL_ID,
                        "Heap dumps",
                        NotificationManager.IMPORTANCE_DEFAULT));

        final int titleId = isUserInitiated
                ? com.android.internal.R.string.dump_heap_ready_notification
                : com.android.internal.R.string.dump_heap_notification;
        final String procDisplayName = uid == Process.SYSTEM_UID
                ? context.getString(com.android.internal.R.string.android_system_label)
                : procName;
        String text = context.getString(titleId, procDisplayName);

        Intent shareIntent = new Intent();
        shareIntent.setClassName(context, HeapDumpActivity.class.getName());
        shareIntent.putExtra(EXTRA_PROCESS_NAME, procName);
        shareIntent.putExtra(EXTRA_SIZE_BYTES, size);
        shareIntent.putExtra(EXTRA_IS_USER_INITIATED, isUserInitiated);
        shareIntent.putExtra(Intent.EXTRA_UID, uid);
        if (reportPackage != null) {
            shareIntent.putExtra(EXTRA_REPORT_PACKAGE, reportPackage);
        }
        final Notification.Builder builder = new Notification.Builder(context,
                NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(
                        isTv(context) ? R.drawable.ic_bug_report_black_24dp
                                : com.android.internal.R.drawable.stat_sys_adb)
                .setLocalOnly(true)
                .setColor(context.getColor(
                        com.android.internal.R.color.system_notification_accent_color))
                .setContentTitle(text)
                .setTicker(text)
                .setAutoCancel(true)
                .setContentText(context.getText(
                        com.android.internal.R.string.dump_heap_notification_detail))
                .setContentIntent(PendingIntent.getActivity(context, 2, shareIntent,
                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));

        Log.v(TAG, "Creating share heap dump notification");
        NotificationManager.from(context).notify(NOTIFICATION_ID, builder.build());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy