blob: f1e39da8677db9201bd2cf3582950af9edbac61d [file] [log] [blame]
// Copyright 2016 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'dart:io';
import '../mobile/device.dart';
import '../mobile/device_spec.dart';
import '../globals.dart';
import '../util.dart';
Future<List<String>> getAndroidDeviceIDs() async {
List<String> androidIDs = <String>[];
Process process = await Process.start('adb', ['devices']);
RegExp androidIDPattern = new RegExp(r'^(\S+)\s+device$');
Stream lineStream = process.stdout
.transform(new Utf8Decoder())
.transform(new LineSplitter());
await for (var line in lineStream) {
Match androidIDMatcher = androidIDPattern.firstMatch(line.toString());
if (androidIDMatcher != null) {
String androidID = androidIDMatcher.group(1);
androidIDs.add(androidID);
}
}
return androidIDs;
}
const String lockProp = 'mHoldingWakeLockSuspendBlocker';
/// Check if the device is locked
Future<bool> _deviceIsLocked(Device device) async {
Process process = await Process.start(
'adb',
['-s', '${device.id}', 'shell', 'dumpsys', 'power']
);
bool isLocked;
RegExp lockStatusPattern = new RegExp(lockProp + r'=(.*)');
Stream lineStream = process.stdout
.transform(new Utf8Decoder())
.transform(new LineSplitter());
await for (var line in lineStream) {
Match lockMatcher = lockStatusPattern.firstMatch(line.toString());
if (lockMatcher != null) {
isLocked = lockMatcher.group(1) == 'false';
break;
}
}
process.stderr.drain();
await process.exitCode;
return isLocked;
}
/// Unlock devices if the device is locked
Future<int> unlockDevice(Device device) async {
bool isLocked = await _deviceIsLocked(device);
if (isLocked == null) {
printError('adb error: cannot find device $lockProp property');
return 1;
}
if (!isLocked) return 0;
Process wakeUpAndUnlockProcess = await Process.start(
'adb',
['-s', '${device.id}', 'shell', 'input', 'keyevent', 'KEYCODE_MENU']
);
wakeUpAndUnlockProcess.stdout.drain();
wakeUpAndUnlockProcess.stderr.drain();
return await wakeUpAndUnlockProcess.exitCode;
}
// Uninstall an Android app
Future<int> uninstallAndroidTestedApp(DeviceSpec spec, Device device) async {
String androidManifestPath
= normalizePath(spec.appRootPath, 'android/AndroidManifest.xml');
String androidManifest = await new File(androidManifestPath).readAsString();
RegExp packagePattern
= new RegExp(r'<manifest[\s\S]*?package="(\S+)"\s+[\s\S]*?>', multiLine: true);
Match packageMatcher = packagePattern.firstMatch(androidManifest);
if (packageMatcher == null) {
printError('Package name not found in $androidManifestPath');
return 1;
}
String packageName = packageMatcher.group(1);
Process uninstallProcess = await Process.start(
'adb',
['-s', '${device.id}', 'uninstall', '$packageName']
);
Stream lineStream = uninstallProcess.stdout
.transform(new Utf8Decoder())
.transform(new LineSplitter());
await for (var line in lineStream) {
printTrace(
'Uninstall $packageName on device ${device.id}: ${line.toString().trim()}'
);
}
uninstallProcess.stderr.drain();
return uninstallProcess.exitCode;
}
/// Get device property
Future<String> getAndroidProperty(String deviceID, String propName) async {
ProcessResult results = await Process.run(
'adb',
['-s', deviceID, 'shell', 'getprop', propName]
);
return results.stdout.toString().trim();
}
/// Get device pixels and dpi to compute screen diagonal size in inches
Future<String> getAndroidScreenSize(String deviceID) async {
Process sizeProcess = await Process.start(
'adb',
['-s', '$deviceID', 'shell', 'wm', 'size']
);
RegExp sizePattern = new RegExp(r'Physical size:\s*(\d+)x(\d+)');
Stream sizeLineStream = sizeProcess.stdout
.transform(new Utf8Decoder())
.transform(new LineSplitter());
int xSize;
int ySize;
await for (var line in sizeLineStream) {
Match sizeMatcher = sizePattern.firstMatch(line.toString());
if (sizeMatcher != null) {
xSize = int.parse(sizeMatcher.group(1));
ySize = int.parse(sizeMatcher.group(2));
break;
}
}
if (xSize == null || ySize == null) {
printError('Screen size not found.');
return null;
}
sizeProcess.stderr.drain();
Process densityProcess = await Process.start(
'adb',
['-s', '$deviceID', 'shell', 'wm', 'density']
);
RegExp densityPattern = new RegExp(r'Physical density:\s*(\d+)');
Stream densityLineStream = densityProcess.stdout
.transform(new Utf8Decoder())
.transform(new LineSplitter());
int density;
await for (var line in densityLineStream) {
Match densityMatcher = densityPattern.firstMatch(line.toString());
if (densityMatcher != null) {
density = int.parse(densityMatcher.group(1));
break;
}
}
if (density == null) {
printError('Density not found.');
return null;
}
densityProcess.stderr.drain();
double xInch = xSize / density;
double yInch = ySize / density;
double diagonalSize = sqrt(xInch * xInch + yInch * yInch);
return categorizeScreenSize(diagonalSize);
}
Future<Device> collectAndroidDeviceProps(String deviceID, {String groupKey}) async {
return new Device(
properties: <String, String> {
'platform': 'android',
'device-id': deviceID,
'model-name': await getAndroidProperty(deviceID, 'ro.product.model'),
'os-version': expandOSVersion(
await getAndroidProperty(deviceID, 'ro.build.version.release')
),
'screen-size': await getAndroidScreenSize(deviceID)
},
groupKey: groupKey
);
}