├── .gitignore
├── LICENSE
├── README.md
├── simple-aidl-objects.jar
└── src
└── mn
└── hart
└── android
└── simpleaidl
├── AIDLBundleable.java
├── AIDLBundler.aidl
├── AIDLBundler.java
├── AIDLObject.aidl
└── AIDLObject.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store*
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012-2012, Kevin Hartman
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above copyright
9 | notice, this list of conditions and the following disclaimer in the
10 | documentation and/or other materials provided with the distribution.
11 | * Neither the name of Simple AIDL Objects nor the
12 | names of its contributors may be used to endorse or promote products
13 | derived from this software without specific prior written permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL KEVIN HARTMAN BE LIABLE FOR ANY
19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Simple AIDL Objects
2 | ===================
3 |
4 | Simple AIDL Objects offers a versatile solution for AIDL-defined methods that need to accept interfaces or superclasses, allowing design patterns to be used more easily across Android services and their clients.
5 |
6 | by Kevin Hartman
7 |
8 | Disclaimers
9 | ===========
10 | * This was just a fun tangent project that I did to work out how I would have wanted Android's AIDL API to look. I don't maintain it, and I don't recommend using it for anything other than science.
11 |
12 | * This is an interesting approach to enabling AIDL-defined methods to accept interfaces and superclasses as parameters. This is possible without Simple AIDL Objects, but it's a messy solution. For production, you shouldn't use this library. Check out this blog post which describes how to go about doing that.
13 |
14 | Preface
15 | =======
16 | Android provides an interface definition language that developers can use directly in order to create and communicate with a Service, across processes, in a complex way that cannot be suited by using a simple Messenger.
17 |
18 | Problem
19 | =======
20 | Methods that can be defined in an AIDL service (ex. a method within IPotatoSaladService.aidl) can, by default, only accept some very basic types as parameters. The full list can be found on the Android developer website. If the developer wishes to have a method accept custom types (ex. Potato), they must:
21 |
22 | * Define a new AIDL interface for said type (ex. Potato.aidl)
23 | * Implement Parcelable in the type's class (implementing all of its required methods, of course)
24 | * Define a static CREATOR field with a Creator object that requires a concrete implementation, which must be set up as well...
25 |
26 | That's a lot of complication.
27 |
28 | Solution
29 | ========
30 | ##How it Simplifies:
31 |
32 | * No longer do you need to define a .aidl file for the type.
33 | * Just implement AIDLBundleable or extend AIDLObject and define two methods in your new type's class.
34 |
35 | ##How it Handles Inheritance:
36 |
37 | If your type implements AIDLBundleable, all you need to do is instantiate a new AIDLBundler with your type object as a parameter and pass the AIDLBundler to your AIDL service's methods.
38 |
39 | If your type extends AIDLObject, you can pass your type or its inheritors directly to your AIDL service's methods, and this library will take care of the rest for you, allowing you to use inheritance naturally.
40 |
41 | Installation
42 | ============
43 | Grab simple-aidl-objects.jar from root, and add it to your Android project's build path.
44 |
45 | Usage
46 | =====
47 | The examples provided for both solutions are strategy design pattern implementations.
48 |
49 | ##Interface Solution
50 | This is an example of how you can use the AIDLBundler solution included within this library to create a new type for use with a Strategy design pattern.
51 |
52 | IMyStrategyService.aidl:
53 | `````java
54 | package mn.hart.example;
55 | import mn.hart.android.simpleaidl.AIDLBundler;
56 |
57 | interface IMyStrategyService {
58 | void setStrategy(in AIDLBundler strategy);
59 | }
60 | `````
61 | This is the AIDL file associated with your AIDL-using service. All you need to note here is that the method used to set the strategy to use in our service takes an AIDLBundler object.
62 |
63 | MyStrategy.java:
64 | `````java
65 | package mn.hart.example;
66 | import mn.hart.android.simpleaidl.AIDLBundleable;
67 |
68 | public abstract class MyStrategy implements AIDLBundleable {
69 | // Nothing here, because I'm too lazy to make our strategies
70 | // do something.
71 | }
72 | `````
73 | This is the strategy abstract class that we can use to define some standard functionality for all of our concrete strategies.
74 |
75 | MyConcreteStrategy.java:
76 | `````java
77 | package mn.hart.example;
78 | import android.os.Bundle;
79 | import android.util.Log;
80 |
81 | public class MyConcreteStrategy extends MyStrategy {
82 | private final String INTERVAL_KEY = "interval";
83 | private long interval;
84 |
85 | /**
86 | * Notice how the constructor accepts a long. That's
87 | * not required by MyStrategy. We could've constructed this
88 | * object with whatever we'd wanted. Point is, subclasses
89 | * of our type can be constructed with variable constructors
90 | * that differ from one another. Without using serialization,
91 | * implementing that was an interesting problem.
92 | */
93 | public MyConcreteStrategy(long intervalMillis) {
94 | this.interval = intervalMillis;
95 | }
96 |
97 | /**
98 | * Sets this object up on the other side. Should do
99 | * an initialization equivalent to that of the
100 | * constructor.
101 | */
102 | @Override
103 | public void contructFromInstanceData(Bundle instanceData) {
104 | this.interval = instanceData.getLong(INTERVAL_KEY);
105 | }
106 |
107 | /**
108 | * Stuff this object's information into a Bundle.
109 | * We will get this bundle back on the other side
110 | * and will use it to remake this object.
111 | */
112 | @Override
113 | public void writeInstanceData(Bundle instanceData) {
114 | instanceData.putLong(INTERVAL_KEY, interval);
115 | }
116 | }
117 | `````
118 | This is a concrete strategy that happens to take a parameter of type long in its constructor. As noted in the comments within the actual code, constructors take whatever you'd like as parameters. Just like they would if you weren't implementing your strategy pattern over AIDL.
119 |
120 | MyStrategyActivity.java:
121 | `````java
122 | package mn.hart.example;
123 | import mn.hart.android.simpleaidl.AIDLBundler;
124 |
125 | ...
126 |
127 | public void onServiceConnected(ComponentName className, IBinder service) {
128 | IMyStrategyService mIMyStrategyService = IMyStrategyService.Stub.asInterface(service);
129 |
130 | try {
131 | mIMyStrategyService.setStrategy(new AIDLBundler(new MyConcreteStrategy(100L)));
132 | } catch (RemoteException e) {
133 | // Fail
134 | }
135 | }
136 |
137 | ...
138 |
139 | `````
140 | Notice how the strategy is set using the AIDLBundler above. A particular concrete strategy is passed, but we could've passed any concrete strategy that we'd wanted.
141 |
142 | MyStrategyService.java:
143 | `````java
144 | /**
145 | * A client activity that can use the service remotely.
146 | */
147 |
148 | package mn.hart.example;
149 | import mn.hart.android.simpleaidl.AIDLBundler;
150 |
151 | ...
152 |
153 | public class MyStrategyService extends Service {
154 | MyStrategy strategy = null;
155 |
156 | @Override
157 | public IBinder onBind(Intent intent) {
158 | return mBinder;
159 | }
160 |
161 | private synchronized void setStrategySafe(MyStrategy myStrategy) {
162 | strategy = myStrategy;
163 | }
164 |
165 | private final IMyStrategyService.Stub mBinder = new IMyStrategyService.Stub() {
166 | public void setStrategy(AIDLBundler aidlBundler) throws RemoteException {
167 | setStrategySafe((MyStrategy) aidlBundler.getBundleable());
168 | }
169 | };
170 | }
171 | `````
172 | Notice how the instance is retrieved from the AIDLBundler. An explicit cast to our interface type is required.
173 |
174 |
175 | ##Abstract Class Solution
176 | This is an example of how you can use the AIDLObject solution included within this library to create a new type for use with a Strategy design pattern. With this option, you're able to use inheritance directly, without the AIDLBundler wrapper. The caveat, however, is that you must extend AIDLObject, rather than just implement AIDLBundleable.
177 |
178 |
179 | IMyStrategyService.aidl:
180 | `````java
181 | package mn.hart.example;
182 | import mn.hart.android.simpleaidl.AIDLObject;
183 |
184 | interface IMyStrategyService {
185 | void setStrategy(in AIDLObject strategy);
186 | }
187 | `````
188 | This is the AIDL file associated with your AIDL-using service. All you need to note here is that the method used to set the strategy to use in our service takes an AIDLObject object.
189 |
190 | MyStrategy.java:
191 | `````java
192 | package mn.hart.example;
193 | import mn.hart.android.simpleaidl.AIDLObject;
194 |
195 | public abstract class MyStrategy extends AIDLObject {
196 | // Nothing here, because I'm too lazy to make our strategies
197 | // do something.
198 | }
199 | `````
200 | This is the strategy abstract class that we can use to define some standard functionality for all of our concrete strategies.
201 |
202 | MyConcreteStrategy.java:
203 | `````java
204 | package mn.hart.example;
205 | import android.os.Bundle;
206 | import android.util.Log;
207 |
208 | public class MyConcreteStrategy extends MyStrategy {
209 | private final String INTERVAL_KEY = "interval";
210 | private long interval;
211 |
212 | /**
213 | * Notice how the constructor accepts a long. That's
214 | * not required by MyStrategy. We could've constructed this
215 | * object with whatever we'd wanted. Point is, subclasses
216 | * of our type can be constructed with variable constructors
217 | * that differ from one another. Without using serialization,
218 | * implementing that was an interesting problem.
219 | */
220 | public MyConcreteStrategy(long intervalMillis) {
221 | this.interval = intervalMillis;
222 | }
223 |
224 | /**
225 | * Sets this object up on the other side. Should do
226 | * an initialization equivalent to that of the
227 | * constructor.
228 | */
229 | @Override
230 | public void contructFromInstanceData(Bundle instanceData) {
231 | this.interval = instanceData.getLong(INTERVAL_KEY);
232 | }
233 |
234 | /**
235 | * Stuff this object's information into a Bundle.
236 | * We will get this bundle back on the other side
237 | * and will use it to remake this object.
238 | */
239 | @Override
240 | public void writeInstanceData(Bundle instanceData) {
241 | instanceData.putLong(INTERVAL_KEY, interval);
242 | }
243 | }
244 | `````
245 |
246 | MyStrategyActivity.java:
247 | `````java
248 | package mn.hart.example;
249 |
250 | ...
251 |
252 | public void onServiceConnected(ComponentName className, IBinder service) {
253 | IMyStrategyService mIMyStrategyService = IMyStrategyService.Stub.asInterface(service);
254 |
255 | try {
256 | mIMyStrategyService.setStrategy(new MyConcreteStrategy(100L));
257 | } catch (RemoteException e) {
258 | // Fail
259 | }
260 | }
261 |
262 | ...
263 |
264 | `````
265 |
266 | MyStrategyService.java:
267 | `````java
268 | /**
269 | * A client activity that can use the service remotely.
270 | */
271 |
272 | package mn.hart.example;
273 | import mn.hart.android.simpleaidl.AIDLObject;
274 |
275 | ...
276 |
277 | public class MyStrategyService extends Service {
278 | MyStrategy strategy = null;
279 |
280 | @Override
281 | public IBinder onBind(Intent intent) {
282 | return mBinder;
283 | }
284 |
285 | private synchronized void setStrategySafe(MyStrategy myStrategy) {
286 | this.strategy = myStrategy;
287 | }
288 |
289 | private final IMyStrategyService.Stub mBinder = new IMyStrategyService.Stub() {
290 | public void setStrategy(AIDLObject aidlObject) throws RemoteException {
291 | setStrategySafe((MyStrategy) aidlObject);
292 | }
293 | };
294 | }
295 | `````
296 | An explicit cast to our interface type is required from AIDLObject.
297 |
298 | Limitations
299 | ===========
300 |
301 | ##Explicit Casting
302 | Using the AIDLBundler and Bundleable interface solution, an explicit cast to your desired type on the Service side is required:
303 |
304 |
305 | `````java
306 | ...
307 | YourType type = (YourType) aidlBundler.getBundleable(); // Where YourType implements AIDLBundleable
308 | `````
309 |
310 | This is due to an implementation detail of AIDL itself. Specifically, generics were not accounted for in the design of AIDL.
311 |
312 | The explicit casting means that it's possible to pass particular AIDLBundler and AIDLObject objects to service methods that should not be allowed to accept them- which will result in a runtime error. With very mild caution, this should be easily avoidable.
313 |
314 | ##Not so Standard Reflection
315 | Because this library uses some less than standard reflection in order to make your life so simple, there's a possiblity that it may not work with particular versions of Android. It is currently tested only on Android 4.0.3 and 4.1.1.
316 |
317 | ##Current Development State
318 | Simple AIDL Objects is in a useable state, but it was just a fun tangent project.
319 |
--------------------------------------------------------------------------------
/simple-aidl-objects.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevinhartman/simple-aidl-objects/6e4c9539c565789c4df5864e97ae0ff0d6eeaf64/simple-aidl-objects.jar
--------------------------------------------------------------------------------
/src/mn/hart/android/simpleaidl/AIDLBundleable.java:
--------------------------------------------------------------------------------
1 | package mn.hart.android.simpleaidl;
2 |
3 | import android.os.Bundle;
4 |
5 | /**
6 | * Provides constructs that allow an object to pack and
7 | * recreate itself before and after IPC, respectively.
8 | * into a Bundle and r
9 | * @author Kevin Hartman
10 | * @version 1.0
11 | */
12 | public interface AIDLBundleable {
13 |
14 | /**
15 | * Set up instance using provided data.
16 | * @param instanceData Bundle created by this object before IPC.
17 | */
18 | public void contructFromInstanceData(Bundle instanceData);
19 |
20 | /**
21 | * Write instance data prior to shuttling.
22 | * @param instanceData Bundle in which to write instance data.
23 | */
24 | public void writeInstanceData(Bundle instanceData);
25 | }
26 |
--------------------------------------------------------------------------------
/src/mn/hart/android/simpleaidl/AIDLBundler.aidl:
--------------------------------------------------------------------------------
1 | package mn.hart.android.simpleaidl;
2 |
3 | parcelable AIDLBundler;
--------------------------------------------------------------------------------
/src/mn/hart/android/simpleaidl/AIDLBundler.java:
--------------------------------------------------------------------------------
1 | package mn.hart.android.simpleaidl;
2 |
3 | import java.lang.reflect.Constructor;
4 |
5 | import android.os.Bundle;
6 | import android.os.Parcel;
7 | import android.os.Parcelable;
8 | import android.util.Log;
9 |
10 | /**
11 | * Handles the packing of an AIDLBundleable implementer
12 | * prior to its shuttling across processes, and is responsible
13 | * for orchestrating that implementer's reconstruction on
14 | * the other side.
15 | * @author Kevin Hartman
16 | * @version 1.0
17 | */
18 | public class AIDLBundler implements Parcelable {
19 | private AIDLBundleable bundleable;
20 |
21 | /**
22 | * Constructs an AIDLBundler to handle the bundling
23 | * and unpacking of an AIDLBundleable implementer
24 | * before and after its interprocess transmission,
25 | * respectively.
26 | *
27 | * NOTE: This constructor is also called when the
28 | * AIDLBundler object itself is recreated on the other
29 | * side.
30 | * @param bundleable The AIDLBundleable to be shuttled.
31 | */
32 | public AIDLBundler(AIDLBundleable bundleable) {
33 | this.bundleable = bundleable;
34 | }
35 |
36 | /**
37 | * Get the AIDLBundleable that is housed by this
38 | * AIDLBundler.
39 | *
40 | * NOTE: A call to this method from the local side
41 | * will not return the same object as a call to this
42 | * method from the remote side. However, these objects
43 | * should be equivalent if bundling and unpacking is
44 | * done appropriately in the AIDLBundleable implementer
45 | * class.
46 | * @return The AIDLBundleable housed by this AIDLBundler
47 | */
48 | public AIDLBundleable getBundleable() {
49 | return bundleable;
50 | }
51 |
52 | /**
53 | * Specify special flags for marshaling process
54 | */
55 | public int describeContents() {
56 | return 0;
57 | }
58 |
59 | /**
60 | * Write the AIDLBundleable's data bundle and save
61 | * that, and the AIDLBundleable's name, in the parcel
62 | * that will be used in reconstruction.
63 | * @param out The parcel in which to save the AIDLBundleable's data
64 | */
65 | public void writeToParcel(Parcel out, int flags) {
66 | Bundle instanceData = new Bundle();
67 | bundleable.writeInstanceData(instanceData);
68 |
69 | out.writeString(bundleable.getClass().getName());
70 | out.writeBundle(instanceData);
71 |
72 | }
73 |
74 | /**
75 | * Creator object that the AIDL generated service class
76 | * will be looking for when it's time to recreate this
77 | * AIDLBundler on the other side.
78 | */
79 | public static final Creator CREATOR
80 | = new Parcelable.Creator() {
81 |
82 | /**
83 | * Instantiate the desired AIDLBundleable by name and provide
84 | * @param in The AIDLBundleable's data.
85 | * @return An AIDLBundler, holding the desired AIDLBundleable or null if error.
86 | */
87 | public AIDLBundler createFromParcel(Parcel in) {
88 | String className = in.readString();
89 | Bundle instanceData = in.readBundle();
90 |
91 | try {
92 | Constructor> implementerConstructor = AndroidMagicConstructorMaker.make(Class.forName(className));
93 | implementerConstructor.setAccessible(true);
94 |
95 | AIDLBundleable implementer = (AIDLBundleable) implementerConstructor.newInstance();
96 | implementer.contructFromInstanceData(instanceData);
97 |
98 | AIDLBundler subclasser = new AIDLBundler(implementer);
99 |
100 | return subclasser;
101 |
102 | } catch (Exception e) {
103 | Log.e("AIDLObject.CREATOR.createFromParcel", e.getCause().getMessage());
104 | }
105 |
106 | return null;
107 | }
108 |
109 | /**
110 | * Required by Parcelable
111 | */
112 | public AIDLBundler[] newArray(int size) {
113 | return new AIDLBundler[size];
114 | }
115 | };
116 |
117 | /**
118 | * Create a new no-args constructor for any class by its name.
119 | * any of its constructors.
120 | * @author Kevin Hartman
121 | * @version 1.0
122 | *
123 | */
124 | private static class AndroidMagicConstructorMaker {
125 | /**
126 | *
127 | * @param clazz The class for which to create a constructor.
128 | * @return A no-args constructor.
129 | * @throws Exception
130 | */
131 | @SuppressWarnings("unchecked")
132 | public static Constructor make(Class clazz) throws Exception {
133 | Constructor> constr = Constructor.class.getDeclaredConstructor(
134 | Class.class, // Class declaringClass
135 | Class[].class, // Class>[] parameterTypes
136 | Class[].class, // Class>[] checkedExceptions
137 | int.class); // int slot
138 | constr.setAccessible(true);
139 |
140 | return (Constructor) constr.newInstance(clazz, new Class[0],
141 | new Class[0], 1);
142 | }
143 | }
144 |
145 |
146 | }
147 |
--------------------------------------------------------------------------------
/src/mn/hart/android/simpleaidl/AIDLObject.aidl:
--------------------------------------------------------------------------------
1 | package mn.hart.android.simpleaidl;
2 |
3 | parcelable AIDLObject;
--------------------------------------------------------------------------------
/src/mn/hart/android/simpleaidl/AIDLObject.java:
--------------------------------------------------------------------------------
1 | package mn.hart.android.simpleaidl;
2 |
3 | import java.lang.reflect.Constructor;
4 | import android.os.Bundle;
5 | import android.os.Parcel;
6 | import android.os.Parcelable;
7 | import android.util.Log;
8 |
9 | /**
10 | * Packs itself prior to its shuttling across processes,
11 | * and is responsible for orchestrating its own reconstruction
12 | * on the other side.
13 | * @author Kevin Hartman
14 | * @version 1.0
15 | */
16 | public abstract class AIDLObject implements Parcelable {
17 |
18 | /**
19 | * Specify special flags for marshaling process
20 | */
21 | public int describeContents() {
22 | return 0;
23 | }
24 |
25 | /**
26 | * Write this object's data bundle and save
27 | * that, and its name, in the parcel
28 | * that will be used in reconstruction.
29 | * @param out The parcel in which to save its data.
30 | */
31 | public void writeToParcel(Parcel out, int flags) {
32 | Bundle instanceData = new Bundle();
33 | writeInstanceData(instanceData);
34 |
35 | out.writeString(this.getClass().getName());
36 | out.writeBundle(instanceData);
37 |
38 | }
39 |
40 | /**
41 | * Creator object that the AIDL generated service class
42 | * will be looking for when it's time to recreate this
43 | * AIDLObject on the other side.
44 | */
45 | public static final Creator CREATOR
46 | = new Parcelable.Creator() {
47 |
48 | /**
49 | * Instantiate the desired AIDLObject subclass by name and provide
50 | * it with its data bundle.
51 | * @param in The AIDLObject's data.
52 | * @return An AIDLObject, or null if error.
53 | */
54 | public AIDLObject createFromParcel(Parcel in) {
55 | String className = in.readString();
56 | Bundle instanceData = in.readBundle();
57 |
58 | try {
59 | Constructor> implementerConstructor = AndroidMagicConstructorMaker.make(Class.forName(className));
60 | implementerConstructor.setAccessible(true);
61 | AIDLObject implementer = (AIDLObject) implementerConstructor.newInstance();
62 |
63 | implementer.contructFromInstanceData(instanceData);
64 | return implementer;
65 |
66 | } catch (Exception e) {
67 | Log.e("AIDLObject.CREATOR.createFromParcel", e.getCause().getMessage());
68 | }
69 |
70 | return null;
71 | }
72 |
73 | /**
74 | * Required by Parcelable
75 | */
76 | public AIDLObject[] newArray(int size) {
77 | return new AIDLObject[size];
78 | }
79 | };
80 |
81 | /**
82 | * Create a new no-args constructor for any class by its name.
83 | * @author Kevin Hartman
84 | * @version 1.0
85 | *
86 | */
87 | private static class AndroidMagicConstructorMaker {
88 |
89 | @SuppressWarnings("unchecked")
90 | public static Constructor make(Class clazz) throws Exception {
91 | Constructor> constr = Constructor.class.getDeclaredConstructor(
92 | Class.class, // Class declaringClass
93 | Class[].class, // Class>[] parameterTypes
94 | Class[].class, // Class>[] checkedExceptions
95 | int.class); // int slot
96 | constr.setAccessible(true);
97 |
98 | return (Constructor) constr.newInstance(clazz, new Class[0],
99 | new Class[0], 1);
100 | }
101 | }
102 |
103 | /**
104 | * Set up instance using provided data.
105 | * @param instanceData Bundle created by this object before IPC.
106 | */
107 | protected abstract void contructFromInstanceData(Bundle instanceData);
108 |
109 | /**
110 | * Write instance data prior to shuttling.
111 | * @param instanceData Bundle in which to write instance data.
112 | */
113 | protected abstract void writeInstanceData(Bundle instanceData);
114 |
115 |
116 |
117 |
118 | }
119 |
--------------------------------------------------------------------------------