All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robolectric.shadows.ShadowInstrumentation Maven / Gradle / Ivy

package org.robolectric.shadows;

import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.N;
import static android.os.Build.VERSION_CODES.N_MR1;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Build.VERSION_CODES.P;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.util.concurrent.Futures.immediateFuture;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static org.robolectric.util.reflector.Reflector.reflector;

import android.app.Activity;
import android.app.ActivityThread;
import android.app.Fragment;
import android.app.IUiAutomationConnection;
import android.app.Instrumentation;
import android.app.Instrumentation.ActivityResult;
import android.app.UiAutomation;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.Intent.FilterComparison;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Pair;
import androidx.annotation.Nullable;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.concurrent.GuardedBy;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowActivity.IntentForResult;
import org.robolectric.shadows.ShadowApplication.Wrapper;
import org.robolectric.util.Logger;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.ReflectionHelpers.ClassParameter;
import org.robolectric.util.reflector.Direct;
import org.robolectric.util.reflector.ForType;
import org.robolectric.util.reflector.WithType;

@Implements(value = Instrumentation.class, looseSignatures = true)
public class ShadowInstrumentation {

  @RealObject private Instrumentation realObject;

  private final List startedActivities = Collections.synchronizedList(new ArrayList<>());
  private final List startedActivitiesForResults =
      Collections.synchronizedList(new ArrayList<>());
  private final Map intentRequestCodeMap =
      Collections.synchronizedMap(new HashMap<>());
  private final List startedServices =
      Collections.synchronizedList(new ArrayList<>());
  private final List stoppedServices =
      Collections.synchronizedList(new ArrayList<>());
  private final List broadcastIntents = Collections.synchronizedList(new ArrayList<>());
  private final Map broadcastOptions = Collections.synchronizedMap(new HashMap<>());
  private final Map> broadcastIntentsForUser =
      Collections.synchronizedMap(new HashMap<>());
  private final List boundServiceConnections =
      Collections.synchronizedList(new ArrayList<>());
  private final List unboundServiceConnections =
      Collections.synchronizedList(new ArrayList<>());

  @GuardedBy("itself")
  private final List registeredReceivers = new ArrayList<>();
  // map of pid+uid to granted permissions
  private final Map, Set> grantedPermissionsMap =
      Collections.synchronizedMap(new HashMap<>());
  private boolean unbindServiceShouldThrowIllegalArgument = false;
  private SecurityException exceptionForBindService = null;
  private boolean bindServiceCallsOnServiceConnectedInline;
  private final Map
      serviceConnectionDataForIntent = Collections.synchronizedMap(new HashMap<>());
  // default values for bindService
  private ServiceConnectionDataWrapper defaultServiceConnectionData =
      new ServiceConnectionDataWrapper(null, null);
  private final List unbindableActions = Collections.synchronizedList(new ArrayList<>());
  private final List unbindableComponents =
      Collections.synchronizedList(new ArrayList<>());
  private final Map stickyIntents =
      Collections.synchronizedMap(new LinkedHashMap<>());
  private Handler mainHandler;
  private final Map
      serviceConnectionDataForServiceConnection = Collections.synchronizedMap(new HashMap<>());

  private boolean checkActivities;
  // This will default to False in the future to correctly mirror real Android behavior.
  private boolean unbindServiceCallsOnServiceDisconnected = true;
  @Nullable private UiAutomation uiAutomation;

  @Implementation(minSdk = P)
  protected Activity startActivitySync(Intent intent, Bundle options) {
    throw new UnsupportedOperationException("Implement me!!");
  }

  @Implementation
  protected void execStartActivities(
      Context who,
      IBinder contextThread,
      IBinder token,
      Activity target,
      Intent[] intents,
      Bundle options) {
    for (Intent intent : intents) {
      execStartActivity(who, contextThread, token, target, intent, -1, options);
    }
  }

