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