1 /*
2  * Copyright 2024 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 androidx.xr.runtime.openxr
18 
19 import android.os.IBinder
20 import androidx.annotation.RestrictTo
21 import androidx.xr.runtime.internal.Anchor
22 import androidx.xr.runtime.internal.TrackingState
23 import androidx.xr.runtime.math.Pose
24 import java.nio.ByteBuffer
25 import java.util.UUID
26 
27 /** Wraps the native [XrSpace] with the [Anchor] interface. */
28 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
29 public class OpenXrAnchor
30 internal constructor(
31     override public val nativePointer: Long,
32     private val xrResources: XrResources,
33     loadedUuid: UUID? = null,
34 ) : ExportableAnchor, Updatable {
35 
<lambda>null36     override public val anchorToken: IBinder by lazy { nativeGetAnchorToken(nativePointer) }
37 
38     override var pose: Pose = Pose()
39         private set
40 
41     override var trackingState: TrackingState = TrackingState.Paused
42         private set
43 
44     override var persistenceState: Anchor.PersistenceState = Anchor.PersistenceState.NotPersisted
45         private set
46 
47     override var uuid: UUID? = loadedUuid
48         private set
49 
persistnull50     override fun persist() {
51         if (
52             persistenceState == Anchor.PersistenceState.Persisted ||
53                 persistenceState == Anchor.PersistenceState.Pending
54         ) {
55             return
56         }
57         val uuidBytes =
58             checkNotNull(nativePersistAnchor(nativePointer)) { "Failed to persist anchor." }
59         UUIDFromByteArray(uuidBytes)?.let {
60             uuid = it
61             persistenceState = Anchor.PersistenceState.Pending
62         }
63     }
64 
detachnull65     override fun detach() {
66         check(nativeDestroyAnchor(nativePointer)) { "Failed to destroy anchor." }
67         xrResources.removeUpdatable(this)
68     }
69 
updatenull70     override fun update(xrTime: Long) {
71         val anchorState: AnchorState =
72             nativeGetAnchorState(nativePointer, xrTime)
73                 ?: throw IllegalStateException(
74                     "Could not retrieve data for anchor. Is the anchor valid?"
75                 )
76 
77         trackingState = anchorState.trackingState
78         anchorState.pose?.let { pose = it }
79         if (uuid != null && persistenceState == Anchor.PersistenceState.Pending) {
80             persistenceState = nativeGetPersistenceState(uuid!!)
81         }
82     }
83 
84     internal companion object {
UUIDFromByteArraynull85         internal fun UUIDFromByteArray(bytes: ByteArray?): UUID? {
86             if (bytes == null || bytes.size != 16) {
87                 return null
88             }
89             val longBytes = ByteBuffer.wrap(bytes)
90             val mostSignificantBits = longBytes.long
91             val leastSignificantBits = longBytes.long
92             return UUID(mostSignificantBits, leastSignificantBits)
93         }
94     }
95 
nativeGetAnchorStatenull96     private external fun nativeGetAnchorState(nativePointer: Long, timestampNs: Long): AnchorState?
97 
98     private external fun nativeGetAnchorToken(nativePointer: Long): IBinder
99 
100     private external fun nativePersistAnchor(nativePointer: Long): ByteArray?
101 
102     private external fun nativeGetPersistenceState(uuid: UUID): Anchor.PersistenceState
103 
104     private external fun nativeDestroyAnchor(nativePointer: Long): Boolean
105 }
106 
107 internal fun Anchor.PersistenceState.Companion.fromOpenXrPersistenceState(
108     value: Int
109 ): Anchor.PersistenceState =
110     when (value) {
111         0 ->
112             Anchor.PersistenceState
113                 .NotPersisted // XR_ANCHOR_PERSIST_STATE_PERSIST_NOT_REQUESTED_ANDROID
114         1 -> Anchor.PersistenceState.Pending // XR_ANCHOR_PERSIST_STATE_PERSIST_PENDING_ANDROID
115         2 -> Anchor.PersistenceState.Persisted // XR_ANCHOR_PERSIST_STATE_PERSISTED_ANDROID
116         else -> {
117             throw IllegalArgumentException("Invalid persistence state value.")
118         }
119     }
120