  @Implementation
  protected ActivityResult execStartActivity(
      Context who,
      IBinder contextThread,
      IBinder token,
      Activity target,
      Intent intent,
      int requestCode,
      Bundle options) {

    verifyActivityInManifest(intent);
    logStartedActivity(intent, null, requestCode, options);

    if (who == null) {
      return null;
    }
    return reflector(_Instrumentation_.class, realObject)
        .execStartActivity(who, contextThread, token, target, intent, requestCode, options);
  }

  @Implementation(maxSdk = LOLLIPOP_MR1)
  protected ActivityResult execStartActivity(
      Context who,
      IBinder contextThread,
      IBinder token,
      Fragment target,
      Intent intent,
      int requestCode,
      Bundle options) {
    verifyActivityInManifest(intent);
    logStartedActivity(intent, null, requestCode, options);
    return null;
  }

  @Implementation(minSdk = M)
  protected ActivityResult execStartActivity(
      Context who,
      IBinder contextThread,
      IBinder token,
      String target,
      Intent intent,
      int requestCode,
      Bundle options) {
    verifyActivityInManifest(intent);
    logStartedActivity(intent, target, requestCode, options);

    return reflector(_Instrumentation_.class, realObject)
        .execStartActivity(who, contextThread, token, target, intent, requestCode, options);
  }

