Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2018 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 package android.content.cts;
     17 
     18 import android.app.ActivityManager;
     19 import android.content.Context;
     20 import android.content.ContextWrapper;
     21 import android.test.AndroidTestCase;
     22 import android.test.suitebuilder.annotation.LargeTest;
     23 import android.util.Log;
     24 
     25 import java.lang.reflect.Field;
     26 import java.util.Arrays;
     27 import java.util.concurrent.atomic.AtomicBoolean;
     28 import java.util.concurrent.atomic.AtomicInteger;
     29 
     30 public class ContextMoreTest extends AndroidTestCase {
     31     private static final String TAG = "ContextMoreTest";
     32 
     33     /**
     34      * Test for {@link Context#getSystemService)}.
     35      *
     36      * Call it repeatedly from multiple threads, and:
     37      * - Make sure getSystemService(ActivityManager) will always return non-null.
     38      * - If ContextImpl.mServiceCache is accessible via reflection, clear it once in a while and
     39      * make sure getSystemService() still returns non-null.
     40      */
     41     @LargeTest
     42     public void testGetSystemService_multiThreaded() throws Exception {
     43         // # of times the tester Runnable has been executed.
     44         final AtomicInteger totalCount = new AtomicInteger(0);
     45 
     46         // # of times the tester Runnable has failed.
     47         final AtomicInteger failCount = new AtomicInteger(0);
     48 
     49         // Run the threads until this becomes true.
     50         final AtomicBoolean stop = new AtomicBoolean(false);
     51 
     52         final Context context = getContext();
     53         final Object[] serviceCache = findServiceCache(context);
     54         if (serviceCache == null) {
     55             Log.w(TAG, "mServiceCache not found.");
     56         }
     57 
     58         final Runnable tester = () -> {
     59             for (;;) {
     60                 final int pass = totalCount.incrementAndGet();
     61 
     62                 final Object service = context.getSystemService(ActivityManager.class);
     63                 if (service == null) {
     64                     failCount.incrementAndGet(); // Fail!
     65                 }
     66 
     67                 if (stop.get()) {
     68                     return;
     69                 }
     70 
     71                 // Yield the CPU.
     72                 try {
     73                     Thread.sleep(0);
     74                 } catch (InterruptedException e) {
     75                 }
     76 
     77                 // Once in a while, force clear mServiceCache.
     78                 if ((serviceCache != null) && ((pass % 7) == 0)) {
     79                     Arrays.fill(serviceCache, null);
     80                 }
     81             }
     82         };
     83 
     84         final int NUM_THREADS = 20;
     85 
     86         // Create and start the threads...
     87         final Thread[] threads = new Thread[NUM_THREADS];
     88         for (int i = 0; i < NUM_THREADS; i++) {
     89             threads[i] = new Thread(tester);
     90         }
     91         for (int i = 0; i < NUM_THREADS; i++) {
     92             threads[i].start();
     93         }
     94 
     95         Thread.sleep(10 * 1000);
     96 
     97         stop.set(true);
     98 
     99         // Wait for them to stop...
    100         for (int i = 0; i < NUM_THREADS; i++) {
    101             threads[i].join();
    102         }
    103 
    104         assertEquals(0, failCount.get());
    105         assertTrue("totalCount must be bigger than " + NUM_THREADS
    106                 + " but was " + totalCount.get(), totalCount.get() > NUM_THREADS);
    107     }
    108 
    109     /**
    110      * Find a field by name using reflection.
    111      */
    112     private static Object readField(Object instance, String fieldName) {
    113         final Field f;
    114         try {
    115             f = instance.getClass().getDeclaredField(fieldName);
    116             f.setAccessible(true);
    117             final Object ret = f.get(instance);
    118             if (ret == null) {
    119                 return null;
    120             }
    121             return ret;
    122         } catch (NoSuchFieldException | IllegalAccessException e) {
    123             return null;
    124         }
    125     }
    126 
    127     /**
    128      * Try to find the mServiceCache field from a Context. Returns null if none found.
    129      */
    130     private static Object[] findServiceCache(Context context) {
    131         // Find the base context.
    132         while (context instanceof ContextWrapper) {
    133             context = ((ContextWrapper) context).getBaseContext();
    134         }
    135         // Try to find the mServiceCache field.
    136         final Object serviceCache = readField(context, "mServiceCache");
    137         if (serviceCache instanceof Object[]) {
    138             return (Object[]) serviceCache;
    139         }
    140         return null;
    141     }
    142 }
    143