Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 100 additions & 19 deletions docs/js/adb/account-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,36 +64,117 @@ export async function deviceHasAccounts(adb) {

export async function disableAccountApps(adb) {
if (!adb) return [];

const disabled = new Set();
const packagesToDisable = new Set([
'com.microsoft.office.officehubrow',
'com.microsoft.office.word',
'com.microsoft.office.excel',
'com.microsoft.office.outlook',
'com.microsoft.office.powerpoint',
'com.microsoft.skydrive',
'com.microsoft.appmanager',
'com.facebook.katana',
'com.facebook.orca',
'com.whatsapp',
'com.google.android.gm',
'com.google.android.gsf.login',
'com.google.android.gms',
'com.samsung.android.mobileservice',
]);

const addPackage = pkg => {
if (!pkg) return;
const clean = pkg.trim();
if (!clean || clean === 'android') return;
if (!/^[a-zA-Z0-9_.]+$/.test(clean)) return;
packagesToDisable.add(clean);
};

const accountTypePackageMap = {
'com.google': ['com.google.android.gsf.login', 'com.google.android.gms'],
'com.google.work': ['com.google.android.gm', 'com.google.android.gsf.login', 'com.google.android.gms'],
'com.google.android.gm.exchange': ['com.google.android.gm'],
'com.android.exchange': ['com.google.android.gm'],
'com.microsoft.exchange': ['com.microsoft.office.outlook'],
'com.microsoft.workaccount': ['com.microsoft.appmanager'],
'com.facebook.auth.login': ['com.facebook.katana', 'com.facebook.orca'],
'com.whatsapp': ['com.whatsapp'],
'com.samsung.android.mobileservice': ['com.samsung.android.mobileservice'],
};

const commands = [
'cmd account list --user 0',
'cmd account list',
'dumpsys account',
];

const outputs = [];
for (const cmd of commands) {
try {
const out = await adb.executeShellCommand(cmd);
if (out && out.trim()) outputs.push(out);
} catch (e) {
console.log(`command ${cmd} failed`, e?.message || e);
}
}

const combined = outputs.join('\n\n');
const componentRegex = /ComponentInfo\{([^\/}]+)\/[^}]+\}/g;
let match;
while ((match = componentRegex.exec(combined))) {
addPackage(match[1]);
}

const packageRegex = /packageName=([a-zA-Z0-9_.]+)/g;
while ((match = packageRegex.exec(combined))) {
addPackage(match[1]);
}

const authenticatorRegex = /AuthenticatorDescription \{([^}]+)\}/g;
while ((match = authenticatorRegex.exec(combined))) {
const block = match[1];
const typeMatch = /type=([^,\s]+)/.exec(block);
const packageMatch = /packageName=([^,\s]+)/.exec(block);
if (packageMatch) addPackage(packageMatch[1]);
if (typeMatch && accountTypePackageMap[typeMatch[1]]) {
for (const pkg of accountTypePackageMap[typeMatch[1]]) addPackage(pkg);
}
}

const typeRegex = /type[=:\s]+([^,\s]+)/g;
const accountTypes = new Set();
while ((match = typeRegex.exec(combined))) {
const type = match[1]?.trim();
if (type) accountTypes.add(type);
}
for (const type of accountTypes) {
const mapped = accountTypePackageMap[type];
if (mapped) {
for (const pkg of mapped) addPackage(pkg);
}
}

const run = async pkg => {
if (disabled.has(pkg)) return;
try {
await adb.executeShellCommand(`pm disable-user --user 0 ${pkg}`);
disabled.add(pkg);
console.log('disabled', pkg);
} catch (e) {
console.log('failed to disable', pkg);
console.log('failed to disable', pkg, e?.message || e);
}
};
const manualPkgs = [
'com.microsoft.office.officehubrow',
'com.microsoft.office.word',
'com.microsoft.office.excel',
'com.microsoft.office.outlook',
'com.microsoft.office.powerpoint',
];
for (const pkg of manualPkgs) {

for (const pkg of packagesToDisable) {
await run(pkg);
}
try {
const out = await adb.executeShellCommand('dumpsys account');
const re = /ComponentInfo\{([^\/}]+)\/[^}]+\}/g;
let m;
while ((m = re.exec(out))) {
await run(m[1]);
}
} catch (e) {
console.log('dumpsys account error', e?.message || e);

// Give the system a brief moment to acknowledge the disabled authenticators
if (disabled.size > 0) {
await new Promise(resolve => setTimeout(resolve, 1000));
}

return Array.from(disabled);
}

