• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.shadows;
2 
3 import static android.os.Build.VERSION_CODES.TIRAMISU;
4 
5 import android.os.Build.VERSION_CODES;
6 import android.os.CancellationSignal;
7 import android.os.PersistableBundle;
8 import android.uwb.AdapterState;
9 import android.uwb.RangingSession;
10 import android.uwb.StateChangeReason;
11 import android.uwb.UwbManager;
12 import android.uwb.UwbManager.AdapterStateCallback;
13 import com.google.common.collect.ImmutableList;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.concurrent.Executor;
17 import org.robolectric.annotation.Implementation;
18 import org.robolectric.annotation.Implements;
19 import org.robolectric.shadow.api.Shadow;
20 
21 /** Adds Robolectric support for UWB ranging. */
22 @Implements(value = UwbManager.class, minSdk = VERSION_CODES.S, isInAndroidSdk = false)
23 public class ShadowUwbManager {
24 
25   private AdapterStateCallback callback;
26 
27   private int adapterState = AdapterStateCallback.STATE_ENABLED_INACTIVE;
28 
29   private int stateChangedReason = AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY;
30 
31   private PersistableBundle specificationInfo = new PersistableBundle();
32 
33   private List<PersistableBundle> chipInfos = new ArrayList<>();
34 
35   private ShadowRangingSession.Adapter adapter =
36       new ShadowRangingSession.Adapter() {
37         @Override
38         public void onOpen(
39             RangingSession session, RangingSession.Callback callback, PersistableBundle params) {}
40 
41         @Override
42         public void onStart(
43             RangingSession session, RangingSession.Callback callback, PersistableBundle params) {}
44 
45         @Override
46         public void onReconfigure(
47             RangingSession session, RangingSession.Callback callback, PersistableBundle params) {}
48 
49         @Override
50         public void onStop(RangingSession session, RangingSession.Callback callback) {}
51 
52         @Override
53         public void onClose(RangingSession session, RangingSession.Callback callback) {}
54       };
55 
56   @Implementation
registerAdapterStateCallback(Executor executor, AdapterStateCallback callback)57   protected void registerAdapterStateCallback(Executor executor, AdapterStateCallback callback) {
58     this.callback = callback;
59     callback.onStateChanged(adapterState, stateChangedReason);
60   }
61 
62   /**
63    * Simulates adapter state change by invoking a callback registered by {@link
64    * ShadowUwbManager#registerAdapterStateCallback(Executor executor, AdapterStateCallback
65    * callback)}.
66    *
67    * @param state A state that should be passed to the callback.
68    * @param reason A reason that should be passed to the callback.
69    * @throws IllegalArgumentException if the callback is missing.
70    */
simulateAdapterStateChange(@dapterState int state, @StateChangeReason int reason)71   public void simulateAdapterStateChange(@AdapterState int state, @StateChangeReason int reason) {
72     if (this.callback == null) {
73       throw new IllegalArgumentException("AdapterStateCallback should not be null");
74     }
75 
76     adapterState = state;
77     stateChangedReason = reason;
78 
79     this.callback.onStateChanged(state, reason);
80   }
81 
82   /**
83    * Simply returns the bundle provided by {@link ShadowUwbManager#setSpecificationInfo()}, allowing
84    * the tester to dictate available features.
85    */
86   @Implementation
getSpecificationInfo()87   protected PersistableBundle getSpecificationInfo() {
88     return specificationInfo;
89   }
90 
91   /**
92    * Instantiates a {@link ShadowRangingSession} with the adapter provided by {@link
93    * ShadowUwbManager#setUwbAdapter()}, allowing the tester dictate the results of ranging attempts.
94    *
95    * @throws IllegalArgumentException if UWB is disabled.
96    */
97   @Implementation
openRangingSession( PersistableBundle params, Executor executor, RangingSession.Callback callback)98   protected CancellationSignal openRangingSession(
99       PersistableBundle params, Executor executor, RangingSession.Callback callback) {
100     if (!isUwbEnabled()) {
101       throw new IllegalStateException("Uwb is not enabled");
102     }
103     RangingSession session = ShadowRangingSession.newInstance(executor, callback, adapter);
104     CancellationSignal cancellationSignal = new CancellationSignal();
105     cancellationSignal.setOnCancelListener(session::close);
106     Shadow.<ShadowRangingSession>extract(session).open(params);
107     return cancellationSignal;
108   }
109 
110   /** Sets the UWB adapter to use for new {@link ShadowRangingSession}s. */
setUwbAdapter(ShadowRangingSession.Adapter adapter)111   public void setUwbAdapter(ShadowRangingSession.Adapter adapter) {
112     this.adapter = adapter;
113   }
114 
115   /** Sets the bundle to be returned by {@link android.uwb.UwbManager#getSpecificationInfo}. */
setSpecificationInfo(PersistableBundle specificationInfo)116   public void setSpecificationInfo(PersistableBundle specificationInfo) {
117     this.specificationInfo = new PersistableBundle(specificationInfo);
118   }
119 
120   /**
121    * Instantiates a {@link ShadowRangingSession} with the multi-chip API call. {@code chipId} is
122    * unused in the shadow implementation, so this is equivalent to {@link
123    * ShadowUwbManager#openRangingSession(PersistableBundle, Executor, RangingSession.Callback)}
124    */
125   @Implementation(minSdk = TIRAMISU)
openRangingSession( PersistableBundle params, Executor executor, RangingSession.Callback callback, String chipId)126   protected CancellationSignal openRangingSession(
127       PersistableBundle params,
128       Executor executor,
129       RangingSession.Callback callback,
130       String chipId) {
131     return openRangingSession(params, executor, callback);
132   }
133 
134   /** Returns whether UWB is enabled or disabled. */
135   @Implementation(minSdk = TIRAMISU)
isUwbEnabled()136   protected boolean isUwbEnabled() {
137     return adapterState != AdapterStateCallback.STATE_DISABLED;
138   }
139 
140   /**
141    * Disables or enables UWB by the user.
142    *
143    * @param enabled value representing intent to disable or enable UWB.
144    */
145   @Implementation
setUwbEnabled(boolean enabled)146   protected void setUwbEnabled(boolean enabled) {
147     boolean stateChanged = false;
148 
149     if (enabled && adapterState == AdapterStateCallback.STATE_DISABLED) {
150       adapterState = AdapterStateCallback.STATE_ENABLED_INACTIVE;
151       stateChanged = true;
152     } else if (!enabled && adapterState != AdapterStateCallback.STATE_DISABLED) {
153       adapterState = AdapterStateCallback.STATE_DISABLED;
154       stateChanged = true;
155     }
156 
157     if (this.callback != null && stateChanged) {
158       this.callback.onStateChanged(
159           adapterState, AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY);
160     }
161   }
162 
163   /**
164    * Simply returns the List of bundles provided by {@link ShadowUwbManager#setChipInfos(List)} ,
165    * allowing the tester to set multi-chip configuration.
166    */
167   @Implementation(minSdk = TIRAMISU)
getChipInfos()168   protected List<PersistableBundle> getChipInfos() {
169     return ImmutableList.copyOf(chipInfos);
170   }
171 
172   /** Sets the list of bundles to be returned by {@link android.uwb.UwbManager#getChipInfos}. */
setChipInfos(List<PersistableBundle> chipInfos)173   public void setChipInfos(List<PersistableBundle> chipInfos) {
174     this.chipInfos = new ArrayList<>(chipInfos);
175   }
176 }
177