/* * Copyright (C) 2013 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.launcher3; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.net.Uri; import android.os.*; import android.util.Log; import java.io.*; import java.util.ArrayList; import java.util.Arrays; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class MemoryDumpActivity extends Activity { private static final String TAG = "MemoryDumpActivity"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } public static String zipUp(ArrayList paths) { final int BUFSIZ = 256 * 1024; // 256K final byte[] buf = new byte[BUFSIZ]; final String zipfilePath = String.format("%s/hprof-%d.zip", Environment.getExternalStorageDirectory(), System.currentTimeMillis()); ZipOutputStream zos = null; try { OutputStream os = new FileOutputStream(zipfilePath); zos = new ZipOutputStream(new BufferedOutputStream(os)); for (String filename : paths) { InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(filename)); ZipEntry entry = new ZipEntry(filename); zos.putNextEntry(entry); int len; while ( 0 < (len = is.read(buf, 0, BUFSIZ)) ) { zos.write(buf, 0, len); } zos.closeEntry(); } finally { is.close(); } } } catch (IOException e) { Log.e(TAG, "error zipping up profile data", e); return null; } finally { if (zos != null) { try { zos.close(); } catch (IOException e) { // ugh, whatever } } } return zipfilePath; } public static void dumpHprofAndShare(final Context context, MemoryTracker tracker) { final StringBuilder body = new StringBuilder(); final ArrayList paths = new ArrayList(); final int myPid = android.os.Process.myPid(); final int[] pids_orig = tracker.getTrackedProcesses(); final int[] pids_copy = Arrays.copyOf(pids_orig, pids_orig.length); for (int pid : pids_copy) { MemoryTracker.ProcessMemInfo info = tracker.getMemInfo(pid); if (info != null) { body.append("pid ").append(pid).append(":") .append(" up=").append(info.getUptime()) .append(" pss=").append(info.currentPss) .append(" uss=").append(info.currentUss) .append("\n"); } if (pid == myPid) { final String path = String.format("%s/launcher-memory-%d.ahprof", Environment.getExternalStorageDirectory(), pid); Log.v(TAG, "Dumping memory info for process " + pid + " to " + path); try { android.os.Debug.dumpHprofData(path); // will block } catch (IOException e) { Log.e(TAG, "error dumping memory:", e); } paths.add(path); } } String zipfile = zipUp(paths); if (zipfile == null) return; Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("application/zip"); final PackageManager pm = context.getPackageManager(); shareIntent.putExtra(Intent.EXTRA_SUBJECT, String.format("Launcher memory dump (%d)", myPid)); String appVersion; try { appVersion = pm.getPackageInfo(context.getPackageName(), 0).versionName; } catch (PackageManager.NameNotFoundException e) { appVersion = "?"; } body.append("\nApp version: ").append(appVersion).append("\nBuild: ").append(Build.DISPLAY).append("\n"); shareIntent.putExtra(Intent.EXTRA_TEXT, body.toString()); final File pathFile = new File(zipfile); final Uri pathUri = Uri.fromFile(pathFile); shareIntent.putExtra(Intent.EXTRA_STREAM, pathUri); context.startActivity(shareIntent); } @Override public void onStart() { super.onStart(); startDump(this, new Runnable() { @Override public void run() { finish(); } }); } public static void startDump(final Context context) { startDump(context, null); } public static void startDump(final Context context, final Runnable andThen) { final ServiceConnection connection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { Log.v(TAG, "service connected, dumping..."); dumpHprofAndShare(context, ((MemoryTracker.MemoryTrackerInterface) service).getService()); context.unbindService(this); if (andThen != null) andThen.run(); } public void onServiceDisconnected(ComponentName className) { } }; Log.v(TAG, "attempting to bind to memory tracker"); context.bindService(new Intent(context, MemoryTracker.class), connection, Context.BIND_AUTO_CREATE); } }