Expand All @@ -102,7 +183,7 @@ export async function reenablePackages(adb, packages) {
console.log('reenablePackages', packages);
for (const pkg of packages) {
try {
await adb.executeShellCommand(`pm enable ${pkg}`);
await adb.executeShellCommand(`pm enable --user 0 ${pkg}`);
console.log('reenabled', pkg);
} catch (e) {
console.log('failed to reenable', pkg);
Expand Down
10 changes: 10 additions & 0 deletions docs/js/apps.js
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ class JTechMDMInstaller {
const isLocal = location.hostname === 'localhost' || location.hostname === '127.0.0.1';
try {
if (isLocal) {
// Prefer dynamic API in dev to avoid 404 noise
const res = await fetch('/api/apks');
if (!res.ok) throw new Error('/api/apks failed');
apks = await res.json();
Expand All @@ -429,6 +430,7 @@ class JTechMDMInstaller {
apks = await res.json();
}
} catch (err) {
// Fallback to the other source
const res = await fetch(isLocal ? 'apks.json' : '/api/apks');
apks = await res.json();
}
Expand Down Expand Up @@ -956,6 +958,11 @@ class JTechMDMInstaller {
if (hasAccounts) {
this.uiManager.logToConsole('Accounts detected - temporarily disabling account apps...', 'warning');
const disabled = await disableAccountApps(this.adbConnection);
if (disabled.length > 0) {
this.uiManager.logToConsole(`Temporarily disabled: ${disabled.join(', ')}`, 'info');
} else {
this.uiManager.logToConsole('No specific account packages detected; proceeding with retry.', 'warning');
}
try {
this.uiManager.logToConsole('Retrying device owner command...', 'info');
result = await this.adbConnection.executeShellCommand(command);
Expand All @@ -965,6 +972,9 @@ class JTechMDMInstaller {
}
} finally {
await reenablePackages(this.adbConnection, disabled);
if (disabled.length > 0) {
this.uiManager.logToConsole('Re-enabled previously disabled account packages.', 'info');
}
}
if (!/success/i.test(result)) {
// Surface a clear warning but keep full output visible above
Expand Down
119 changes: 100 additions & 19 deletions js/adb/account-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,36 +64,117 @@ export async function deviceHasAccounts(adb) {

export async function disableAccountApps(adb) {
if (!adb) return [];

const disabled = new Set();
const packagesToDisable = new Set([
'com.microsoft.office.officehubrow',
'com.microsoft.office.word',
'com.microsoft.office.excel',
'com.microsoft.office.outlook',
'com.microsoft.office.powerpoint',
'com.microsoft.skydrive',
'com.microsoft.appmanager',
'com.facebook.katana',
'com.facebook.orca',
'com.whatsapp',
'com.google.android.gm',
'com.google.android.gsf.login',
'com.google.android.gms',
'com.samsung.android.mobileservice',
]);

const addPackage = pkg => {
if (!pkg) return;
const clean = pkg.trim();
if (!clean || clean === 'android') return;
if (!/^[a-zA-Z0-9_.]+$/.test(clean)) return;
packagesToDisable.add(clean);
};

const accountTypePackageMap = {
'com.google': ['com.google.android.gsf.login', 'com.google.android.gms'],
'com.google.work': ['com.google.android.gm', 'com.google.android.gsf.login', 'com.google.android.gms'],
'com.google.android.gm.exchange': ['com.google.android.gm'],
'com.android.exchange': ['com.google.android.gm'],
'com.microsoft.exchange': ['com.microsoft.office.outlook'],
'com.microsoft.workaccount': ['com.microsoft.appmanager'],
'com.facebook.auth.login': ['com.facebook.katana', 'com.facebook.orca'],
'com.whatsapp': ['com.whatsapp'],
'com.samsung.android.mobileservice': ['com.samsung.android.mobileservice'],
};

const commands = [
'cmd account list --user 0',
'cmd account list',
'dumpsys account',
];

const outputs = [];
for (const cmd of commands) {
try {
const out = await adb.executeShellCommand(cmd);
if (out && out.trim()) outputs.push(out);
} catch (e) {
console.log(`command ${cmd} failed`, e?.message || e);
}
}

const combined = outputs.join('\n\n');
const componentRegex = /ComponentInfo\{([^\/}]+)\/[^}]+\}/g;
let match;
while ((match = componentRegex.exec(combined))) {
addPackage(match[1]);
}

const packageRegex = /packageName=([a-zA-Z0-9_.]+)/g;
while ((match = packageRegex.exec(combined))) {
addPackage(match[1]);
}

const authenticatorRegex = /AuthenticatorDescription \{([^}]+)\}/g;
while ((match = authenticatorRegex.exec(combined))) {
const block = match[1];
const typeMatch = /type=([^,\s]+)/.exec(block);
const packageMatch = /packageName=([^,\s]+)/.exec(block);
if (packageMatch) addPackage(packageMatch[1]);
if (typeMatch && accountTypePackageMap[typeMatch[1]]) {
for (const pkg of accountTypePackageMap[typeMatch[1]]) addPackage(pkg);
}
}

const typeRegex = /type[=:\s]+([^,\s]+)/g;
const accountTypes = new Set();
while ((match = typeRegex.exec(combined))) {
const type = match[1]?.trim();
if (type) accountTypes.add(type);
}
for (const type of accountTypes) {
const mapped = accountTypePackageMap[type];
if (mapped) {
for (const pkg of mapped) addPackage(pkg);
}
}

const run = async pkg => {
if (disabled.has(pkg)) return;
try {
await adb.executeShellCommand(`pm disable-user --user 0 ${pkg}`);
disabled.add(pkg);
console.log('disabled', pkg);
} catch (e) {
console.log('failed to disable', pkg);
console.log('failed to disable', pkg, e?.message || e);
}
};
const manualPkgs = [
'com.microsoft.office.officehubrow',
'com.microsoft.office.word',
'com.microsoft.office.excel',
'com.microsoft.office.outlook',
'com.microsoft.office.powerpoint',
];
for (const pkg of manualPkgs) {

for (const pkg of packagesToDisable) {
await run(pkg);
}
try {
const out = await adb.executeShellCommand('dumpsys account');
const re = /ComponentInfo\{([^\/}]+)\/[^}]+\}/g;
let m;
while ((m = re.exec(out))) {
await run(m[1]);
}
} catch (e) {
console.log('dumpsys account error', e?.message || e);

// Give the system a brief moment to acknowledge the disabled authenticators
if (disabled.size > 0) {
await new Promise(resolve => setTimeout(resolve, 1000));
}

return Array.from(disabled);
}

Expand All @@ -102,7 +183,7 @@ export async function reenablePackages(adb, packages) {
console.log('reenablePackages', packages);
for (const pkg of packages) {
try {
await adb.executeShellCommand(`pm enable ${pkg}`);
await adb.executeShellCommand(`pm enable --user 0 ${pkg}`);
console.log('reenabled', pkg);
} catch (e) {
console.log('failed to reenable', pkg);
Expand Down
8 changes: 8 additions & 0 deletions js/apps.js
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,11 @@ class JTechMDMInstaller {
if (hasAccounts) {
this.uiManager.logToConsole('Accounts detected - temporarily disabling account apps...', 'warning');
const disabled = await disableAccountApps(this.adbConnection);
if (disabled.length > 0) {
this.uiManager.logToConsole(`Temporarily disabled: ${disabled.join(', ')}`, 'info');
} else {
this.uiManager.logToConsole('No specific account packages detected; proceeding with retry.', 'warning');
}
try {
this.uiManager.logToConsole('Retrying device owner command...', 'info');
result = await this.adbConnection.executeShellCommand(command);
Expand All @@ -967,6 +972,9 @@ class JTechMDMInstaller {
}
} finally {
await reenablePackages(this.adbConnection, disabled);
if (disabled.length > 0) {
this.uiManager.logToConsole('Re-enabled previously disabled account packages.', 'info');
}
}
if (!/success/i.test(result)) {
// Surface a clear warning but keep full output visible above
Expand Down