1 /*
2  * Copyright (C) 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 android.app.wearable;
18 
19 import android.annotation.FlaggedApi;
20 import android.annotation.NonNull;
21 import android.annotation.SystemApi;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.os.PersistableBundle;
25 
26 import java.time.Duration;
27 
28 /**
29  * Data class for a data request for wearable sensing.
30  *
31  * @hide
32  */
33 @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
34 @SystemApi
35 public final class WearableSensingDataRequest implements Parcelable {
36     private static final int MAX_REQUEST_SIZE = 200;
37     private static final Duration RATE_LIMIT_WINDOW_SIZE = Duration.ofMinutes(1);
38     private static final int RATE_LIMIT = 30;
39 
40     private final int mDataType;
41     @NonNull private final PersistableBundle mRequestDetails;
42 
WearableSensingDataRequest(int dataType, @NonNull PersistableBundle requestDetails)43     private WearableSensingDataRequest(int dataType, @NonNull PersistableBundle requestDetails) {
44         mDataType = dataType;
45         mRequestDetails = requestDetails;
46     }
47 
48     /** Returns the data type this request is for. */
getDataType()49     public int getDataType() {
50         return mDataType;
51     }
52 
53     /** Returns the details for this request. */
54     @NonNull
getRequestDetails()55     public PersistableBundle getRequestDetails() {
56         return mRequestDetails;
57     }
58 
59     /** Returns the data size of this object when it is parcelled. */
getDataSize()60     public int getDataSize() {
61         Parcel parcel = Parcel.obtain();
62         try {
63             writeToParcel(parcel, describeContents());
64             return parcel.dataSize();
65         } finally {
66             parcel.recycle();
67         }
68     }
69 
70     @Override
writeToParcel(@onNull Parcel dest, int flags)71     public void writeToParcel(@NonNull Parcel dest, int flags) {
72         dest.writeInt(mDataType);
73         dest.writeTypedObject(mRequestDetails, flags);
74     }
75 
76     @Override
describeContents()77     public int describeContents() {
78         return 0;
79     }
80 
81     @Override
toString()82     public String toString() {
83         return "WearableSensingDataRequest { "
84                 + "dataType = "
85                 + mDataType
86                 + ", "
87                 + "requestDetails = "
88                 + mRequestDetails
89                 + " }";
90     }
91 
92     /**
93      * Returns a String representation of this data request that shows its contents.
94      *
95      * @hide
96      */
toExpandedString()97     public String toExpandedString() {
98         if (mRequestDetails != null) {
99             // Trigger unparcelling so that its individual fields will be listed in toString
100             boolean unused =
101                     mRequestDetails.getBoolean(
102                             "PlaceholderForWearableSensingDataRequest#toExpandedString()");
103         }
104         return toString();
105     }
106 
107     /**
108      * The bundle key for this class of object, used in {@code RemoteCallback#sendResult}.
109      *
110      * @hide
111      */
112     public static final String REQUEST_BUNDLE_KEY =
113             "android.app.wearable.WearableSensingDataRequestBundleKey";
114 
115     /**
116      * The bundle key for the status callback for a data request, used in {@code
117      * RemoteCallback#sendResult}.
118      *
119      * @hide
120      */
121     public static final String REQUEST_STATUS_CALLBACK_BUNDLE_KEY =
122             "android.app.wearable.WearableSensingDataRequestStatusCallbackBundleKey";
123 
124     public static final @NonNull Parcelable.Creator<WearableSensingDataRequest> CREATOR =
125             new Parcelable.Creator<WearableSensingDataRequest>() {
126                 @Override
127                 public WearableSensingDataRequest[] newArray(int size) {
128                     return new WearableSensingDataRequest[size];
129                 }
130 
131                 @Override
132                 public WearableSensingDataRequest createFromParcel(@NonNull Parcel in) {
133                     int dataType = in.readInt();
134                     PersistableBundle requestDetails =
135                             in.readTypedObject(PersistableBundle.CREATOR);
136                     return new WearableSensingDataRequest(dataType, requestDetails);
137                 }
138             };
139 
140     /**
141      * Returns the maximum allowed size of a WearableSensingDataRequest when it is parcelled.
142      * Instances that exceed this size can be constructed, but will be rejected by the system when
143      * they leave the isolated WearableSensingService.
144      */
getMaxRequestSize()145     public static int getMaxRequestSize() {
146         return MAX_REQUEST_SIZE;
147     }
148 
149     /**
150      * Returns the rolling time window used to perform rate limiting on data requests leaving the
151      * WearableSensingService.
152      */
153     @NonNull
getRateLimitWindowSize()154     public static Duration getRateLimitWindowSize() {
155         return RATE_LIMIT_WINDOW_SIZE;
156     }
157 
158     /**
159      * Returns the number of data requests allowed to leave the WearableSensingService in each
160      * {@link #getRateLimitWindowSize()}.
161      */
getRateLimit()162     public static int getRateLimit() {
163         return RATE_LIMIT;
164     }
165 
166     /** A builder for WearableSensingDataRequest. */
167     @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
168     public static final class Builder {
169         private int mDataType;
170         private PersistableBundle mRequestDetails;
171 
Builder(int dataType)172         public Builder(int dataType) {
173             mDataType = dataType;
174         }
175 
176         /** Sets the request details. */
setRequestDetails(@onNull PersistableBundle requestDetails)177         public @NonNull Builder setRequestDetails(@NonNull PersistableBundle requestDetails) {
178             mRequestDetails = requestDetails;
179             return this;
180         }
181 
182         /** Builds the WearableSensingDataRequest. */
build()183         public @NonNull WearableSensingDataRequest build() {
184             if (mRequestDetails == null) {
185                 mRequestDetails = PersistableBundle.EMPTY;
186             }
187             return new WearableSensingDataRequest(mDataType, mRequestDetails);
188         }
189     }
190 }
191