  /**
   * Behaves as {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle).
   *
   * 

Currently ignores the user. */ @Implementation(minSdk = JELLY_BEAN_MR1, maxSdk = N_MR1) protected ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity resultWho, Intent intent, int requestCode, Bundle options, UserHandle user) { return execStartActivity(who, contextThread, token, resultWho, intent, requestCode, options); } /** * Behaves as {@link #execStartActivity(Context, IBinder, IBinder, String, Intent, int, Bundle). * *

Currently ignores the user. */ @Implementation(minSdk = O) protected ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, String resultWho, Intent intent, int requestCode, Bundle options, UserHandle user) { return execStartActivity(who, contextThread, token, resultWho, intent, requestCode, options); } @Implementation protected void setInTouchMode(boolean inTouchMode) { ShadowWindowManagerGlobal.setInTouchMode(inTouchMode); } @Implementation(minSdk = JELLY_BEAN_MR2, maxSdk = M) protected UiAutomation getUiAutomation() { return getUiAutomation(0); } @Implementation(minSdk = N) protected UiAutomation getUiAutomation(int flags) { if (uiAutomation == null) { // Create a new automation using reflection, the real code just connects through the // automation connection and to the accessibility service, neither of which exist in // Robolectric. uiAutomation = ReflectionHelpers.callConstructor( UiAutomation.class, ClassParameter.from(Looper.class, Looper.getMainLooper()), ClassParameter.from( IUiAutomationConnection.class, ReflectionHelpers.createNullProxy(IUiAutomationConnection.class))); } return uiAutomation; } private void logStartedActivity(Intent intent, String target, int requestCode, Bundle options) { startedActivities.add(intent); intentRequestCodeMap.put( new FilterComparison(intent), new TargetAndRequestCode(target, requestCode)); startedActivitiesForResults.add(new IntentForResult(intent, requestCode, options)); } private void verifyActivityInManifest(Intent intent) { if (checkActivities && RuntimeEnvironment.getApplication() .getPackageManager() .resolveActivity(intent, MATCH_DEFAULT_ONLY) == null) { throw new ActivityNotFoundException(intent.getAction()); } } void sendOrderedBroadcastAsUser( Intent intent, UserHandle userHandle, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras, Context context) { List receivers = getAppropriateWrappers( context, userHandle, intent, receiverPermission, /* broadcastOptions= */ null); sortByPriority(receivers); if (resultReceiver != null) { receivers.add(new Wrapper(resultReceiver, null, context, null, scheduler, 0)); } postOrderedToWrappers(receivers, intent, initialCode, initialData, initialExtras, context); } void assertNoBroadcastListenersOfActionRegistered(ContextWrapper context, String action) { synchronized (registeredReceivers) { for (Wrapper registeredReceiver : registeredReceivers) { if (registeredReceiver.context == context.getBaseContext()) { Iterator actions = registeredReceiver.intentFilter.actionsIterator(); while (actions.hasNext()) { if (actions.next().equals(action)) { RuntimeException e = new IllegalStateException( "Unexpected BroadcastReceiver on " + context + " with action " + action + " " + registeredReceiver.broadcastReceiver + " that was originally registered here:"); e.setStackTrace(registeredReceiver.exception.getStackTrace()); throw e; } } } } } } /** Returns the BroadcaseReceivers wrappers, matching intent's action and permissions. */ private List getAppropriateWrappers( Context context, @Nullable UserHandle userHandle, Intent intent, String receiverPermission, @Nullable Bundle broadcastOptions) { broadcastIntents.add(intent); this.broadcastOptions.put(intent, broadcastOptions); if (userHandle != null) { List intentsForUser = broadcastIntentsForUser.get(userHandle); if (intentsForUser == null) { intentsForUser = new ArrayList<>(); broadcastIntentsForUser.put(userHandle, intentsForUser); } intentsForUser.add(intent); } List result = new ArrayList<>(); List copy = new ArrayList<>(); synchronized (registeredReceivers) { copy.addAll(registeredReceivers); } for (Wrapper wrapper : copy) { if (broadcastReceiverMatchesIntent(context, wrapper, intent, receiverPermission)) { result.add(wrapper); } } System.err.format("Intent = %s; Matching wrappers: %s\n", intent, result); return result; } private static boolean broadcastReceiverMatchesIntent( Context broadcastContext, Wrapper wrapper, Intent intent, String receiverPermission) { String intentClass = intent.getComponent() != null ? intent.getComponent().getClassName() : null; boolean matchesIntentClass = intentClass != null && intentClass.equals(wrapper.broadcastReceiver.getClass().getName()); // The receiver must hold the permission specified by sendBroadcast, and the broadcaster must // hold the permission specified by registerReceiver. boolean hasPermissionFromManifest = hasRequiredPermissionForBroadcast(wrapper.context, receiverPermission) && hasRequiredPermissionForBroadcast(broadcastContext, wrapper.broadcastPermission); // Many existing tests don't declare manifest permissions, relying on the old equality check. boolean hasPermissionForBackwardsCompatibility = TextUtils.equals(receiverPermission, wrapper.broadcastPermission); boolean hasPermission = hasPermissionFromManifest || hasPermissionForBackwardsCompatibility; boolean matchesAction = wrapper.intentFilter.matchAction(intent.getAction()); final int match = wrapper.intentFilter.matchData(intent.getType(), intent.getScheme(), intent.getData()); boolean matchesDataAndType = match != IntentFilter.NO_MATCH_DATA && match != IntentFilter.NO_MATCH_TYPE; return matchesIntentClass || (hasPermission && matchesAction && matchesDataAndType); } /** A null {@code requiredPermission} indicates that no permission is required. */ private static boolean hasRequiredPermissionForBroadcast( Context context, @Nullable String requiredPermission) { if (requiredPermission == null) { return true; } // Check manifest-based permissions from PackageManager. Context applicationContext = RuntimeEnvironment.getApplication(); if (applicationContext .getPackageManager() .checkPermission(requiredPermission, context.getPackageName()) == PERMISSION_GRANTED) { return true; } // Check dynamically-granted permissions from here in ShadowInstrumentation. if (Objects.equals(context.getPackageName(), applicationContext.getPackageName()) && applicationContext.checkPermission(requiredPermission, Process.myPid(), Process.myUid()) == PERMISSION_GRANTED) { return true; } return false; } private void postIntent( Intent intent, Wrapper wrapper, final AtomicBoolean abort, Context context, int resultCode) { final Handler scheduler = (wrapper.scheduler != null) ? wrapper.scheduler : getMainHandler(context); final BroadcastReceiver receiver = wrapper.broadcastReceiver; final ShadowBroadcastReceiver shReceiver = Shadow.extract(receiver); final Intent broadcastIntent = intent; scheduler.post( new Runnable() { @Override public void run() { receiver.setPendingResult( ShadowBroadcastPendingResult.create(resultCode, null, null, false)); shReceiver.onReceive(context, broadcastIntent, abort); } }); } private void postToWrappers( List wrappers, Intent intent, Context context, int resultCode) { AtomicBoolean abort = new AtomicBoolean(false); // abort state is shared among all broadcast receivers for (Wrapper wrapper : wrappers) { postIntent(intent, wrapper, abort, context, resultCode); } } private void postOrderedToWrappers( List wrappers, final Intent intent, int initialCode, String data, Bundle extras, final Context context) { final AtomicBoolean abort = new AtomicBoolean(false); // abort state is shared among all broadcast receivers ListenableFuture future = immediateFuture(new BroadcastResultHolder(initialCode, data, extras)); for (final Wrapper wrapper : wrappers) { future = postIntent(wrapper, intent, future, abort, context); } final ListenableFuture finalFuture = future; future.addListener( new Runnable() { @Override public void run() { getMainHandler(context) .post( new Runnable() { @Override public void run() { try { finalFuture.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } } }); } }, directExecutor()); } /** * Enforces that BroadcastReceivers invoked during an ordered broadcast run serially, passing * along their results. */ private ListenableFuture postIntent( final Wrapper wrapper, final Intent intent, ListenableFuture oldResult, final AtomicBoolean abort, final Context context) { final Handler scheduler = (wrapper.scheduler != null) ? wrapper.scheduler : getMainHandler(context); return Futures.transformAsync( oldResult, new AsyncFunction() { @Override public ListenableFuture apply( BroadcastResultHolder broadcastResultHolder) throws Exception { final BroadcastReceiver.PendingResult result = ShadowBroadcastPendingResult.create( broadcastResultHolder.resultCode, broadcastResultHolder.resultData, broadcastResultHolder.resultExtras, true /*ordered */); wrapper.broadcastReceiver.setPendingResult(result); scheduler.post( () -> { ShadowBroadcastReceiver shadowBroadcastReceiver = Shadow.extract(wrapper.broadcastReceiver); shadowBroadcastReceiver.onReceive(context, intent, abort); }); return BroadcastResultHolder.transform(result); } }, directExecutor()); } /** * Broadcasts the {@code Intent} by iterating through the registered receivers, invoking their * filters including permissions, and calling {@code onReceive(Application, Intent)} as * appropriate. Does not enqueue the {@code Intent} for later inspection. * * @param context * @param intent the {@code Intent} to broadcast todo: enqueue the Intent for later inspection */ void sendBroadcastWithPermission( Intent intent, UserHandle userHandle, String receiverPermission, Context context) { sendBroadcastWithPermission( intent, userHandle, receiverPermission, context, /* broadcastOptions= */ null, /* resultCode= */ 0); } void sendBroadcastWithPermission( Intent intent, String receiverPermission, Context context, int resultCode) { sendBroadcastWithPermission( intent, /*userHandle=*/ null, receiverPermission, context, null, resultCode); } void sendBroadcastWithPermission( Intent intent, String receiverPermission, Context context, @Nullable Bundle broadcastOptions, int resultCode) { sendBroadcastWithPermission( intent, /*userHandle=*/ null, receiverPermission, context, broadcastOptions, resultCode); } void sendBroadcastWithPermission( Intent intent, UserHandle userHandle, String receiverPermission, Context context, @Nullable Bundle broadcastOptions, int resultCode) { List wrappers = getAppropriateWrappers(context, userHandle, intent, receiverPermission, broadcastOptions); postToWrappers(wrappers, intent, context, resultCode); } void sendOrderedBroadcastWithPermission( Intent intent, String receiverPermission, Context context) { List wrappers = getAppropriateWrappers( context, /*userHandle=*/ null, intent, receiverPermission, /* broadcastOptions= */ null); // sort by the decrease of priorities sortByPriority(wrappers); postOrderedToWrappers(wrappers, intent, 0, null, null, context); } private void sortByPriority(List wrappers) { Collections.sort( wrappers, new Comparator() { @Override public int compare(Wrapper o1, Wrapper o2) { return Integer.compare( o2.getIntentFilter().getPriority(), o1.getIntentFilter().getPriority()); } }); } List getBroadcastIntents() { return broadcastIntents; } @Nullable Bundle getBroadcastOptions(Intent intent) { synchronized (broadcastOptions) { for (Intent broadcastIntent : broadcastOptions.keySet()) { if (broadcastIntent.filterEquals(intent)) { return broadcastOptions.get(broadcastIntent); } } return null; } } List getBroadcastIntentsForUser(UserHandle userHandle) { List intentsForUser = broadcastIntentsForUser.get(userHandle); if (intentsForUser == null) { intentsForUser = new ArrayList<>(); broadcastIntentsForUser.put(userHandle, intentsForUser); } return intentsForUser; } void clearBroadcastIntents() { broadcastIntents.clear(); broadcastOptions.clear(); broadcastIntentsForUser.clear(); } Intent getNextStartedActivity() { if (startedActivities.isEmpty()) { return null; } else { return startedActivities.remove(startedActivities.size() - 1); } } Intent peekNextStartedActivity() { if (startedActivities.isEmpty()) { return null; } else { return startedActivities.get(startedActivities.size() - 1); } } /** * Clears all {@code Intent}s started by {@link #execStartActivity(Context, IBinder, IBinder, * Activity, Intent, int, Bundle)}, {@link #execStartActivity(Context, IBinder, IBinder, Fragment, * Intent, int, Bundle)}, and {@link #execStartActivity(Context, IBinder, IBinder, String, Intent, * int, Bundle)}. */ void clearNextStartedActivities() { startedActivities.clear(); startedActivitiesForResults.clear(); } IntentForResult getNextStartedActivityForResult() { if (startedActivitiesForResults.isEmpty()) { return null; } else { return startedActivitiesForResults.remove(startedActivitiesForResults.size() - 1); } } IntentForResult peekNextStartedActivityForResult() { if (startedActivitiesForResults.isEmpty()) { return null; } else { return startedActivitiesForResults.get(startedActivitiesForResults.size() - 1); } } void checkActivities(boolean checkActivities) { this.checkActivities = checkActivities; } TargetAndRequestCode getTargetAndRequestCodeForIntent(Intent requestIntent) { return checkNotNull( intentRequestCodeMap.get(new Intent.FilterComparison(requestIntent)), "No intent matches %s among %s", requestIntent, intentRequestCodeMap.keySet()); } protected ComponentName startService(Intent intent) { startedServices.add(new Intent.FilterComparison(intent)); if (intent.getComponent() != null) { return intent.getComponent(); } return new ComponentName("some.service.package", "SomeServiceName-FIXME"); } boolean stopService(Intent name) { stoppedServices.add(new Intent.FilterComparison(name)); return startedServices.contains(new Intent.FilterComparison(name)); } /** * Set the default IBinder implementation that will be returned when the service is bound using * the specified Intent. The IBinder can implement the methods to simulate a bound Service. Useful * for testing the ServiceConnection implementation. * * @param name The ComponentName of the Service * @param service The IBinder implementation to return when the service is bound. */ void setComponentNameAndServiceForBindService(ComponentName name, IBinder service) { defaultServiceConnectionData = new ServiceConnectionDataWrapper(name, service); } /** * Set the IBinder implementation that will be returned when the service is bound using the * specified Intent. The IBinder can implement the methods to simulate a bound Service. Useful for * testing the ServiceConnection implementation. * * @param intent The exact Intent used in Context#bindService(...) * @param name The ComponentName of the Service * @param service The IBinder implementation to return when the service is bound. */ void setComponentNameAndServiceForBindServiceForIntent( Intent intent, ComponentName name, IBinder service) { serviceConnectionDataForIntent.put( new Intent.FilterComparison(intent), new ServiceConnectionDataWrapper(name, service)); } protected boolean bindService( Intent intent, int flags, Executor executor, ServiceConnection serviceConnection) { return bindService(intent, serviceConnection, new ExecutorServiceCallbackScheduler(executor)); } protected boolean bindService( final Intent intent, final ServiceConnection serviceConnection, int flags) { return bindService(intent, serviceConnection, new HandlerCallbackScheduler()); } private boolean bindService( final Intent intent, final ServiceConnection serviceConnection, ServiceCallbackScheduler serviceCallbackScheduler) { boundServiceConnections.add(serviceConnection); unboundServiceConnections.remove(serviceConnection); if (exceptionForBindService != null) { throw exceptionForBindService; } final Intent.FilterComparison filterComparison = new Intent.FilterComparison(intent); final ServiceConnectionDataWrapper serviceConnectionDataWrapper = serviceConnectionDataForIntent.getOrDefault(filterComparison, defaultServiceConnectionData); if (unbindableActions.contains(intent.getAction()) || unbindableComponents.contains(intent.getComponent()) || unbindableComponents.contains( serviceConnectionDataWrapper.componentNameForBindService)) { return false; } startedServices.add(filterComparison); Runnable onServiceConnectedRunnable = () -> { serviceConnectionDataForServiceConnection.put( serviceConnection, serviceConnectionDataWrapper); serviceConnection.onServiceConnected( serviceConnectionDataWrapper.componentNameForBindService, serviceConnectionDataWrapper.binderForBindService); }; if (bindServiceCallsOnServiceConnectedInline) { onServiceConnectedRunnable.run(); } else { serviceCallbackScheduler.schedule(onServiceConnectedRunnable); } return true; } protected void setUnbindServiceCallsOnServiceDisconnected(boolean flag) { unbindServiceCallsOnServiceDisconnected = flag; } protected void unbindService(final ServiceConnection serviceConnection) { if (unbindServiceShouldThrowIllegalArgument) { throw new IllegalArgumentException(); } unboundServiceConnections.add(serviceConnection); boundServiceConnections.remove(serviceConnection); Handler handler = new Handler(Looper.getMainLooper()); handler.post( () -> { final ServiceConnectionDataWrapper serviceConnectionDataWrapper; if (serviceConnectionDataForServiceConnection.containsKey(serviceConnection)) { serviceConnectionDataWrapper = serviceConnectionDataForServiceConnection.get(serviceConnection); } else { serviceConnectionDataWrapper = defaultServiceConnectionData; } if (unbindServiceCallsOnServiceDisconnected) { Logger.warn( "Configured to call onServiceDisconnected when unbindService is called. This is" + " not accurate Android behavior. Please update your tests and call" + " ShadowActivity#setUnbindCallsOnServiceDisconnected(false). This will" + " become default behavior in the future, which may break your tests if you" + " are expecting this inaccurate behavior."); serviceConnection.onServiceDisconnected( serviceConnectionDataWrapper.componentNameForBindService); } }); } protected List getBoundServiceConnections() { return boundServiceConnections; } void setUnbindServiceShouldThrowIllegalArgument(boolean flag) { unbindServiceShouldThrowIllegalArgument = flag; } void setThrowInBindService(SecurityException e) { exceptionForBindService = e; } void setBindServiceCallsOnServiceConnectedDirectly( boolean bindServiceCallsOnServiceConnectedInline) { this.bindServiceCallsOnServiceConnectedInline = bindServiceCallsOnServiceConnectedInline; } protected List getUnboundServiceConnections() { return unboundServiceConnections; } void declareActionUnbindable(String action) { unbindableActions.add(action); } void declareComponentUnbindable(ComponentName component) { checkNotNull(component); unbindableComponents.add(component); } public List getUnbindableActions() { return unbindableActions; } List getUnbindableComponents() { return unbindableComponents; } /** * Consumes the most recent {@code Intent} started by {@link * #startService(android.content.Intent)} and returns it. * * @return the most recently started {@code Intent} */ Intent getNextStartedService() { if (startedServices.isEmpty()) { return null; } else { return startedServices.remove(0).getIntent(); } } /** * Returns the most recent {@code Intent} started by {@link #startService(android.content.Intent)} * without consuming it. * * @return the most recently started {@code Intent} */ Intent peekNextStartedService() { if (startedServices.isEmpty()) { return null; } else { return startedServices.get(0).getIntent(); } } /** Clears all {@code Intent} started by {@link #startService(android.content.Intent)}. */ void clearStartedServices() { startedServices.clear(); } /** * Returns all {@code Intent} started by {@link #startService(android.content.Intent)} without * consuming them. * * @return the list of {@code Intent} */ List getAllStartedServices() { ArrayList startedServicesIntents = new ArrayList<>(); for (Intent.FilterComparison filterComparison : startedServices) { startedServicesIntents.add(filterComparison.getIntent()); } return startedServicesIntents; } /** * Consumes the {@code Intent} requested to stop a service by {@link * #stopService(android.content.Intent)} from the bottom of the stack of stop requests. */ Intent getNextStoppedService() { if (stoppedServices.isEmpty()) { return null; } else { return stoppedServices.remove(0).getIntent(); } } void sendStickyBroadcast(Intent intent, Context context) { stickyIntents.put(intent.getAction(), intent); sendBroadcast(intent, context); } void sendBroadcast(Intent intent, Context context) { sendBroadcastWithPermission( intent, /*userHandle=*/ null, /*receiverPermission=*/ null, context); } Intent registerReceiver( BroadcastReceiver receiver, IntentFilter filter, int flags, Context context) { return registerReceiver(receiver, filter, null, null, flags, context); } Intent registerReceiver( BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler, int flags, Context context) { return registerReceiverWithContext( receiver, filter, broadcastPermission, scheduler, flags, context); } Intent registerReceiverWithContext( BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler, int flags, Context context) { if (receiver != null) { synchronized (registeredReceivers) { registeredReceivers.add( new Wrapper(receiver, filter, context, broadcastPermission, scheduler, flags)); } } return processStickyIntents(filter, receiver, context); } private Intent processStickyIntents( IntentFilter filter, BroadcastReceiver receiver, Context context) { Intent result = null; for (Intent stickyIntent : stickyIntents.values()) { if (filter.matchAction(stickyIntent.getAction())) { if (result == null) { result = stickyIntent; } if (receiver != null) { receiver.setPendingResult(ShadowBroadcastPendingResult.createSticky(stickyIntent)); receiver.onReceive(context, stickyIntent); receiver.setPendingResult(null); } else if (result != null) { break; } } } return result; } void unregisterReceiver(BroadcastReceiver broadcastReceiver) { boolean found = false; synchronized (registeredReceivers) { Iterator iterator = registeredReceivers.iterator(); while (iterator.hasNext()) { Wrapper wrapper = iterator.next(); if (wrapper.broadcastReceiver == broadcastReceiver) { iterator.remove(); found = true; } } } if (!found) { throw new IllegalArgumentException("Receiver not registered: " + broadcastReceiver); } } void clearRegisteredReceivers() { synchronized (registeredReceivers) { registeredReceivers.clear(); } } /** * @deprecated use PackageManager.queryBroadcastReceivers instead */ @Deprecated boolean hasReceiverForIntent(Intent intent) { synchronized (registeredReceivers) { for (Wrapper wrapper : registeredReceivers) { if (wrapper.intentFilter.matchAction(intent.getAction())) { return true; } } } return false; } /** * @deprecated use PackageManager.queryBroadcastReceivers instead */ @Deprecated List getReceiversForIntent(Intent intent) { ArrayList broadcastReceivers = new ArrayList<>(); synchronized (registeredReceivers) { for (Wrapper wrapper : registeredReceivers) { if (wrapper.intentFilter.matchAction(intent.getAction())) { broadcastReceivers.add(wrapper.getBroadcastReceiver()); } } } return broadcastReceivers; } /** * @return copy of the list of {@link Wrapper}s for registered receivers */ ImmutableList getRegisteredReceivers() { ImmutableList copy; synchronized (registeredReceivers) { copy = ImmutableList.copyOf(registeredReceivers); } return copy; } int checkPermission(String permission, int pid, int uid) { if (pid == -1) { for (Map.Entry, Set> entry : grantedPermissionsMap.entrySet()) { if (entry.getKey().second == uid && entry.getValue().contains(permission)) { return PERMISSION_GRANTED; } } return PERMISSION_DENIED; } else { Set grantedPermissionsForPidUid = grantedPermissionsMap.get(new Pair(pid, uid)); return grantedPermissionsForPidUid != null && grantedPermissionsForPidUid.contains(permission) ? PERMISSION_GRANTED : PERMISSION_DENIED; } } void grantPermissions(String... permissionNames) { grantPermissions(Process.myPid(), Process.myUid(), permissionNames); } void grantPermissions(int pid, int uid, String... permissions) { Set grantedPermissionsForPidUid = grantedPermissionsMap.get(new Pair<>(pid, uid)); if (grantedPermissionsForPidUid == null) { grantedPermissionsForPidUid = new HashSet<>(); grantedPermissionsMap.put(new Pair<>(pid, uid), grantedPermissionsForPidUid); } Collections.addAll(grantedPermissionsForPidUid, permissions); } void denyPermissions(String... permissionNames) { denyPermissions(Process.myPid(), Process.myUid(), permissionNames); } void denyPermissions(int pid, int uid, String... permissions) { Set grantedPermissionsForPidUid = grantedPermissionsMap.get(new Pair<>(pid, uid)); if (grantedPermissionsForPidUid != null) { for (String permissionName : permissions) { grantedPermissionsForPidUid.remove(permissionName); } } } private Handler getMainHandler(Context context) { if (mainHandler == null) { mainHandler = new Handler(context.getMainLooper()); } return mainHandler; } /** Reflector interface for {@link Instrumentation}'s internals. */ @ForType(Instrumentation.class) public interface _Instrumentation_ { // <= JELLY_BEAN_MR1: void init( ActivityThread thread, Context instrContext, Context appContext, ComponentName component, @WithType("android.app.IInstrumentationWatcher") Object watcher); // > JELLY_BEAN_MR1: void init( ActivityThread thread, Context instrContext, Context appContext, ComponentName component, @WithType("android.app.IInstrumentationWatcher") Object watcher, @WithType("android.app.IUiAutomationConnection") Object uiAutomationConnection); @Direct ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options); @Direct ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, String target, Intent intent, int requestCode, Bundle options); } private static final class BroadcastResultHolder { private final int resultCode; private final String resultData; private final Bundle resultExtras; private BroadcastResultHolder(int resultCode, String resultData, Bundle resultExtras) { this.resultCode = resultCode; this.resultData = resultData; this.resultExtras = resultExtras; } private static ListenableFuture transform( BroadcastReceiver.PendingResult result) { ShadowBroadcastPendingResult shadowBroadcastPendingResult = Shadow.extract(result); return Futures.transform( shadowBroadcastPendingResult.getFuture(), pendingResult -> new BroadcastResultHolder( pendingResult.getResultCode(), pendingResult.getResultData(), pendingResult.getResultExtras(false)), directExecutor()); } } private static class ServiceConnectionDataWrapper { public final ComponentName componentNameForBindService; public final IBinder binderForBindService; private ServiceConnectionDataWrapper( ComponentName componentNameForBindService, IBinder binderForBindService) { this.componentNameForBindService = componentNameForBindService; this.binderForBindService = binderForBindService; } } /** Handles thread on which service lifecycle callbacks are run. */ private interface ServiceCallbackScheduler { void schedule(Runnable runnable); } private static final class ExecutorServiceCallbackScheduler implements ServiceCallbackScheduler { private final Executor executor; ExecutorServiceCallbackScheduler(Executor executor) { this.executor = executor; } @Override public void schedule(Runnable runnable) { executor.execute(runnable); } } private static final class HandlerCallbackScheduler implements ServiceCallbackScheduler { private final Handler mainHandler = new Handler(Looper.getMainLooper()); @Override public void schedule(Runnable runnable) { mainHandler.post(runnable); } } static final class TargetAndRequestCode { final String target; final int requestCode; private TargetAndRequestCode(String target, int requestCode) { this.target = target; this.requestCode = requestCode; } } public static Instrumentation getInstrumentation() { ActivityThread activityThread = (ActivityThread) RuntimeEnvironment.getActivityThread(); if (activityThread != null) { return activityThread.getInstrumentation(); } return null; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy