Home | History | Annotate | Download | only in abcc
      1 package compiler.abcc;
      2 
      3 import android.app.IntentService;
      4 import android.app.Service;
      5 import android.content.Intent;
      6 import android.content.res.AssetManager;
      7 import android.content.pm.ApplicationInfo;
      8 import android.content.pm.PackageManager;
      9 import android.os.Binder;
     10 import android.os.Bundle;
     11 import android.os.IBinder;
     12 import android.os.Parcel;
     13 import android.os.ResultReceiver;
     14 import android.os.RemoteException;
     15 import android.util.Log;
     16 import java.io.File;
     17 import java.io.FilenameFilter;
     18 import java.io.FileOutputStream;
     19 import java.io.IOException;
     20 import java.io.InputStream;
     21 import java.io.OutputStream;
     22 
     23 public class AbccService extends IntentService {
     24   private static final String TAG = "AbccService";
     25 
     26   String mWorkingDir = null;
     27   String mSysroot = null;
     28 
     29   // For onBind()
     30   private IBinder mBinder = new LocalBinder();
     31   private int mCurrentStatus = AbccService.STATUS_OKAY;
     32   private Object mStatusLock = new Object();
     33 
     34   private static int STATUS_OKAY = 0;
     35   private static int STATUS_WORKING = 1;
     36   private static int STATUS_ERROR = 2;
     37 
     38   class WorkingThread extends Thread {
     39     @Override
     40     public void run() {
     41       Log.i(TAG, "WorkingThread run");
     42 
     43 
     44       synchronized (mStatusLock) {
     45         mCurrentStatus = AbccService.STATUS_WORKING;
     46         if (mWorkingDir == null) {
     47           mCurrentStatus = AbccService.STATUS_ERROR;
     48           mStatusLock.notifyAll();
     49           return;
     50         }
     51       }
     52 
     53       Log.i(TAG, "mWorkingDir for genLibs: " + mWorkingDir);
     54       if (genLibs(mWorkingDir, mSysroot) != 0) {
     55         synchronized (mStatusLock) {
     56           mCurrentStatus = AbccService.STATUS_ERROR;
     57           mStatusLock.notifyAll();
     58           return;
     59         }
     60       }
     61 
     62       Log.i(TAG, "WorkingThread run okay");
     63       synchronized (mStatusLock) {
     64         mCurrentStatus = AbccService.STATUS_OKAY;
     65         mStatusLock.notifyAll();
     66         Log.i(TAG, "WorkingThread run done");
     67         return;
     68       }
     69     }
     70   }
     71 
     72   class LocalBinder extends Binder {
     73     @Override
     74     protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
     75       if (!pingBinder()) {
     76         Log.e(TAG, "Process doesn't exist. Error!");
     77         throw new RemoteException("AbccLocalBinder pingBinder return false!");
     78       }
     79 
     80       // The code is not important now. This is just a communication way
     81       // and what we really need is the information from @data
     82       Bundle real_data = data.readBundle();
     83       if (real_data == null) {
     84         Log.e(TAG, "Should get some information from the Parcel.");
     85         return false;
     86       }
     87       String path = real_data.getString("working_dir");
     88       if (path == null ) {
     89         Log.e(TAG, "onTransact but no working_dir provided?");
     90         return false;
     91       }
     92 
     93       mWorkingDir = path;
     94       compileForBinder();
     95       return queryStatusForBinder();
     96     }
     97   }
     98 
     99   private boolean compileForBinder() {
    100     Log.i(TAG, "compileForBinder");
    101     new WorkingThread().start();
    102     synchronized (mStatusLock) {
    103       mCurrentStatus = AbccService.STATUS_WORKING;
    104     }
    105     Log.i(TAG, "compileForBinder done");
    106     return true;
    107   }
    108 
    109   private boolean queryStatusForBinder() {
    110     Log.i(TAG, "queryStatusForBinder");
    111     Log.i(TAG, "mCurrentStatus = " + mCurrentStatus);
    112 
    113     while (mCurrentStatus == STATUS_WORKING) {
    114       try {
    115         Log.i(TAG, "queryStatusForBinder wait");
    116         synchronized (mStatusLock) {
    117           mStatusLock.wait();
    118         }
    119       } catch (InterruptedException e) {
    120       }
    121     }
    122 
    123     Log.i(TAG, "queryStatusForBinder checking result");
    124 
    125     if (mCurrentStatus == STATUS_OKAY)
    126       return true;
    127     else
    128       return false;
    129   }
    130 
    131   private void extractIntentInfo(Intent intent) {
    132     if (intent == null) {
    133       Log.e(TAG, "Intent should not be null when extractIntentInfo.");
    134       return;
    135     }
    136 
    137     String path = intent.getStringExtra("working_dir");
    138     Log.i(TAG, "Got working_dir from intent: " + path);
    139     if (path != null)
    140       mWorkingDir = path;
    141     else
    142       Log.e(TAG, "Intent extra 'working_dir' cannot be null.");
    143 
    144     path = intent.getStringExtra("toolchain_sysroot");
    145     if (path != null)
    146       mSysroot = path;
    147   }
    148 
    149   private void installToolchain() {
    150     if (mSysroot != null) {
    151       // User specify a customized toolchain sysroot, so we don't need to copy.
    152       return;
    153     }
    154 
    155     // The toolchain is enclosed inside assets/
    156     File sysroot = getDir("toolchain_sysroot", MODE_WORLD_READABLE);
    157     mSysroot = sysroot.getAbsolutePath();
    158     File cur_file = new File(mSysroot + "/usr");
    159     cur_file.mkdirs();
    160     cur_file.setReadable(true, /*OwnerOnly=*/false);
    161     cur_file.setExecutable(true, /*OwnerOnly=*/false);
    162     cur_file = new File(mSysroot + "/usr/bin");
    163     cur_file.mkdirs();
    164     cur_file.setReadable(true, /*OwnerOnly=*/false);
    165     cur_file.setExecutable(true, /*OwnerOnly=*/false);
    166     cur_file = new File(mSysroot + "/usr/lib");
    167     cur_file.mkdirs();
    168     cur_file.setReadable(true, /*OwnerOnly=*/false);
    169     cur_file.setExecutable(true, /*OwnerOnly=*/false);
    170     copyAssets("usr/bin", mSysroot + "/usr/bin", /*executable=*/true);
    171     copyAssets("usr/lib", mSysroot + "/usr/lib", /*executable=*/false);
    172   }
    173 
    174   private void copyAssets(String asset_dir, String out_dir, boolean executable) {
    175     AssetManager assetManager = getAssets();
    176     String[] files = null;
    177     try {
    178       files = assetManager.list(asset_dir);
    179     } catch (IOException e) {
    180       Log.e(TAG, "Failed to get asset file list.", e);
    181     }
    182     for(String filename : files) {
    183       InputStream in = null;
    184       OutputStream out = null;
    185       try {
    186         in = assetManager.open(asset_dir + "/" + filename);
    187         out = new FileOutputStream(out_dir + "/" + filename);
    188         copyFile(in, out);
    189         in.close();
    190         in = null;
    191         out.flush();
    192         out.close();
    193         out = null;
    194       } catch(IOException e) {
    195         Log.e(TAG, "Failed to copy asset file: " + filename, e);
    196       }
    197 
    198       File cur_file = new File(out_dir + "/" + filename);
    199       cur_file.setReadable(true, /*OwnerOnly=*/false);
    200       if (executable) {
    201         cur_file.setExecutable(true, /*OwnerOnly=*/false);
    202       }
    203     }
    204   }
    205 
    206   private void copyFile(InputStream in, OutputStream out) throws IOException {
    207     byte[] buffer = new byte[1024];
    208     int read;
    209     while((read = in.read(buffer)) != -1){
    210       out.write(buffer, 0, read);
    211     }
    212   }
    213 
    214   public AbccService() {
    215     super("AbccService");
    216   }
    217 
    218   // Usage: adb shell am startservice -a compiler.abcc.BITCODE_COMPILE_TEST -n compiler.abcc/compiler.abcc.AbccService -e working_dir /data/local/tmp/test
    219   @Override
    220   public void onHandleIntent(Intent intent) {
    221     Log.i(TAG, "got onHandleIntent intent: " + intent);
    222     if (intent.getAction() != "compiler.abcc.BITCODE_COMPILE_TEST") {
    223       Log.e(TAG, "We don't support formal release by onHandleIntent() yet!");
    224       return;
    225     }
    226 
    227     extractIntentInfo(intent);
    228     installToolchain();
    229     new WorkingThread().start();
    230   }
    231 
    232   @Override
    233   public IBinder onBind(Intent intent) {
    234     Log.i(TAG, "got onBind intent: " + intent);
    235     if (intent.getAction() != "compiler.abcc.BITCODE_COMPILE") {
    236       Log.e(TAG, "We don't support other intent except for BITCODE_COMPILE by onBind() yet!");
    237       return null;
    238     }
    239 
    240     installToolchain();
    241 
    242     if (mBinder == null) {
    243       Log.e(TAG, "Why mBinder is null?");
    244     }
    245     return mBinder;
    246   }
    247 
    248   private void dumpDebugInfo() {
    249     Log.i(TAG, "AbccService field dump:");
    250     Log.i(TAG, "- mWorkingDir: " + mWorkingDir);
    251     Log.i(TAG, "- mSysroot: " + mSysroot);
    252     Log.i(TAG, "- mBinder: " + mBinder);
    253     Log.i(TAG, "- mCurrentStatus: " + mCurrentStatus);
    254   }
    255 
    256   @Override
    257   public boolean onUnbind(Intent intent) {
    258     Log.i(TAG, "got onUnbind intent: " + intent);
    259     return false;
    260   }
    261 
    262   // If succeess, it will be 0 in file working_dir/compile_result.
    263   // Otherwise, there will be error message in file working_dir/compile_error.
    264   private native int genLibs(String working_dir, String sysroot);
    265 
    266   static {
    267     // Distinguish whether this is a system prebuilt apk or an updated apk
    268     // ApplicationInfo info = getApplicationInfo();
    269     // FIXME: We cannot access ApplicationInfo in static context, how to prevent magic path?
    270     if (new File("/data/data/compiler.abcc/lib/libjni_abcc.so").exists())
    271       System.loadLibrary("jni_abcc");
    272     else
    273       System.load("/system/lib/libjni_abcc.so");
    274   }
    275 }
    276