blob: bf522a28b399d2d34b41ab1da969e407fe9c9307 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 utils;
import java.io.*;
import java.lang.reflect.Array;
import java.net.*;
import java.util.*;
import javax.imageio.*;
//import javax.mail.*;
//import javax.mail.internet.*;
//import utils.HtmlNotify;
public class MobileUtil {
// OSs
public static final String ANDROID_OS = "android";
public static final String IOS = "ios"; // "iPhone OS" is what the iPod Touch 4G returns, anyway.
public static final String QNX = "qnx";
public static final String MAC = "mac";
public static final String WIN = "win";
public static final String WINDOWS = "windows";
// devices
// Get rid of these two when we do device types (soon).
public static final String ANDROID = "android";
public static final String ANDROID2 = "Android";
public static final String PLAYBOOK = "playbook";
public static final String DESIRE = "desire";
public static final String DROID = "droid";
public static final String DROID_2 = "droid2";
public static final String DROID_X = "droidX";
public static final String DROID_PRO = "droidPro";
public static final String NEXUS_ONE = "nexusOne";
public static final String EVO = "evo";
public static final String INCREDIBLE = "incredible";
public static final String XOOM = "xoom";
public static final String SDCARD_DIR = "/sdcard/mustella";
public static final String IPAD = "iPad";
public static final String IPAD2 = "iPad2";
public static final String IPAD3 = "iPad3";
public static final String IPAD4 = "iPad4";
public static final String IPOD_TOUCH_3GS = "iPodTouch3GS";
public static final String IPOD_TOUCH_4G = "iPodTouch4G";
public static final String IPOD_TOUCH_5G = "iPodTouch5G";
public static final String ANDROID_TABLET = "androidTablet"; // not used anywhere yet, not sure if this is a good name.
// Useful collections
public static final String[] DEVICES_USING_ANDROID = {ANDROID, ANDROID2, DESIRE, DROID, DROID_2, DROID_X, DROID_PRO, NEXUS_ONE, EVO, INCREDIBLE, ANDROID_TABLET, XOOM};
public static final String[] DEVICES_USING_IOS = {IPAD, IPAD2, IPOD_TOUCH_3GS, IPOD_TOUCH_4G, IPOD_TOUCH_5G};
public static final String[] DEVICES_USING_QNX = {PLAYBOOK};
public static final String[] DEVICES_USING_SDCARD = {ANDROID, ANDROID2, DESIRE, DROID, DROID_2, DROID_X, DROID_PRO, NEXUS_ONE, EVO, INCREDIBLE, ANDROID_TABLET, XOOM};
public static final String[] DEVICES_AROUND_160PPI = {WIN, DROID_PRO, ANDROID_TABLET, XOOM, IPOD_TOUCH_3GS, IPAD, IPAD2, PLAYBOOK};
public static final String[] DEVICES_AROUND_240PPI = {ANDROID, ANDROID2, DESIRE, DROID, DROID_2, DROID_X, NEXUS_ONE, EVO, INCREDIBLE};
public static final String[] DEVICES_AROUND_320PPI = {WIN, IPOD_TOUCH_4G, IPOD_TOUCH_5G, IPAD3, IPAD4};
public static final String[] DEVICES_AROUND_480PPI = {};
// Other
public static final String MOBILE_FRAMEWORK_DIR = "MobileConfig";
public static final String IOS_PRODUCTNAME = "ProductName:";
public static final String IOS_PRODUCTVERSION = "ProductVersion:";
public static final String IOS_PROCESSOR = "Processor type:";
public static final String IOS_MEMORYAVAILABLE = "Primary memory available:";
/**
* getOSForDevice
* Given a device type, return the OS. e.g. Input "nexus_one", get back "android".
**/
public static String getOSForDevice(String device_name){
int i = 0;
String ret = null;
for( i = 0; i < Array.getLength( DEVICES_USING_ANDROID ); ++i){
if( device_name.compareToIgnoreCase( DEVICES_USING_ANDROID[ i ] ) == 0 ){
ret = ANDROID_OS;
}
}
if( ret == null ){
for( i = 0; i < Array.getLength( DEVICES_USING_IOS ); ++i){
if( device_name.compareToIgnoreCase( DEVICES_USING_IOS[ i ] ) == 0 ){
ret = IOS;
}
}
}
if( ret == null ){
for( i = 0; i < Array.getLength( DEVICES_USING_QNX ); ++i){
if( device_name.compareToIgnoreCase( DEVICES_USING_QNX[ i ] ) == 0 ){
ret = QNX;
}
}
}
// When we run mobile tests on the desktop...
if( ret == null ){
if( device_name.compareToIgnoreCase( MobileUtil.MAC ) == 0 )
return MobileUtil.MAC;
else if( device_name.compareToIgnoreCase( MobileUtil.WIN ) == 0 )
return MobileUtil.WIN;
else if( device_name.compareToIgnoreCase( MobileUtil.WINDOWS ) == 0 )
return MobileUtil.WIN;
}
return ret;
}
/**
* getDeviceIds
* Fetches the serial numbers of all devices available if they are in allowedIds.
*/
public static ArrayList getAndroidDeviceIds(String adb, int run_id, String[] allowedIds){
String line = null;
String body = null;
Process p = null;
ArrayList ret = new ArrayList();
int numOfflineDevices = 0;
int numReadyDevices = 0;
int i = 0;
InetAddress ia = null;
/**
if(allowedIds != null){
System.out.println("allowedIds=" + allowedIds);
for(i = 0; i < allowedIds.length; ++i){
System.out.println("\t" + allowedIds[i]);
}
}
**/
try{
if( new File(adb).exists() ){
String[] listDevices = { adb, "devices" };
p = Runtime.getRuntime().exec( listDevices );
BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
// Collect all of the device serial numbers. Note that these serial numbers aren't the
// same as the serial numbers found on the back of a device.
while ( true ){
line = br.readLine();
if( (line == null) || (line.trim().compareToIgnoreCase("") == 0) ){
break;
}
if( line.indexOf( "List of devices" ) == -1 ){
if( line.indexOf( "device" ) > -1 ){
++numReadyDevices;
line = line.substring( 0, line.indexOf( "device" ) );
line = line.trim();
//System.out.println("getAndroidDeviceIds: line=" + line);
// Make sure we're allowed to use this device.
if( allowedIds != null ){
for( i = 0; i < allowedIds.length; ++i ){
if( line.compareToIgnoreCase( allowedIds[i].trim() ) == 0 ){
System.out.println("Adding device: '" + line + "'");
ret.add( line );
}else{
//System.out.println("Found device '" + line + "', but that does not match " + allowedIds[i].trim());
}
}
}else{
// If no restricted list of IDs was given, return all of them.
ret.add( line );
}
} else if( line.indexOf( "offline" ) > -1 ) {
++numOfflineDevices;
line = line.substring( 0, line.indexOf( "offline" ) );
line = line.trim();
System.out.println("Found offline device: '" + line + "'");
}
}
}
if( ( numReadyDevices == 0 || numOfflineDevices > 0 ) && run_id > -1 ){
try {
System.out.println("There are no devices reporting at all.");
/*
ia = InetAddress.getLocalHost();
String hostname = ia.getHostName();
body = "(The following message is automatically generated by the Mustella framework.) Device trouble is ocurring on " + hostname + ". ";
if( numOfflineDevices > 0 ){
body = body + "We have " + Integer.toString(numOfflineDevices) + " offline device(s). ";
}
if( numReadyDevices + numOfflineDevices == 0 ){
body = body + "There are no devices reporting at all. ";
}
if( numReadyDevices > 0 ){
body = body + "On the bright side, at least we have " + Integer.toString(numReadyDevices) + " device(s) still able to respond, so all is not lost. ";
}
body = body + "Can you please help? Thanks! ";
InternetAddress[] to = new InternetAddress[1];
to[0] = new InternetAddress("MustellaMobileResults@adobe.com");
HtmlNotify hn = new HtmlNotify();
hn.sendMessage("inner-relay-1.corp.adobe.com",
"rvollmar@adobe.com",
to,
hostname + " mustella device offline",
body);
*/
}
catch(Exception e){
e.printStackTrace();
}
}
}
}catch( IOException e ){
e.printStackTrace();
}
return ret;
}
/**
* getAndroidOsVersion
* Asks a device for its OS version.
**/
public static String getAndroidOsVersion( String adb, String deviceId ){
String line = null;
String ret = null;
Process p = null;
try{
String[] getstats = { adb, "-s", deviceId, "shell", "getprop", "ro.build.version.release" };
p = Runtime.getRuntime().exec( getstats );
BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
while ( true ){
line = br.readLine();
if( line == null ){
break;
} else {
ret = line;
}
}
}catch( IOException e ){
e.printStackTrace();
}
// Remove the period (replace() works OK if there isn't one).
ret = ret.replace(".", "");
return MobileUtil.ANDROID_OS + ret;
}
/**
* getDeviceDensity
* Returns a number to use for the PPI.
* system.Capabilities gives an exact number, like 254. We don't want that.
* The device itself returns a general number, like "240", today, but who knows about later.
* So we're just going to hard code the sorting of devices into groups to reduce uncertainty.
**/
//public static int getDeviceDensity( String adb, String deviceId ){
public static int getDeviceDensity( String deviceId ){
int i = 0;
int ret = 0;
for( i = 0; i < Array.getLength( DEVICES_AROUND_160PPI ); ++i ){
if( deviceId.compareToIgnoreCase( DEVICES_AROUND_160PPI[ i ] ) == 0 )
ret = 160;
}
for( i = 0; i < Array.getLength( DEVICES_AROUND_240PPI ); ++i ){
if( deviceId.compareToIgnoreCase( DEVICES_AROUND_240PPI[ i ] ) == 0 )
ret = 240;
}
for( i = 0; i < Array.getLength( DEVICES_AROUND_320PPI ); ++i ){
if( deviceId.compareToIgnoreCase( DEVICES_AROUND_320PPI[ i ] ) == 0 )
ret = 320;
}
for( i = 0; i < Array.getLength( DEVICES_AROUND_480PPI ); ++i ){
if( deviceId.compareToIgnoreCase( DEVICES_AROUND_480PPI[ i ] ) == 0 )
ret = 480;
}
return ret;
/**
String line = null;
String ret = null;
Process p = null;
try{
String[] getstats = { adb, "-s", deviceId, "shell", "getprop", "ro.sf.lcd_density" };
p = Runtime.getRuntime().exec( getstats );
BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
while ( true ){
line = br.readLine();
if( line == null ){
break;
} else {
ret = line;
}
}
}catch( IOException e ){
e.printStackTrace();
}
return ret;
**/
}
/**
* getAndroidModel
* Asks a device for its model. It returns, for example, "Nexus One".
* We don't use this yet, but it might be handy.
**/
public static String getAndroidModel( String adb, String deviceId ){
String line = null;
String ret = null;
Process p = null;
try{
String[] getstats = { adb, "-s", deviceId, "shell", "getprop", "ro.product.model" };
p = Runtime.getRuntime().exec( getstats );
BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
while ( true ){
line = br.readLine();
if( line == null ){
break;
} else {
ret = line;
}
}
}catch( IOException e ){
e.printStackTrace();
}
return ret;
}
/**
* pingIOSDevice
* Checks to see if the device we're ssh'ing to is an "iPhone".
* sw_vers returns:
* ProductName: iPhone OS
* ProductVersion: 4.1
* BuildVersion: 8B118
**/
public static boolean pingIOSDevice(){
String line = null;
boolean ret = false;
Process p = null;
try{
String[] sw_vers = { "ssh", "-p", "2222", "root@localhost", "sw_vers" };
p = Runtime.getRuntime().exec( sw_vers );
BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
System.out.println("Gathering device details");
while ( true ){
line = br.readLine();
if( line == null )
break;
System.out.println("\t" + line);
if( line.indexOf( IOS_PRODUCTNAME ) > -1 ){
line = line.substring( IOS_PRODUCTNAME.length() ).trim();
if( line.toLowerCase().indexOf( IOS ) > -1 ){
ret = true;
}
}
}
}catch( IOException e ){
e.printStackTrace();
}
return ret;
}
/**
* getIOSVersion
* Asks a device for its OS version.
* sw_vers returns:
* ProductName: iPhone OS
* ProductVersion: 4.1
* BuildVersion: 8B118
**/
public static String getIOSVersion(){
String line = null;
String ret = null;
Process p = null;
try{
String[] sw_vers = { "ssh", "-p", "2222", "root@localhost", "sw_vers" };
p = Runtime.getRuntime().exec( sw_vers );
BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
while ( true ){
line = br.readLine();
if( line == null )
break;
if( line.indexOf( IOS_PRODUCTVERSION ) > -1 ){
ret = line.substring( IOS_PRODUCTVERSION.length() ).trim();
System.out.println("getIOSVersion found " + ret);
}
}
}catch( IOException e ){
e.printStackTrace();
}
return ret;
}
/**
* getIOSProcessor
* Asks a device for its architecture
* hostinfo returns:
* Mach kernel version:
* Darwin Kernel Version 10.3.1: Wed Aug 4 22:35:51 PDT 2010; root:xnu-1504.55.33~10/RELEASE_ARM_S5L8930X
* Kernel configured for a single processor only.
* 1 processor is physically available.
* 1 processor is logically available.
* Processor type: armv7 (arm v7)
* Processor active: 0
* Primary memory available: 247.00 megabytes
* Default processor set: 27 tasks, 195 threads, 1 processors
* Load average: 0.05, Mach factor: 0.94
**/
public static String getIOSProcessor(){
String line = null;
String ret = null;
Process p = null;
try{
String[] hostinfo = { "ssh", "-p", "2222", "root@localhost", "hostinfo" };
p = Runtime.getRuntime().exec( hostinfo );
BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
while ( true ){
line = br.readLine();
if( line == null )
break;
if( line.indexOf( IOS_PROCESSOR ) > -1 ){
ret = line.substring( IOS_PROCESSOR.length() ).trim(); // Now we have "armv7 (arm v7)"
if( ret.indexOf( " " ) > -1 ){
ret = ret.substring( 0, ret.indexOf( " " ) ).trim();
}
System.out.println("getIOSProcessor found " + ret);
}
}
}catch( IOException e ){
e.printStackTrace();
}
return ret;
}
/**
* getIOSMemoryAvailable
* Asks a device for its available memory
* hostinfo returns:
* Mach kernel version:
* Darwin Kernel Version 10.3.1: Wed Aug 4 22:35:51 PDT 2010; root:xnu-1504.55.33~10/RELEASE_ARM_S5L8930X
* Kernel configured for a single processor only.
* 1 processor is physically available.
* 1 processor is logically available.
* Processor type: armv7 (arm v7)
* Processor active: 0
* Primary memory available: 247.00 megabytes
* Default processor set: 27 tasks, 195 threads, 1 processors
* Load average: 0.05, Mach factor: 0.94
**/
public static String getIOSMemoryAvailable(){
String line = null;
String ret = null;
Process p = null;
try{
String[] hostinfo = { "ssh", "-p", "2222", "root@localhost", "hostinfo" };
p = Runtime.getRuntime().exec( hostinfo );
BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
while ( true ){
line = br.readLine();
if( line == null )
break;
if( line.indexOf( IOS_MEMORYAVAILABLE ) > -1 ){
ret = line.substring( IOS_MEMORYAVAILABLE.length() ).trim();
System.out.println("getIOSMemoryAvailable found " + ret);
}
}
}catch( IOException e ){
e.printStackTrace();
}
return ret;
}
/**
* Restarts the springboard. Hopefully someday we can bypass the swipe!
**/
public static boolean restartSpringboard( String message ){
int inputData = -1;
BufferedReader keyboardInput = null;
Process p = null;
boolean ret = false;
try{
String[] cmd = { "ssh", "-p", "2222", "root@localhost", "launchctl", "stop", "com.apple.SpringBoard" };
p = Runtime.getRuntime().exec( cmd );
p.waitFor();
System.out.println(message);
keyboardInput = new BufferedReader( new InputStreamReader( System.in ) );
inputData = keyboardInput.read();
System.out.println("got it, thanks");
ret = true;
}catch(Exception e){
e.printStackTrace();
ret = false;
}
return ret;
}
/**
* rebootIOSDevice
* Reboots, then waits for the device to become responsive to ssh commands.
**/
public static void rebootIOSDevice(){
boolean ready = false;
Process p = null;
try{
String[] reboot = { "ssh", "-p", "2222", "root@localhost", "reboot" };
p = Runtime.getRuntime().exec( reboot );
while( !ready ){
System.out.println("Waiting for device to reboot...");
Thread.sleep( 1000 );
ready = pingIOSDevice();
}
}catch( Exception e ){
e.printStackTrace();
}
return;
}
/**
* Calls uicache on the device. This refreshes the Springboard's list of apps
* so we don't have to swipe. There are reports that it can sometimes take a
* while to take effect, so we have some "wait" parameters, just in case.
**/
public static void refreshIOSUICache(){
refreshIOSUICache( 0, 0 );
}
public static void refreshIOSUICache( int attempts, int waitMillis ){
Process p = null;
int ret = -1;
int i = 0;
try{
for( i = 0; i < attempts; ++i ){
System.out.println( "Calling uicache" );
String[] cmdRefresh = { "ssh", "-p", "2222", "root@localhost", "/usr/bin/uicache" };
p = Runtime.getRuntime().exec( cmdRefresh );
p.waitFor();
//System.out.println( "Giving iOS " + waitMillis + " ms to catch up" );
Thread.sleep( waitMillis );
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
* Copies a file or directory to the device. Returns true if to is present afterward.
**/
public static void copyToIOS( String from, String to ){
Process p = null;
File fromFile = new File( from );
String[] dirs = null;
String curDir = null;
String finalDir = null;
try{
if( !fromFile.exists() ){
System.out.println("source " + from + " not found.");
}
if( to.trim().compareTo("") == 0 ){
System.out.println("dest " + to + " is not valid.");
}
// ISSUE: sometimes we pass in a dir and want the dir, sometimes we want the CONTENTS of the dir.
// Need to call this consistently and handle consistently.
// Maybe if it ends in *, copy contents, otherwise copy the dir itself.
// This should be more elegant, but I'm checking it in as is b/c people are blocked.
// If it's a file, be sure the parent directory exists.
if( new File( from ).isFile() ){
curDir = to.substring( 0, to.lastIndexOf( "/" ) );
String[] cmdMkdir = { "ssh", "-p", "2222", "root@localhost", "mkdir", "-p", curDir };
p = Runtime.getRuntime().exec( cmdMkdir );
p.waitFor();
}
//System.out.println("copying to iOS: " + from);
String[] cmdCopy = { "scp", "-P", "2222", "-r", from, "root@localhost:" + to };
p = Runtime.getRuntime().exec( cmdCopy );
p.waitFor();
}catch(Exception e){
e.printStackTrace();
}
}
/**
* Copies a file or directory from the device. Returns true if to is present afterward.
* Note that true will be returned if the copy failed and the file existed already.
* It's up to the caller to delete to first if desired.
**/
public static boolean copyFromIOS( String from, String to ){
Process p = null;
boolean ret = false;
try{
if( findIOSFile( from ) == null ){
System.out.println("MobileUtil.copyFromIOS: Source file " + from + " not found.");
return ret;
}
if( to.trim().compareTo("") == 0 ){
System.out.println("MobileUtil.copyFromIOS: Dest " + to + " is not valid.");
return ret;
}
//System.out.println("Copying from " + from + " to " + to);
String[] cmdCopy = { "scp", "-P", "2222", "-r", "root@localhost:" + from, to };
p = Runtime.getRuntime().exec( cmdCopy );
p.waitFor();
if( new File( to ).exists() ){
ret = true;
}
}catch(Exception e){
e.printStackTrace();
}
return ret;
}
/**
* Launches the given app. If wait is true, waits for it to start and returns the process ID.
*
**/
public static int launchIOSApp( String appName, boolean wait, long launchTimeout ){
Process p = null;
int ret = -1;
try{
String[] cmdOpenUrl = { "ssh", "-p", "2222", "root@localhost", "openURL", appName + ".app:/" };
p = Runtime.getRuntime().exec( cmdOpenUrl );
p.waitFor();
if( wait ){
ret = MobileUtil.getIOSProcessId( appName, true, launchTimeout );
}
}catch(Exception e){
e.printStackTrace();
}
return ret;
}
/**
* Kills a process which contains the given string in its name.
**/
public static void killIOSApp( String appString ){
Process p = null;
int processId = -1;
try{
processId = MobileUtil.getIOSProcessId( appString, false );
killIOSApp( new Integer( processId ).intValue() );
}catch(Exception e){
e.printStackTrace();
}
}
/**
* Kills a proces with a given process ID number.
**/
public static void killIOSApp( int processId ){
Process p = null;
try{
String[] cmdStop = { "ssh", "-p", "2222", "root@localhost", "kill", Integer.toString( processId ) };
p = Runtime.getRuntime().exec( cmdStop );
p.waitFor();
}catch(Exception e){
e.printStackTrace();
}
}
/**
* Removes an app.
**/
public static void removeIOSApp( String appName ){
Process p = null;
try{
//System.out.println("Removing /User/Applications/" + appName + " if present");
String[] cmdRemoveApp = { "ssh", "-p", "2222", "root@localhost", "rm", "-r", "/User/Applications/" + appName };
p = Runtime.getRuntime().exec( cmdRemoveApp );
p.waitFor();
}catch(Exception e){
e.printStackTrace();
}
}
/**
* Removes all apps in /var/mobile/Applications.
**/
public static void removeAllIOSApps(){
Process p = null;
try{
String[] cmdRemoveApps = { "ssh", "-p", "2222", "root@localhost", "rm", "-r", "/User/Applications/*" };
p = Runtime.getRuntime().exec( cmdRemoveApps );
p.waitFor();
}catch(Exception e){
e.printStackTrace();
}
}
/**
* Get the process id of the app on the device.
* If wait = true, it will check every 500 ms until the process appears. Be aware.
* If wait = false, it will just check once.
**/
public static int getIOSProcessId( String appName, boolean wait ){
return MobileUtil.getIOSProcessId( appName, wait, -1 );
}
public static int getIOSProcessId( String appName, boolean wait, long timeout ){
Process p = null;
boolean launchProcess = true;
boolean foundIt = false;
int ret = -1;
String line = "";
int i = 0;
long startTime = Calendar.getInstance().getTimeInMillis();
long curTime = 0;
try{
while( launchProcess ){
curTime = Calendar.getInstance().getTimeInMillis();
String[] cmdPS = { "ssh", "-p", "2222", "root@localhost", "ps", "-A" };
p = Runtime.getRuntime().exec( cmdPS );
line = MobileUtil.monitorProcessOutput( p, appName );
foundIt = (line != null);
if( !wait || foundIt ){
launchProcess = false;
}else if ( wait && (timeout > -1) && (curTime - startTime >= timeout) ){
launchProcess = false;
}else{
Thread.sleep(500);
}
// If we got a line, it will look like " 448 ?? 0:33.90 /var/mobile/Applications/iconButtonTester/iconButtonTester.app/iconButtonTester"
// The first token which is a number is the process ID.
if( foundIt ){
if( line.trim().compareTo( "" ) != 0 ){
String[] chunks = line.split( " " );
for(i = 0; i < chunks.length; ++i){
try{
ret = new Integer( chunks[i] ).intValue();
break;
}catch(Exception e){
}
}
}
}
}
}catch(Exception e){
e.printStackTrace();
}
return ret;
}
/**
* Call ls via ssh.
* Returns a string with information about the file, or null if not found.
**/
public static String findIOSFile( String filename ){
Process p = null;
String ret = null;
try{
String[] cmdLS = { "ssh", "-p", "2222", "root@localhost", "ls", "-l", "--time-style=full-iso", filename };
p = Runtime.getRuntime().exec( cmdLS );
ret = MobileUtil.monitorProcessOutput( p, filename );
}catch(Exception e){
e.printStackTrace();
}
return ret;
}
/**
* Looks for the given string in the given file.
* Returns true if found, false if not.
**/
public static boolean searchIOSFile( String findMe, String filename ){
Process p = null;
boolean ret = false;
try{
String[] cmdCat = { "ssh", "-p", "2222", "root@localhost", "cat", filename };
p = Runtime.getRuntime().exec( cmdCat );
ret = ( MobileUtil.monitorProcessOutput( p, findMe ) != null );
}catch(Exception e){
e.printStackTrace();
ret = false;
}
return ret;
}
/**
* Given a process and a string, monitors the output of the
* process for the string. Returns the line in which
* the string is found, or null if not found.
* It will continue until the process exits.
**/
public static String monitorProcessOutput( Process p, String theString ){
boolean keepReadingLines = true;
boolean endOfLine = false;
boolean foundIt = false;
boolean appRunning = true;
String ret = null;
int readInt = -1;
InputStream is = null;
BufferedReader br = null;
String line = "";
try{
//System.out.println("monitorProcessOutput: theString=" + theString);
is = p.getInputStream();
br = new BufferedReader( new InputStreamReader( is ) );
while( keepReadingLines ){
//System.out.println("monitorProcessOutput: reading lines");
endOfLine = false;
line = "";
while( !endOfLine ){
//System.out.println("monitorProcessOutput: reading a line");
if( br.ready() && (is.available() > 0) ){
readInt = is.read();
// We get 13 & 10 as end of line.
if( readInt == 13 || readInt == 10 ){
endOfLine = true;
}else{
line += (char)readInt;
}
}else{
Thread.sleep(100);
if( !processRunning(p) && (!br.ready()) && (is.available() <= 0) ){
// There's nothing else to do. Bail.
endOfLine = true;
keepReadingLines = false;
}else{
//System.out.println("monitorProcessOutput: not bailing yet");
}
}
}
//System.out.println("monitorProcessOutput: line=" + line);
// At this point, we might have a line.
//System.out.println("\t\t" + line);
if( line.indexOf( theString ) > -1 ){
//System.out.println("monitorProcessOutput: found our line");
ret = line;
foundIt = true;
keepReadingLines = false;
}
}
//System.out.println("monitorProcessOutput: done reading lines");
}catch(Exception e){
e.printStackTrace();
}
//System.out.println("monitorProcessOutput: returning " + ret);
return ret;
}
/**
* Returns whether a process is running.
* exitValue() throws an exception if the process
* is still running.
**/
public static boolean processRunning(Process p){
try{
int exitVal = p.exitValue();
return false;
}catch(IllegalThreadStateException e){
return true;
}
}
/**
* Removes a file or directory from the device.
**/
public static void removeIOSFile( String target ){
Process p = null;
File file = null;
if( findIOSFile( target ) != null ){
try{
System.out.println("Removing " + target);
String[] cmdRemove = { "ssh", "-p", "2222", "root@localhost", "rm", "-r", target };
p = Runtime.getRuntime().exec( cmdRemove );
p.waitFor();
if( findIOSFile( target ) != null ){
System.out.println("Removal of " + target + " failed. Maybe it wasn't present.");
}
}catch(Exception e){
e.printStackTrace();
}
}
}
/**
* getSizeFromLsLine() receives a line like:
* -rw-r--r-- 1 mobile mobile 16326 2011-01-06 14:08:27.000000000 -0800 /User/Applications/iconButtonTester/Documents/MustellaResults.txt
* and returns the size. So far, I don't see how to make the ls command return just the size.
* To get seconds, ls is used like this: ls -l --time-style=full-iso
**/
public static long getSizeFromLsLine( String line ){
String[] tokens = line.split( " " );
int i = 0;
long ret = -1L;
try{
for( i = 0; i < tokens.length; ++i ){
if( i == 4 ){
ret = new Long( tokens[ i ] ).longValue();
}
}
}catch( Exception e ){
e.printStackTrace();
}
return ret;
}
/**
* getTimeFromLsLine() receives a line like:
* -rw-r--r-- 1 mobile mobile 16326 2011-01-06 14:08:27.000000000 -0800 /User/Applications/iconButtonTester/Documents/MustellaResults.txt
* and returns the time (milliseconds). So far, I don't see how to make the ls command return just the time.
* To get seconds, ls is used like this: ls -l --time-style=full-iso
**/
public static long getTimeFromLsLine( String line ){
long ret = -1L;
GregorianCalendar cal = null;
String dateBlock = null;
String timeBlock = null;
String[] tokens = line.split( " " );
int year = -1;
int month = -1;
int date = -1;
int hours = -1;
int minutes = -1;
int seconds = -1;
int i = 0;
int j = 0;
for( i = 0; i < tokens.length; ++i ){
if( tokens[ i ].indexOf( "-" ) > -1 ){
// It's one of the fields with dashes. See if it looks like a date.
String[] dateTokens = tokens[ i ].split( "-" );
if( dateTokens.length == 3 ){
for( j = 0; j < dateTokens.length; ++j ){
try{
if( j == 0 ){
year = new Integer( dateTokens[ j ] ).intValue();
}else if( j == 1 ){
month = new Integer( dateTokens[ j ] ).intValue();
}else{
date = new Integer( dateTokens[ j ] ).intValue();
}
}catch( Exception e ){
//System.out.println(dateTokens[j] + " is not a date");
}
}
}
}
// It didn't contain dashes, so let's look for a time.
if( tokens[ i ].indexOf( ":" ) > -1 ){
String[] timeTokens = tokens[ i ].split( ":" );
if( timeTokens.length == 3 ){
for( j = 0; j < timeTokens.length; ++j ){
try{
if( j == 0 ){
hours = new Integer( timeTokens[ j ] ).intValue();
}else if( j == 1 ){
minutes = new Integer( timeTokens[ j ] ).intValue();
}else if( j == 2 ){
String secondsToken = timeTokens[j].substring( 0, timeTokens[j].indexOf(".") );
seconds = new Integer( secondsToken ).intValue();
}
}catch( Exception e ){
//System.out.println(timeTokens[j] + " is not a time");
}
}
}
}
}
if( year > -1 && month > -1 && date > -1 && hours > -1 && minutes > -1 && seconds > -1 ){
//System.out.println("getTimeFromLsLine(): We think we have a date and time for the log. year=" + year + ", month=" + month + ", date=" + date + ", hours=" + hours + ", minutes=" + minutes + ", seconds=" + seconds);
cal = new GregorianCalendar( year, month, date, hours, minutes, seconds );
ret = cal.getTimeInMillis();
//System.out.println("getTimeFromLsLine(): getTimeInMillis() returned " + ret);
}
return ret;
}
/**
* Remove temporary packaging files from the dir given (at the swfs level).
* e.g.:
* - AOTBuildOutput1086061381018907696.tmp (dir)
* - MobileButtonMain2_ipa (dir)
* - air48611378876425949.tmp (file)
* - non-aot496151823287214585.tmp (file)
* - apk647202451803245042.tmp (file)
* - All .ipa files
* - All .apk files
* Don't call this until all packaging is done, since many threads can package at once.
**/
public static void removeTempPackagingFiles( String cleanDir ){
File subFile = null;
File dir = new File( cleanDir );
try{
if( dir.exists() && dir.isDirectory() ){
File[] arrFiles = dir.listFiles();
for(int i = 0; i < arrFiles.length; ++i){
subFile = arrFiles[i];
if( subFile.isDirectory() ){
if ( subFile.getName().indexOf( "AOTBuildOutput" ) == 0 ||
( subFile.getName().endsWith( "_ipa" ) ) ){
System.out.println("Deleting packaging temp file directory " + subFile.getCanonicalPath());
FileUtils.recursivelyDelete( subFile.getCanonicalPath() );
}
}else{
// airXXXXXXX.tmp
if( (subFile.getName().indexOf( "air" ) == 0) &&
(subFile.getName().endsWith( ".tmp" ) ) ){
System.out.println("Deleting packaging temp file " + subFile.getCanonicalPath());
subFile.delete();
}
// non-aotXXXXX.tmp
if( (subFile.getName().indexOf( "non-aot" ) == 0) &&
(subFile.getName().endsWith( ".tmp" ) ) ){
System.out.println("Deleting packaging temp file " + subFile.getCanonicalPath());
subFile.delete();
}
// apkXXXXX.tmp
if( (subFile.getName().indexOf( "apk" ) == 0) &&
(subFile.getName().endsWith( ".tmp" ) ) ){
System.out.println("Deleting packaging temp file " + subFile.getCanonicalPath());
subFile.delete();
}
// all .ipa, .apk, .bar
if( ( subFile.getName().endsWith( ".ipa" ) ) ||
( subFile.getName().endsWith( ".bar" ) ) ||
( subFile.getName().endsWith( ".apk" ) ) ){
System.out.println("Deleting packaging temp file " + subFile.getCanonicalPath());
subFile.delete();
}
// BARXXXXXXXX.tmp
if ( ( subFile.getName().indexOf( "BAR" ) == 0 ) &&
( subFile.getName().endsWith( "tmp" ) ) ) {
System.out.println("Deleting packaging temp file " + subFile.getCanonicalPath());
subFile.delete();
}
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
} // End class