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
run()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
onTransact(int code, Parcel data, Parcel reply, int flags)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 
compileForBinder()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 
queryStatusForBinder()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 
extractIntentInfo(Intent intent)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 
installToolchain()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 
copyAssets(String asset_dir, String out_dir, boolean executable)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 
copyFile(InputStream in, OutputStream out)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 
AbccService()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
onHandleIntent(Intent intent)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
onBind(Intent intent)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 
dumpDebugInfo()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
onUnbind(Intent intent)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.
genLibs(String working_dir, String sysroot)264   private native int genLibs(String working_dir, String sysroot);
265 
266   static {
267       System.loadLibrary("jni_abcc");
268   }
269 }
270