Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.webkit.cts;
     18 
     19 
     20 import android.content.ComponentName;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.ServiceConnection;
     24 import android.os.Handler;
     25 import android.os.IBinder;
     26 import android.os.Looper;
     27 import android.os.Message;
     28 import android.os.Messenger;
     29 import android.os.RemoteException;
     30 
     31 import com.android.internal.annotations.GuardedBy;
     32 
     33 import junit.framework.Assert;
     34 import junit.framework.AssertionFailedError;
     35 
     36 class TestProcessClient extends Assert implements AutoCloseable, ServiceConnection {
     37     private Context mContext;
     38 
     39     private static final long CONNECT_TIMEOUT_MS = 5000;
     40 
     41     private Object mLock = new Object();
     42     @GuardedBy("mLock")
     43     private Messenger mService;
     44     @GuardedBy("mLock")
     45     private Integer mLastResult;
     46     @GuardedBy("mLock")
     47     private Throwable mLastException;
     48 
     49     private final Messenger mReplyHandler = new Messenger(new ReplyHandler(Looper.getMainLooper()));
     50 
     51     public static TestProcessClient createProcessA(Context context) throws Throwable {
     52         return new TestProcessClient(context, TestProcessServiceA.class);
     53     }
     54 
     55     public static TestProcessClient createProcessB(Context context) throws Throwable {
     56         return new TestProcessClient(context, TestProcessServiceB.class);
     57     }
     58 
     59     /**
     60      * Subclass this to implement test code to run on the service side.
     61      */
     62     static abstract class TestRunnable extends Assert {
     63         public abstract void run(Context ctx);
     64     }
     65 
     66     static class ProcessFreshChecker extends TestRunnable {
     67         private static Object sFreshLock = new Object();
     68         @GuardedBy("sFreshLock")
     69         private static boolean sFreshProcess = true;
     70 
     71         @Override
     72         public void run(Context ctx) {
     73             synchronized (sFreshLock) {
     74                 if (!sFreshProcess) {
     75                     fail("Service process was unexpectedly reused");
     76                 }
     77                 sFreshProcess = true;
     78             }
     79         }
     80 
     81     }
     82 
     83     private TestProcessClient(Context context, Class service) throws Throwable {
     84         mContext = context;
     85         Intent i = new Intent(context, service);
     86         context.bindService(i, this, Context.BIND_AUTO_CREATE);
     87         synchronized (mLock) {
     88             if (mService == null) {
     89                 mLock.wait(CONNECT_TIMEOUT_MS);
     90                 if (mService == null) {
     91                     fail("Timeout waiting for connection");
     92                 }
     93             }
     94         }
     95 
     96         // Check that we're using an actual fresh process.
     97         // 1000ms timeout is plenty since the service is already running.
     98         run(ProcessFreshChecker.class, 1000);
     99     }
    100 
    101     public void run(Class runnableClass, long timeoutMs) throws Throwable {
    102         Message m = Message.obtain(null, TestProcessService.MSG_RUN_TEST);
    103         m.replyTo = mReplyHandler;
    104         m.getData().putString(TestProcessService.TEST_CLASS_KEY, runnableClass.getName());
    105         int result;
    106         Throwable exception;
    107         synchronized (mLock) {
    108             mService.send(m);
    109             if (mLastResult == null) {
    110                 mLock.wait(timeoutMs);
    111                 if (mLastResult == null) {
    112                     fail("Timeout waiting for result");
    113                 }
    114             }
    115             result = mLastResult;
    116             mLastResult = null;
    117             exception = mLastException;
    118             mLastException = null;
    119         }
    120         if (result == TestProcessService.REPLY_EXCEPTION) {
    121             throw exception;
    122         } else if (result != TestProcessService.REPLY_OK) {
    123             fail("Unknown result from service: " + result);
    124         }
    125     }
    126 
    127     public void close() {
    128         synchronized (mLock) {
    129             if (mService != null) {
    130                 try {
    131                     mService.send(Message.obtain(null, TestProcessService.MSG_EXIT_PROCESS));
    132                 } catch (RemoteException e) {}
    133                 mService = null;
    134                 mContext.unbindService(this);
    135             }
    136         }
    137     }
    138 
    139     @Override
    140     public void onServiceConnected(ComponentName className, IBinder service) {
    141         synchronized (mLock) {
    142             mService = new Messenger(service);
    143             mLock.notify();
    144         }
    145     }
    146 
    147     @Override
    148     public void onServiceDisconnected(ComponentName className) {
    149         synchronized (mLock) {
    150             mService = null;
    151             mContext.unbindService(this);
    152             mLastResult = TestProcessService.REPLY_EXCEPTION;
    153             mLastException = new AssertionFailedError("Service disconnected unexpectedly");
    154             mLock.notify();
    155         }
    156     }
    157 
    158     private class ReplyHandler extends Handler {
    159         ReplyHandler(Looper looper) {
    160             super(looper);
    161         }
    162 
    163         @Override
    164         public void handleMessage(Message msg) {
    165             synchronized (mLock) {
    166                 mLastResult = msg.what;
    167                 if (msg.what == TestProcessService.REPLY_EXCEPTION) {
    168                     mLastException = (Throwable) msg.getData().getSerializable(
    169                             TestProcessService.REPLY_EXCEPTION_KEY);
    170                 }
    171                 mLock.notify();
    172             }
    173         }
    174     }
    175 }
    176