Skip to main content

Overview

APK Extractor provides powerful application extraction capabilities, displaying real application names (not just package names), detecting APK formats, calculating exact file sizes, and enabling filtering and searching across all installed apps.

Real Application Names

Instead of showing cryptic package names like com.whatsapp, APK Extractor displays the actual app name like WhatsApp.

Name Extraction Process

The system uses multiple fallback methods to extract the real app name:
// Method 1: Check nonLocalizedLabel
let appName = null;
const m = dump.match(/nonLocalizedLabel=([^\s\n\r]+)/);
if (m && m[1] && m[1] !== 'null') appName = m[1].replace(/"/g, '');

// Method 2: Check label field
if (!appName) {
  const lm = dump.match(/label="([^"]+)"/); 
  if (lm) appName = lm[1];
}

// Method 3: Fallback to formatted package name
if (!appName) {
  const seg = pkg.split('.').pop() || pkg;
  appName = seg.charAt(0).toUpperCase() + seg.slice(1).replace(/([A-Z])/g, ' $1').trim();
}
The system queries dumpsys package for each app to extract the application label defined in its manifest.

Format Detection

APK Extractor automatically detects whether an app is a single APK or a Split APK (multiple files):
const pathOut = await tryRunAdb(`shell pm path ${pkg}`, serial);
const paths = pathOut.split('\n').map(l => l.replace('package:', '').trim()).filter(Boolean);
const isSplit = paths.length > 1;
const format = paths.length === 0 ? 'N/A' : isSplit ? 'Split APK' : 'APK';

APK Types

FormatDescriptionFile CountExtraction Method
APKSingle file application1Direct download
Split APKMulti-file application2+Compile to XAPK
Split APKs contain separate files for different CPU architectures, screen densities, and language resources. They must be compiled into XAPK format for installation.

Size Calculation

APK Extractor calculates exact file sizes in bytes using ADB’s stat command:
const sizes = await Promise.all(paths.map(async (p) => {
  try {
    const szOut = await runAdb(`shell stat -c "%s" "${p}"`, serial, 5000);
    const sz = parseInt(szOut.trim(), 10);
    return isNaN(sz) ? 0 : sz;
  } catch { return 0; }
}));
totalSizeBytes = sizes.reduce((a, b) => a + b, 0);
const sizeMB = totalSizeBytes > 0 ? (totalSizeBytes / 1048576).toFixed(1) + ' MB' : 'N/A';

Why stat Instead of du?

  • Precision: stat -c "%s" returns exact byte count
  • Speed: Faster than du -b for individual files
  • Reliability: Works on all Android versions
For Split APKs with multiple files, sizes are calculated in parallel and summed for total app size.

Filtering by Type

Filter apps by installation type:
const { type = 'user' } = req.query;
const flag = type === 'user' ? '-3' : type === 'system' ? '-s' : '';
const out = await runAdb(`shell pm list packages ${flag}`, serial, 30000);

Filter Options

  • User Apps (-3): Apps installed by the user
  • System Apps (-s): Pre-installed system applications
  • All Apps: Both user and system apps
adb shell pm list packages -3

Search Functionality

The web interface provides instant search across:
  • Application names (e.g., “WhatsApp”)
  • Package names (e.g., “com.whatsapp”)
  • Partial matches (e.g., “what” matches “WhatsApp”)
Search is client-side for instant results without server round-trips.

Batch Info Processing

To display app lists quickly, APK Extractor uses parallel batch processing:
app.post('/api/devices/:serial/apps/batch-info', async (req, res) => {
  const { serial } = req.params;
  const { packages } = req.body;
  
  // Process all packages in parallel
  const entries = await Promise.all(packages.map(async (pkg) => {
    try {
      const [pathOut, dump] = await Promise.all([
        tryRunAdb(`shell pm path ${pkg}`, serial),
        tryRunAdb(`shell dumpsys package ${pkg}`, serial),
      ]);
      
      // Extract format, size, and name
      // ...
      
      return [pkg, { format, isSplit, sizeMB, appName }];
    } catch {
      // Fallback for errors
    }
  }));
  
  const results = Object.fromEntries(entries);
  res.json({ results });
});

Batch Processing Benefits

  • 100% Parallel: All packages processed simultaneously
  • No Blocking: Uses async/await throughout
  • Fast Display: Shows results as they arrive
  • Progress Tracking: UI displays “X / Y apps” during loading
Processing 100 apps takes approximately the same time as processing 10 apps due to parallel execution.

Direct Download to Browser

APK files stream directly to the browser without server-side storage:
app.post('/api/devices/:serial/apps/:package/extract', async (req, res) => {
  const { serial, package: pkg } = req.params;
  
  // Get APK path from device
  const pathOut = await tryRunAdb(`shell pm path ${pkg}`, serial);
  const paths = pathOut.split('\n').map(l => l.replace('package:', '').trim()).filter(Boolean);
  
  // Only single APKs can be extracted directly
  if (paths.length > 1) {
    return res.status(400).json({
      error: 'Split APK / XAPK detectado',
      detail: `Esta app tiene ${paths.length} archivos APK separados.`,
      isSplit: true
    });
  }
  
  const devicePath = paths[0];
  const outDir = path.join(os.tmpdir(), 'apk-extractor');
  const outFile = path.join(outDir, `${pkg}.apk`);
  
  // Pull APK from device
  await runAdb(`pull "${devicePath}" "${outFile}"`, serial, 60000);
  
  // Stream to browser
  res.setHeader('Content-Disposition', `attachment; filename="${pkg}.apk"`);
  res.setHeader('Content-Type', 'application/vnd.android.package-archive');
  const stream = fs.createReadStream(outFile);
  stream.pipe(res);
  
  // Clean up after download
  stream.on('close', () => { fs.unlinkSync(outFile); });
});

Download Security

  • Files saved to temp directory (os.tmpdir())
  • Automatically deleted after download completes
  • No permanent server-side storage
  • Direct browser download with proper MIME type

Application Info API

Get detailed information about any installed app:
GET /api/devices/:serial/apps/:package/info
Response:
{
  "pkg": "com.whatsapp",
  "appName": "WhatsApp",
  "versionName": "2.23.5.76",
  "versionCode": "223576",
  "firstInstall": "2023-01-15 10:30:45",
  "lastUpdate": "2023-03-20 14:22:10",
  "installer": "com.android.vending",
  "apkPaths": ["/data/app/com.whatsapp-xyz/base.apk"],
  "isSplit": false,
  "format": "APK",
  "sizeMB": "58.3 MB",
  "sizeBytes": 61145088
}

Performance Features

Caching

  • Package list cached after first load
  • Batch info cached per device
  • Switching filters (user/system) shows instant cached results

Async Operations

  • Zero blocking operations
  • All ADB calls use async/await
  • Parallel processing for multiple apps
  • Responsive UI during long operations

Error Handling

try {
  // Attempt extraction
} catch {
  // Graceful fallback
  const seg = pkg.split('.').pop() || pkg;
  return [pkg, {
    format: 'N/A',
    isSplit: false,
    sizeMB: 'N/A',
    appName: seg.charAt(0).toUpperCase() + seg.slice(1),
  }];
}
If any app info extraction fails, the system provides sensible defaults to keep the list functioning.

Build docs developers (and LLMs) love